This is simple Nextjs Application can not do SSG generation Error: Page “/store/[slug]/page” cannot use both “use client” and export function “generateStaticParams()”. I am using Nextjs 15 version I used useclient but could not take build what issues Could you please check my code.
store/[slug]/page.js
<code>"use client";
import { useState } from 'react';
import Image from 'next/image';
import Header from '../../header';
import Footer from '../../footer';
import React from 'react';
import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import {Accordion, AccordionItem} from "@nextui-org/accordion";
interface Store {
store_logo: string;
offer_payment: string;
offer_reward?: string;
}
type ArrowProps = {
onClick?: () => void;
};
// Custom Previous Arrow
const CustomPrevArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-prev-arrow" onClick={onClick}>
<IoIosArrowBack size={30} color="#000" />
</div>
);
};
// Custom Next Arrow
const CustomNextArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-next-arrow" onClick={onClick}>
<IoIosArrowForward size={30} color="#000" />
</div>
);
};
const settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 2,
slidesToScroll: 1,
variableWidth: true, // Allow slides to have variable width
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
},
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
};
interface UserData {
transaction_count: number;
storecash: number;
}
export default function Page({ params }:any) {
const [showOtp, setShowOtp] = React.useState(true);
const [userDataHide, setUserDataHide] = React.useState(true);
// const [otp, setOtp] = useState('');
const [otp, setOtp] = useState(["", "", "", ""]);
const [userData, setUserData] = useState<UserData | null>(null);
const [isVisible, setIsVisible] = useState(true); // State to manage visibility
const [mobileNumber, setMobileNumber] = useState("+971");
const handleToggle = () => {
setIsVisible(!isVisible); // Toggle the visibility
};
const defaultContent =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const [isVisibles, setIsVisibles] = useState(true); // State to manage visibility
const handleToggles = () => {
setIsVisibles(!isVisibles); // Toggle the visibility
};
// Define the type for the arrow props
const [slugPath, setSlugPath] = useState<string | null>(null);
// useEffect(() => {
// Dynamically get the slug (e.g., from the URL)
const currentSlug = window.location.pathname.split("/").pop();
// Set the slugPath to either the slug or null if undefined
setSlugPath(currentSlug ?? null);
// }, []);
// Resolving the `params` Promise using React.use() hook
const { slug } = params;
const [stores, setStores] = useState<Store[]>([]);
const [result, setResult] = useState<any>(null);
const [activeIndex, setActiveIndex] = useState<number | null>(null);
const handleClick = (offerPayment:any, index: number) => {
setActiveIndex(index);
// console.log("Clicked value:", offerPayment);
// console.log("Clicked index:", index);
localStorage.setItem('offer_payment', offerPayment)
};
// Fetch store data
const fetchStoreData = async () => {
if (!slug) return; // Don't fetch if slug is unavailable
const responseData = { store_key: slug };
console.log(responseData)
try {
const response = await fetch(
'https://test.online/api/v9/webpay/evoucher-single-store',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const result = await response.json();
console.log(result)
const storesData: Store[] = Array.isArray(result.credit_list)
? result.credit_list
: [];
setStores(storesData);
setResult(result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
// Fetch data when the component mounts
// useEffect(() => {
// }, [slug]);
fetchStoreData();
if (!result) return <div>Loading...</div>;
// otp section
const handleInputChange = (event:any) => {
const inputValue = event.target.value;
// Ensure the input starts with +971
if (!inputValue.startsWith("+971")) {
setMobileNumber("+971");
} else {
setMobileNumber(inputValue);
}
};
const handleSubmit = async () => {
setShowOtp(false);
let trimmedNumber = mobileNumber.replace(/^+971/, '');
const responseData = { mobile: trimmedNumber };
console.log(JSON.stringify(responseData))
try {
const response = await fetch(
'https://test.online/api/v9/webpay/mobile-check',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log(output)
} catch (error) {
console.error("Error fetching data:", error);
}
};
// verify otp
const handleChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => {
const value = e.target.value;
// Allow only digits
if (!/^d*$/.test(value)) return;
// Update OTP array with new value at the specified index
const newOtp = [...otp];
newOtp[index] = value;
setOtp(newOtp);
// Automatically focus the next input if there's a value and the index is within bounds
if (value && index < otp.length - 1) {
const nextInput = document.getElementById(`otp-input-${index + 1}`);
if (nextInput) {
nextInput.focus();
}
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if (e.key === "Backspace" && !otp[index] && index > 0) {
// Move to the previous input if Backspace is pressed and the current input is empty
const prevInput = document.getElementById(`otp-input-${index - 1}`);
if (prevInput) {
prevInput.focus();
}
}
};
// const handleSubmitOtp = (e) => {
// e.preventDefault();
// alert(`OTP entered: ${otp.join("")}`); // Combine OTP and display it
// };
const store_ids=localStorage.getItem("store_id")
const handleSubmitOtp = async (e: any) => {
e.preventDefault();
// Trim the mobile number
let trimmedNumber = mobileNumber.replace(/^+971/, '');
// Prepare the payload for the first API call
const otpValue = otp.join("");
const responseData = { mobile: trimmedNumber, otp:parseInt(otpValue, 10), };
console.log("Sending:", JSON.stringify(responseData));
// Validate inputs
if (!mobileNumber || !otp) {
console.error("Mobile number or OTP is missing");
return;
}
try {
// First API call to verify OTP
const response = await fetch(
'https://test.online/api/v9/webpay/verify-otp',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log("OTP verification response:", output);
// Check if OTP verification was successful
if (output.status === "success") {
setUserDataHide(false)
// Prepare payload for the second API call
const outputResponse = {
user_id: output.token,
store_id: store_ids, // Ensure this variable is defined
};
console.log("OTP", outputResponse);
try {
// Second API call to fetch user data
const userResponse = await fetch(
'https://test.online/api/v9/webpay/user-data',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(outputResponse),
}
);
if (!userResponse.ok) {
console.error(`HTTP error! Status: ${userResponse.status}`);
return;
}
const userData = await userResponse.json();
console.log("User data response:", userData);
setUserData(userData)
} catch (error) {
console.error("Error fetching user data:", error);
}
} else {
console.error("OTP verification failed:", output);
}
} catch (error) {
console.error("Error verifying OTP:", error);
}
};
return (
<>
<Header />
<div className="container-fluid store-details">
<div className="row">
<div className="col-md-5">
<Image
// src={result.store_data.store_logo}
src="https://www.znap.cash/totls/images/feature-4.png"
width={200}
height={150}
className="stores-img"
alt=""
/>
<Accordion variant="bordered">
<AccordionItem key="1" aria-label="Terms & Conditions" title="Terms & Conditions">
{defaultContent}
</AccordionItem>
<AccordionItem key="2" aria-label="How to Use" title="How to Use">
{defaultContent}
</AccordionItem>
</Accordion>
</div>
<div className="col-md-7 groups">
{isVisible ? (
<>
<div className='slide-groups'>
<h3>Validity 12 months</h3>
<h1>{result.store_data.store_name}</h1>
{/* <h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1> */}
<div className="slides">
<p>
<button className="pay">Pay</button>
<br />
<button className="get"><b>Get</b></button>
</p>
</div>
<Slider {...settings}>
{stores.map((store, index) => (
<div className="slides" key={index}>
<div
className={activeIndex === index ? "actives" : "non-actives"}
onClick={() => handleClick(store.offer_payment, index)}
>
<p>{store.offer_payment}</p>
<hr />
<p><b>{store.offer_reward}</b></p>
</div>
</div>
))}
</Slider>
</div>
<p className="cl">
<button className="buy-btn" onClick={handleToggle}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</p>
</>
) : isVisible ? (
<div className="get-part">
{/* <p className="b-n">
<span>YOU GET</span> <span>YOU PAY</span>
</p>
<p
onClick={() => handleClick(0)}
className={activeIndex === 0 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(1)}
className={activeIndex === 1 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(2)}
className={activeIndex === 2 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p> */}
<div>
<button className="buy-btn" onClick={handleToggles}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</div>
</div>
) : (
<>
{showOtp ? (
<div className='otp' id='otp'>
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<label>Enter Your Mobile Number</label><br />
<div>
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
<input
type="text"
value={mobileNumber}
onChange={handleInputChange}
style={{
padding: "5px",
borderRadius: "4px",
width: "400px",
}}
/>
<button onClick={handleSubmit}>
Submit
</button>
</div>
</div>
<div className='otp-disc'>
<p>Credits purchased will be linked to this Mobile</p>
<h5>Note:</h5>
<p>Mobile number needed to send transaction related information and to review on any disputes later</p>
</div>
</div>
) : (
<>
{userDataHide ? (
<div className="verify-otp">
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<p>Enter OTP sent to +971258025801</p>
<form onSubmit={handleSubmitOtp}>
{otp.map((digit, index) => (
<input
key={index}
id={`otp-input-${index}`}
type="text" // Change to text input
value={digit === '' ? '' : digit} // Ensure value is a string
onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
maxLength={1} // Correctly limit to 1 digit
className="otp-input"
/>
))}
<br />
<button type="submit" style={{ marginTop: "1rem" }}>
Submit OTP
</button>
</form>
</div>
):(
<div className='user-data'>
{userData && (
<div className='userData'>
<h3>Payment Summary</h3>
<div>
<p><b><span>eVoucher</span><span>AED {localStorage.getItem("offer_payment")}</span></b></p>
<p><span>Pay</span><span>AED {userData.transaction_count }</span></p>
<p><span>Store Cash Used</span><span>AED {userData.storecash }</span></p>
<p><span>Pay with Card</span><span>AED {userData.transaction_count }</span></p>
</div>
<div className='receipt'>
<h3>Payment Receipt Details</h3>
<form>
<p>Provide details about the person you are sending this gift to</p>
<input type='text' className='name' placeholder='Name'/>
<input type='text' className='email' placeholder='Email'/>
<input type='number' className='mobile' placeholder='Mobile'/>
<textarea cols={10} rows={10} className='message' placeholder='Message to Receipient'></textarea>
<button>Save Receipient</button>
</form>
</div>
</div>
)}
</div>
)}
</>
)}
</>
)}
</div>
</div>
</div>
<Footer />
</>
);
}
// Generate static paths at build time
export async function generateStaticParams() {
const responseData = {
category_id: "0",
};
const response = await fetch('https://test.online/api/v9/webpay/evoucher-list-web', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(responseData),
});
// Check if the response is OK
if (!response.ok) {
throw new Error(`Failed to fetch categories: ${response.status}`);
}
const data = await response.json();
// Assuming the data.stores is an array with store objects
const categories = data.stores || [];
// Return static params for each store (using store_id as slug)
return categories.map((category: { store_id: number }) => ({
slug: category.store_id.toString(), // Use store_id as the slug
}));
}
</code>
<code>"use client";
import { useState } from 'react';
import Image from 'next/image';
import Header from '../../header';
import Footer from '../../footer';
import React from 'react';
import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import {Accordion, AccordionItem} from "@nextui-org/accordion";
interface Store {
store_logo: string;
offer_payment: string;
offer_reward?: string;
}
type ArrowProps = {
onClick?: () => void;
};
// Custom Previous Arrow
const CustomPrevArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-prev-arrow" onClick={onClick}>
<IoIosArrowBack size={30} color="#000" />
</div>
);
};
// Custom Next Arrow
const CustomNextArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-next-arrow" onClick={onClick}>
<IoIosArrowForward size={30} color="#000" />
</div>
);
};
const settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 2,
slidesToScroll: 1,
variableWidth: true, // Allow slides to have variable width
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
},
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
};
interface UserData {
transaction_count: number;
storecash: number;
}
export default function Page({ params }:any) {
const [showOtp, setShowOtp] = React.useState(true);
const [userDataHide, setUserDataHide] = React.useState(true);
// const [otp, setOtp] = useState('');
const [otp, setOtp] = useState(["", "", "", ""]);
const [userData, setUserData] = useState<UserData | null>(null);
const [isVisible, setIsVisible] = useState(true); // State to manage visibility
const [mobileNumber, setMobileNumber] = useState("+971");
const handleToggle = () => {
setIsVisible(!isVisible); // Toggle the visibility
};
const defaultContent =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const [isVisibles, setIsVisibles] = useState(true); // State to manage visibility
const handleToggles = () => {
setIsVisibles(!isVisibles); // Toggle the visibility
};
// Define the type for the arrow props
const [slugPath, setSlugPath] = useState<string | null>(null);
// useEffect(() => {
// Dynamically get the slug (e.g., from the URL)
const currentSlug = window.location.pathname.split("/").pop();
// Set the slugPath to either the slug or null if undefined
setSlugPath(currentSlug ?? null);
// }, []);
// Resolving the `params` Promise using React.use() hook
const { slug } = params;
const [stores, setStores] = useState<Store[]>([]);
const [result, setResult] = useState<any>(null);
const [activeIndex, setActiveIndex] = useState<number | null>(null);
const handleClick = (offerPayment:any, index: number) => {
setActiveIndex(index);
// console.log("Clicked value:", offerPayment);
// console.log("Clicked index:", index);
localStorage.setItem('offer_payment', offerPayment)
};
// Fetch store data
const fetchStoreData = async () => {
if (!slug) return; // Don't fetch if slug is unavailable
const responseData = { store_key: slug };
console.log(responseData)
try {
const response = await fetch(
'https://test.online/api/v9/webpay/evoucher-single-store',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const result = await response.json();
console.log(result)
const storesData: Store[] = Array.isArray(result.credit_list)
? result.credit_list
: [];
setStores(storesData);
setResult(result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
// Fetch data when the component mounts
// useEffect(() => {
// }, [slug]);
fetchStoreData();
if (!result) return <div>Loading...</div>;
// otp section
const handleInputChange = (event:any) => {
const inputValue = event.target.value;
// Ensure the input starts with +971
if (!inputValue.startsWith("+971")) {
setMobileNumber("+971");
} else {
setMobileNumber(inputValue);
}
};
const handleSubmit = async () => {
setShowOtp(false);
let trimmedNumber = mobileNumber.replace(/^+971/, '');
const responseData = { mobile: trimmedNumber };
console.log(JSON.stringify(responseData))
try {
const response = await fetch(
'https://test.online/api/v9/webpay/mobile-check',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log(output)
} catch (error) {
console.error("Error fetching data:", error);
}
};
// verify otp
const handleChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => {
const value = e.target.value;
// Allow only digits
if (!/^d*$/.test(value)) return;
// Update OTP array with new value at the specified index
const newOtp = [...otp];
newOtp[index] = value;
setOtp(newOtp);
// Automatically focus the next input if there's a value and the index is within bounds
if (value && index < otp.length - 1) {
const nextInput = document.getElementById(`otp-input-${index + 1}`);
if (nextInput) {
nextInput.focus();
}
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if (e.key === "Backspace" && !otp[index] && index > 0) {
// Move to the previous input if Backspace is pressed and the current input is empty
const prevInput = document.getElementById(`otp-input-${index - 1}`);
if (prevInput) {
prevInput.focus();
}
}
};
// const handleSubmitOtp = (e) => {
// e.preventDefault();
// alert(`OTP entered: ${otp.join("")}`); // Combine OTP and display it
// };
const store_ids=localStorage.getItem("store_id")
const handleSubmitOtp = async (e: any) => {
e.preventDefault();
// Trim the mobile number
let trimmedNumber = mobileNumber.replace(/^+971/, '');
// Prepare the payload for the first API call
const otpValue = otp.join("");
const responseData = { mobile: trimmedNumber, otp:parseInt(otpValue, 10), };
console.log("Sending:", JSON.stringify(responseData));
// Validate inputs
if (!mobileNumber || !otp) {
console.error("Mobile number or OTP is missing");
return;
}
try {
// First API call to verify OTP
const response = await fetch(
'https://test.online/api/v9/webpay/verify-otp',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log("OTP verification response:", output);
// Check if OTP verification was successful
if (output.status === "success") {
setUserDataHide(false)
// Prepare payload for the second API call
const outputResponse = {
user_id: output.token,
store_id: store_ids, // Ensure this variable is defined
};
console.log("OTP", outputResponse);
try {
// Second API call to fetch user data
const userResponse = await fetch(
'https://test.online/api/v9/webpay/user-data',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(outputResponse),
}
);
if (!userResponse.ok) {
console.error(`HTTP error! Status: ${userResponse.status}`);
return;
}
const userData = await userResponse.json();
console.log("User data response:", userData);
setUserData(userData)
} catch (error) {
console.error("Error fetching user data:", error);
}
} else {
console.error("OTP verification failed:", output);
}
} catch (error) {
console.error("Error verifying OTP:", error);
}
};
return (
<>
<Header />
<div className="container-fluid store-details">
<div className="row">
<div className="col-md-5">
<Image
// src={result.store_data.store_logo}
src="https://www.znap.cash/totls/images/feature-4.png"
width={200}
height={150}
className="stores-img"
alt=""
/>
<Accordion variant="bordered">
<AccordionItem key="1" aria-label="Terms & Conditions" title="Terms & Conditions">
{defaultContent}
</AccordionItem>
<AccordionItem key="2" aria-label="How to Use" title="How to Use">
{defaultContent}
</AccordionItem>
</Accordion>
</div>
<div className="col-md-7 groups">
{isVisible ? (
<>
<div className='slide-groups'>
<h3>Validity 12 months</h3>
<h1>{result.store_data.store_name}</h1>
{/* <h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1> */}
<div className="slides">
<p>
<button className="pay">Pay</button>
<br />
<button className="get"><b>Get</b></button>
</p>
</div>
<Slider {...settings}>
{stores.map((store, index) => (
<div className="slides" key={index}>
<div
className={activeIndex === index ? "actives" : "non-actives"}
onClick={() => handleClick(store.offer_payment, index)}
>
<p>{store.offer_payment}</p>
<hr />
<p><b>{store.offer_reward}</b></p>
</div>
</div>
))}
</Slider>
</div>
<p className="cl">
<button className="buy-btn" onClick={handleToggle}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</p>
</>
) : isVisible ? (
<div className="get-part">
{/* <p className="b-n">
<span>YOU GET</span> <span>YOU PAY</span>
</p>
<p
onClick={() => handleClick(0)}
className={activeIndex === 0 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(1)}
className={activeIndex === 1 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(2)}
className={activeIndex === 2 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p> */}
<div>
<button className="buy-btn" onClick={handleToggles}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</div>
</div>
) : (
<>
{showOtp ? (
<div className='otp' id='otp'>
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<label>Enter Your Mobile Number</label><br />
<div>
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
<input
type="text"
value={mobileNumber}
onChange={handleInputChange}
style={{
padding: "5px",
borderRadius: "4px",
width: "400px",
}}
/>
<button onClick={handleSubmit}>
Submit
</button>
</div>
</div>
<div className='otp-disc'>
<p>Credits purchased will be linked to this Mobile</p>
<h5>Note:</h5>
<p>Mobile number needed to send transaction related information and to review on any disputes later</p>
</div>
</div>
) : (
<>
{userDataHide ? (
<div className="verify-otp">
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<p>Enter OTP sent to +971258025801</p>
<form onSubmit={handleSubmitOtp}>
{otp.map((digit, index) => (
<input
key={index}
id={`otp-input-${index}`}
type="text" // Change to text input
value={digit === '' ? '' : digit} // Ensure value is a string
onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
maxLength={1} // Correctly limit to 1 digit
className="otp-input"
/>
))}
<br />
<button type="submit" style={{ marginTop: "1rem" }}>
Submit OTP
</button>
</form>
</div>
):(
<div className='user-data'>
{userData && (
<div className='userData'>
<h3>Payment Summary</h3>
<div>
<p><b><span>eVoucher</span><span>AED {localStorage.getItem("offer_payment")}</span></b></p>
<p><span>Pay</span><span>AED {userData.transaction_count }</span></p>
<p><span>Store Cash Used</span><span>AED {userData.storecash }</span></p>
<p><span>Pay with Card</span><span>AED {userData.transaction_count }</span></p>
</div>
<div className='receipt'>
<h3>Payment Receipt Details</h3>
<form>
<p>Provide details about the person you are sending this gift to</p>
<input type='text' className='name' placeholder='Name'/>
<input type='text' className='email' placeholder='Email'/>
<input type='number' className='mobile' placeholder='Mobile'/>
<textarea cols={10} rows={10} className='message' placeholder='Message to Receipient'></textarea>
<button>Save Receipient</button>
</form>
</div>
</div>
)}
</div>
)}
</>
)}
</>
)}
</div>
</div>
</div>
<Footer />
</>
);
}
// Generate static paths at build time
export async function generateStaticParams() {
const responseData = {
category_id: "0",
};
const response = await fetch('https://test.online/api/v9/webpay/evoucher-list-web', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(responseData),
});
// Check if the response is OK
if (!response.ok) {
throw new Error(`Failed to fetch categories: ${response.status}`);
}
const data = await response.json();
// Assuming the data.stores is an array with store objects
const categories = data.stores || [];
// Return static params for each store (using store_id as slug)
return categories.map((category: { store_id: number }) => ({
slug: category.store_id.toString(), // Use store_id as the slug
}));
}
</code>
"use client";
import { useState } from 'react';
import Image from 'next/image';
import Header from '../../header';
import Footer from '../../footer';
import React from 'react';
import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import {Accordion, AccordionItem} from "@nextui-org/accordion";
interface Store {
store_logo: string;
offer_payment: string;
offer_reward?: string;
}
type ArrowProps = {
onClick?: () => void;
};
// Custom Previous Arrow
const CustomPrevArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-prev-arrow" onClick={onClick}>
<IoIosArrowBack size={30} color="#000" />
</div>
);
};
// Custom Next Arrow
const CustomNextArrow: React.FC<ArrowProps> = ({ onClick }) => {
return (
<div className="custom-arrow custom-next-arrow" onClick={onClick}>
<IoIosArrowForward size={30} color="#000" />
</div>
);
};
const settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 2,
slidesToScroll: 1,
variableWidth: true, // Allow slides to have variable width
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
},
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
};
interface UserData {
transaction_count: number;
storecash: number;
}
export default function Page({ params }:any) {
const [showOtp, setShowOtp] = React.useState(true);
const [userDataHide, setUserDataHide] = React.useState(true);
// const [otp, setOtp] = useState('');
const [otp, setOtp] = useState(["", "", "", ""]);
const [userData, setUserData] = useState<UserData | null>(null);
const [isVisible, setIsVisible] = useState(true); // State to manage visibility
const [mobileNumber, setMobileNumber] = useState("+971");
const handleToggle = () => {
setIsVisible(!isVisible); // Toggle the visibility
};
const defaultContent =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
const [isVisibles, setIsVisibles] = useState(true); // State to manage visibility
const handleToggles = () => {
setIsVisibles(!isVisibles); // Toggle the visibility
};
// Define the type for the arrow props
const [slugPath, setSlugPath] = useState<string | null>(null);
// useEffect(() => {
// Dynamically get the slug (e.g., from the URL)
const currentSlug = window.location.pathname.split("/").pop();
// Set the slugPath to either the slug or null if undefined
setSlugPath(currentSlug ?? null);
// }, []);
// Resolving the `params` Promise using React.use() hook
const { slug } = params;
const [stores, setStores] = useState<Store[]>([]);
const [result, setResult] = useState<any>(null);
const [activeIndex, setActiveIndex] = useState<number | null>(null);
const handleClick = (offerPayment:any, index: number) => {
setActiveIndex(index);
// console.log("Clicked value:", offerPayment);
// console.log("Clicked index:", index);
localStorage.setItem('offer_payment', offerPayment)
};
// Fetch store data
const fetchStoreData = async () => {
if (!slug) return; // Don't fetch if slug is unavailable
const responseData = { store_key: slug };
console.log(responseData)
try {
const response = await fetch(
'https://test.online/api/v9/webpay/evoucher-single-store',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const result = await response.json();
console.log(result)
const storesData: Store[] = Array.isArray(result.credit_list)
? result.credit_list
: [];
setStores(storesData);
setResult(result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
// Fetch data when the component mounts
// useEffect(() => {
// }, [slug]);
fetchStoreData();
if (!result) return <div>Loading...</div>;
// otp section
const handleInputChange = (event:any) => {
const inputValue = event.target.value;
// Ensure the input starts with +971
if (!inputValue.startsWith("+971")) {
setMobileNumber("+971");
} else {
setMobileNumber(inputValue);
}
};
const handleSubmit = async () => {
setShowOtp(false);
let trimmedNumber = mobileNumber.replace(/^+971/, '');
const responseData = { mobile: trimmedNumber };
console.log(JSON.stringify(responseData))
try {
const response = await fetch(
'https://test.online/api/v9/webpay/mobile-check',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log(output)
} catch (error) {
console.error("Error fetching data:", error);
}
};
// verify otp
const handleChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => {
const value = e.target.value;
// Allow only digits
if (!/^d*$/.test(value)) return;
// Update OTP array with new value at the specified index
const newOtp = [...otp];
newOtp[index] = value;
setOtp(newOtp);
// Automatically focus the next input if there's a value and the index is within bounds
if (value && index < otp.length - 1) {
const nextInput = document.getElementById(`otp-input-${index + 1}`);
if (nextInput) {
nextInput.focus();
}
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if (e.key === "Backspace" && !otp[index] && index > 0) {
// Move to the previous input if Backspace is pressed and the current input is empty
const prevInput = document.getElementById(`otp-input-${index - 1}`);
if (prevInput) {
prevInput.focus();
}
}
};
// const handleSubmitOtp = (e) => {
// e.preventDefault();
// alert(`OTP entered: ${otp.join("")}`); // Combine OTP and display it
// };
const store_ids=localStorage.getItem("store_id")
const handleSubmitOtp = async (e: any) => {
e.preventDefault();
// Trim the mobile number
let trimmedNumber = mobileNumber.replace(/^+971/, '');
// Prepare the payload for the first API call
const otpValue = otp.join("");
const responseData = { mobile: trimmedNumber, otp:parseInt(otpValue, 10), };
console.log("Sending:", JSON.stringify(responseData));
// Validate inputs
if (!mobileNumber || !otp) {
console.error("Mobile number or OTP is missing");
return;
}
try {
// First API call to verify OTP
const response = await fetch(
'https://test.online/api/v9/webpay/verify-otp',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(responseData),
}
);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
return;
}
const output = await response.json();
console.log("OTP verification response:", output);
// Check if OTP verification was successful
if (output.status === "success") {
setUserDataHide(false)
// Prepare payload for the second API call
const outputResponse = {
user_id: output.token,
store_id: store_ids, // Ensure this variable is defined
};
console.log("OTP", outputResponse);
try {
// Second API call to fetch user data
const userResponse = await fetch(
'https://test.online/api/v9/webpay/user-data',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(outputResponse),
}
);
if (!userResponse.ok) {
console.error(`HTTP error! Status: ${userResponse.status}`);
return;
}
const userData = await userResponse.json();
console.log("User data response:", userData);
setUserData(userData)
} catch (error) {
console.error("Error fetching user data:", error);
}
} else {
console.error("OTP verification failed:", output);
}
} catch (error) {
console.error("Error verifying OTP:", error);
}
};
return (
<>
<Header />
<div className="container-fluid store-details">
<div className="row">
<div className="col-md-5">
<Image
// src={result.store_data.store_logo}
src="https://www.znap.cash/totls/images/feature-4.png"
width={200}
height={150}
className="stores-img"
alt=""
/>
<Accordion variant="bordered">
<AccordionItem key="1" aria-label="Terms & Conditions" title="Terms & Conditions">
{defaultContent}
</AccordionItem>
<AccordionItem key="2" aria-label="How to Use" title="How to Use">
{defaultContent}
</AccordionItem>
</Accordion>
</div>
<div className="col-md-7 groups">
{isVisible ? (
<>
<div className='slide-groups'>
<h3>Validity 12 months</h3>
<h1>{result.store_data.store_name}</h1>
{/* <h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1> */}
<div className="slides">
<p>
<button className="pay">Pay</button>
<br />
<button className="get"><b>Get</b></button>
</p>
</div>
<Slider {...settings}>
{stores.map((store, index) => (
<div className="slides" key={index}>
<div
className={activeIndex === index ? "actives" : "non-actives"}
onClick={() => handleClick(store.offer_payment, index)}
>
<p>{store.offer_payment}</p>
<hr />
<p><b>{store.offer_reward}</b></p>
</div>
</div>
))}
</Slider>
</div>
<p className="cl">
<button className="buy-btn" onClick={handleToggle}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</p>
</>
) : isVisible ? (
<div className="get-part">
{/* <p className="b-n">
<span>YOU GET</span> <span>YOU PAY</span>
</p>
<p
onClick={() => handleClick(0)}
className={activeIndex === 0 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(1)}
className={activeIndex === 1 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p>
<p
onClick={() => handleClick(2)}
className={activeIndex === 2 ? "active" : ""}
>
<span>AED 2000</span> <span>AED 2000</span>
</p> */}
<div>
<button className="buy-btn" onClick={handleToggles}>Buy for Self</button>
<button className="buy-btn1">Send as Gift</button>
</div>
</div>
) : (
<>
{showOtp ? (
<div className='otp' id='otp'>
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<label>Enter Your Mobile Number</label><br />
<div>
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
<input
type="text"
value={mobileNumber}
onChange={handleInputChange}
style={{
padding: "5px",
borderRadius: "4px",
width: "400px",
}}
/>
<button onClick={handleSubmit}>
Submit
</button>
</div>
</div>
<div className='otp-disc'>
<p>Credits purchased will be linked to this Mobile</p>
<h5>Note:</h5>
<p>Mobile number needed to send transaction related information and to review on any disputes later</p>
</div>
</div>
) : (
<>
{userDataHide ? (
<div className="verify-otp">
<h3>Validity 12 months</h3>
<h1><span>{result.store_data.store_name}</span> <span>AED {localStorage.getItem("offer_payment")}</span></h1>
<p>Enter OTP sent to +971258025801</p>
<form onSubmit={handleSubmitOtp}>
{otp.map((digit, index) => (
<input
key={index}
id={`otp-input-${index}`}
type="text" // Change to text input
value={digit === '' ? '' : digit} // Ensure value is a string
onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
maxLength={1} // Correctly limit to 1 digit
className="otp-input"
/>
))}
<br />
<button type="submit" style={{ marginTop: "1rem" }}>
Submit OTP
</button>
</form>
</div>
):(
<div className='user-data'>
{userData && (
<div className='userData'>
<h3>Payment Summary</h3>
<div>
<p><b><span>eVoucher</span><span>AED {localStorage.getItem("offer_payment")}</span></b></p>
<p><span>Pay</span><span>AED {userData.transaction_count }</span></p>
<p><span>Store Cash Used</span><span>AED {userData.storecash }</span></p>
<p><span>Pay with Card</span><span>AED {userData.transaction_count }</span></p>
</div>
<div className='receipt'>
<h3>Payment Receipt Details</h3>
<form>
<p>Provide details about the person you are sending this gift to</p>
<input type='text' className='name' placeholder='Name'/>
<input type='text' className='email' placeholder='Email'/>
<input type='number' className='mobile' placeholder='Mobile'/>
<textarea cols={10} rows={10} className='message' placeholder='Message to Receipient'></textarea>
<button>Save Receipient</button>
</form>
</div>
</div>
)}
</div>
)}
</>
)}
</>
)}
</div>
</div>
</div>
<Footer />
</>
);
}
// Generate static paths at build time
export async function generateStaticParams() {
const responseData = {
category_id: "0",
};
const response = await fetch('https://test.online/api/v9/webpay/evoucher-list-web', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(responseData),
});
// Check if the response is OK
if (!response.ok) {
throw new Error(`Failed to fetch categories: ${response.status}`);
}
const data = await response.json();
// Assuming the data.stores is an array with store objects
const categories = data.stores || [];
// Return static params for each store (using store_id as slug)
return categories.map((category: { store_id: number }) => ({
slug: category.store_id.toString(), // Use store_id as the slug
}));
}
1