|
@@ -1,18 +1,40 @@
|
|
|
-import React, { useState } from "react";
|
|
|
|
|
|
|
+import React, { useState, useMemo, useEffect } from "react";
|
|
|
import { useLocation, useNavigate, Link } from "react-router-dom";
|
|
import { useLocation, useNavigate, Link } from "react-router-dom";
|
|
|
import { SelectedProduct } from "../../services/types";
|
|
import { SelectedProduct } from "../../services/types";
|
|
|
|
|
+import { Area, Package } from "../../services/product/type";
|
|
|
|
|
+import { useQuery } from "@tanstack/react-query";
|
|
|
|
|
+import { DataCacheKey, staleTime } from "../../global/constants";
|
|
|
|
|
+import { useAppDispatch, useAppSelector } from "../../hooks/useRedux";
|
|
|
|
|
+import { startLoading, stopLoading } from "../../features/loading/loadingSlice";
|
|
|
|
|
+import { productApi } from "../../apis/productApi";
|
|
|
|
|
+import { openPopup } from "../../features/popup/popupSlice";
|
|
|
|
|
+import { get } from "http";
|
|
|
|
|
|
|
|
const ProductDetailView: React.FC = () => {
|
|
const ProductDetailView: React.FC = () => {
|
|
|
const location = useLocation();
|
|
const location = useLocation();
|
|
|
const navigate = useNavigate();
|
|
const navigate = useNavigate();
|
|
|
- const product = location.state as SelectedProduct;
|
|
|
|
|
-
|
|
|
|
|
- const [selectedDays, setSelectedDays] = useState<number>(7);
|
|
|
|
|
|
|
+ const dispatch = useAppDispatch();
|
|
|
|
|
+ const area = location.state as Area;
|
|
|
|
|
+ const loading = useAppSelector((state) => state.loading);
|
|
|
|
|
+ const [selectedDays, setSelectedDays] = useState<number>(null);
|
|
|
const [selectedData, setSelectedData] = useState<string>("Unlimited");
|
|
const [selectedData, setSelectedData] = useState<string>("Unlimited");
|
|
|
|
|
+ const [daysOptions, setDaysOptions] = useState<number[]>([]);
|
|
|
|
|
+ const [dataOptions, setDataOptions] = useState<string[]>([]);
|
|
|
|
|
+ const [daysActiveOptions, setDaysActiveOptions] = useState<number[]>([]);
|
|
|
|
|
+ const [dataActiveOptions, setDataActiveOptions] = useState<string[]>([]);
|
|
|
|
|
+ const [prices, setPrices] = useState<{
|
|
|
|
|
+ original: string;
|
|
|
|
|
+ final: string;
|
|
|
|
|
+ discountPercent: string;
|
|
|
|
|
+ }>({
|
|
|
|
|
+ original: "0.00",
|
|
|
|
|
+ final: "0.00",
|
|
|
|
|
+ discountPercent: "0",
|
|
|
|
|
+ });
|
|
|
const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
|
|
const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
|
|
|
const [quantity, setQuantity] = useState<number>(1);
|
|
const [quantity, setQuantity] = useState<number>(1);
|
|
|
|
|
|
|
|
- if (!product) {
|
|
|
|
|
|
|
+ if (!area) {
|
|
|
return (
|
|
return (
|
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
|
<div className="text-center">
|
|
<div className="text-center">
|
|
@@ -25,43 +47,173 @@ const ProductDetailView: React.FC = () => {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const daysOptions = [3, 5, 7, 8, 10, 15, 30];
|
|
|
|
|
- const dataOptions = [
|
|
|
|
|
- "1GB",
|
|
|
|
|
- "2GB",
|
|
|
|
|
- "3GB",
|
|
|
|
|
- "5GB",
|
|
|
|
|
- "10GB",
|
|
|
|
|
- "15GB",
|
|
|
|
|
- "20GB",
|
|
|
|
|
- "50GB",
|
|
|
|
|
- "Unlimited",
|
|
|
|
|
- ];
|
|
|
|
|
-
|
|
|
|
|
- const getPrices = () => {
|
|
|
|
|
- const dataMultiplier: Record<string, number> = {
|
|
|
|
|
- "1GB": 1,
|
|
|
|
|
- "2GB": 1.5,
|
|
|
|
|
- "3GB": 2,
|
|
|
|
|
- "5GB": 3,
|
|
|
|
|
- "10GB": 5,
|
|
|
|
|
- "15GB": 7,
|
|
|
|
|
- "20GB": 9,
|
|
|
|
|
- "50GB": 15,
|
|
|
|
|
- Unlimited: 20,
|
|
|
|
|
- };
|
|
|
|
|
- const base = selectedDays * 1.5 + (dataMultiplier[selectedData] || 5);
|
|
|
|
|
- const discount = 0.1;
|
|
|
|
|
- const original = base * quantity;
|
|
|
|
|
- const final = original * (1 - discount);
|
|
|
|
|
|
|
+ const { data: loadPackage = [] } = useQuery<Package[]>({
|
|
|
|
|
+ queryKey: [DataCacheKey.PACKAGES],
|
|
|
|
|
+ queryFn: async (): Promise<Package[]> => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ dispatch(startLoading({}));
|
|
|
|
|
+ const res = await productApi.loadPackage({
|
|
|
|
|
+ areaId: area.id,
|
|
|
|
|
+ isUnlimited: "-1",
|
|
|
|
|
+ isDaily: "-1",
|
|
|
|
|
+ });
|
|
|
|
|
+ return res.data as Package[];
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ return []; // 🔴 bắt buộc
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ dispatch(stopLoading());
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ staleTime: staleTime,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const options = useMemo(() => {
|
|
|
|
|
+ console.log("Calculating options from loadPackage");
|
|
|
|
|
+
|
|
|
|
|
+ const daysSet = new Set<number>();
|
|
|
|
|
+ const dataSet = new Set<string>();
|
|
|
|
|
+
|
|
|
|
|
+ loadPackage.forEach((p) => {
|
|
|
|
|
+ daysSet.add(p.dayDuration);
|
|
|
|
|
+ dataSet.add(p.amountData.toString());
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const daysArray = Array.from(daysSet).sort((a, b) => a - b);
|
|
|
|
|
+
|
|
|
|
|
+ const dataArray = Array.from(dataSet).sort((a, b) => {
|
|
|
|
|
+ if (a === "Unlimited") return 1;
|
|
|
|
|
+ if (b === "Unlimited") return -1;
|
|
|
|
|
+ return parseInt(a) - parseInt(b);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
- original: original.toFixed(2),
|
|
|
|
|
- final: final.toFixed(2),
|
|
|
|
|
- discountPercent: (discount * 100).toFixed(0),
|
|
|
|
|
|
|
+ daysArray,
|
|
|
|
|
+ dataArray,
|
|
|
};
|
|
};
|
|
|
|
|
+ }, [loadPackage]);
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ setDaysOptions(options.daysArray);
|
|
|
|
|
+ setDataOptions(options.dataArray);
|
|
|
|
|
+ handleSelectDay(options.daysArray[0]);
|
|
|
|
|
+ handleSelectData(options.dataArray[0]);
|
|
|
|
|
+ }, [options]);
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ getPrices();
|
|
|
|
|
+ }, [selectedDays, selectedData]);
|
|
|
|
|
+
|
|
|
|
|
+ const handleSelectDay = (day: number) => {
|
|
|
|
|
+ // filter data options based on selected day if needed
|
|
|
|
|
+ const dataSet = new Set<string>();
|
|
|
|
|
+ loadPackage.forEach((p) => {
|
|
|
|
|
+ if (p.dayDuration === day) {
|
|
|
|
|
+ dataSet.add(p.amountData.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setSelectedDays(day);
|
|
|
|
|
+ setDataActiveOptions(Array.from(dataSet));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleSelectData = (data: string) => {
|
|
|
|
|
+ // filter day options based on selected data if needed
|
|
|
|
|
+ const daysSet = new Set<number>();
|
|
|
|
|
+ loadPackage.forEach((p) => {
|
|
|
|
|
+ if (p.amountData.toString() === data) {
|
|
|
|
|
+ daysSet.add(p.dayDuration);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setSelectedData(data);
|
|
|
|
|
+ setDaysActiveOptions(Array.from(daysSet));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const getPrices = (quantityParam?: number) => {
|
|
|
|
|
+ const quantityToUse =
|
|
|
|
|
+ quantityParam !== undefined ? quantityParam : quantity;
|
|
|
|
|
+ // find package based on selectedDays and selectedData
|
|
|
|
|
+ const selectedPackage = loadPackage.find(
|
|
|
|
|
+ (p) =>
|
|
|
|
|
+ p.dayDuration === selectedDays &&
|
|
|
|
|
+ p.amountData.toString() === selectedData
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!selectedPackage) {
|
|
|
|
|
+ console.log(
|
|
|
|
|
+ "No package found for the selected options " +
|
|
|
|
|
+ selectedDays +
|
|
|
|
|
+ " days and " +
|
|
|
|
|
+ selectedData +
|
|
|
|
|
+ " data"
|
|
|
|
|
+ );
|
|
|
|
|
+ return {
|
|
|
|
|
+ original: "0.00",
|
|
|
|
|
+ final: "0.00",
|
|
|
|
|
+ discountPercent: "0",
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(
|
|
|
|
|
+ "Selected package: ",
|
|
|
|
|
+ selectedPackage +
|
|
|
|
|
+ " for prices calculation " +
|
|
|
|
|
+ selectedPackage.sellPrice +
|
|
|
|
|
+ " quantity " +
|
|
|
|
|
+ quantityToUse
|
|
|
|
|
+ );
|
|
|
|
|
+ setPrices({
|
|
|
|
|
+ original: quantityToUse * selectedPackage.displayPrice,
|
|
|
|
|
+ final: quantityToUse * selectedPackage.sellPrice,
|
|
|
|
|
+ discountPercent: "0",
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const prices = getPrices();
|
|
|
|
|
|
|
+ const handleQuantityChange = (change: number) => {
|
|
|
|
|
+ const newQuantity = Math.max(1, quantity + change);
|
|
|
|
|
+ setQuantity(newQuantity);
|
|
|
|
|
+ console.log("Selected quantity: ", newQuantity);
|
|
|
|
|
+ getPrices(newQuantity);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleBuyNow = async () => {
|
|
|
|
|
+ // Logic for custom order or standard buy
|
|
|
|
|
+ console.log("Buy now clicked");
|
|
|
|
|
+ const selectedPackage = loadPackage.find(
|
|
|
|
|
+ (p) =>
|
|
|
|
|
+ p.dayDuration === selectedDays &&
|
|
|
|
|
+ p.amountData.toString() === selectedData
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!selectedPackage) {
|
|
|
|
|
+ alert("Please select a valid package");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // call logic to proceed to checkout
|
|
|
|
|
+ const res = await productApi.checkout({
|
|
|
|
|
+ packgId: selectedPackage.id,
|
|
|
|
|
+ quantity: quantity,
|
|
|
|
|
+ });
|
|
|
|
|
+ if (res && res.errorCode === "0") {
|
|
|
|
|
+ console.log("Checkout details loaded:", res.data);
|
|
|
|
|
+ // navigate to checkout with selected options
|
|
|
|
|
+ navigate("/checkout", {
|
|
|
|
|
+ state: {
|
|
|
|
|
+ area: area,
|
|
|
|
|
+ package: selectedPackage,
|
|
|
|
|
+ quantity: quantity,
|
|
|
|
|
+ simType: simType,
|
|
|
|
|
+ checkoutDetails: res.data,
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error("Failed to load checkout details:", res.message);
|
|
|
|
|
+ dispatch(
|
|
|
|
|
+ openPopup({
|
|
|
|
|
+ isSuccess: false,
|
|
|
|
|
+ title: "Checkout Error",
|
|
|
|
|
+ message: res.message || "Failed to proceed to checkout.",
|
|
|
|
|
+ buttonText: "Close",
|
|
|
|
|
+ })
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div className="bg-white min-h-screen pb-20">
|
|
<div className="bg-white min-h-screen pb-20">
|
|
@@ -83,7 +235,7 @@ const ProductDetailView: React.FC = () => {
|
|
|
strokeLinejoin="round"
|
|
strokeLinejoin="round"
|
|
|
/>
|
|
/>
|
|
|
</svg>
|
|
</svg>
|
|
|
- <span className="text-slate-900 font-bold">{product.country}</span>
|
|
|
|
|
|
|
+ <span className="text-slate-900 font-bold">{area.areaName1}</span>
|
|
|
</nav>
|
|
</nav>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -91,21 +243,21 @@ const ProductDetailView: React.FC = () => {
|
|
|
<div className="lg:col-span-5 space-y-6">
|
|
<div className="lg:col-span-5 space-y-6">
|
|
|
<div className="relative aspect-[3/4] rounded-[24px] md:rounded-[32px] overflow-hidden shadow-2xl group">
|
|
<div className="relative aspect-[3/4] rounded-[24px] md:rounded-[32px] overflow-hidden shadow-2xl group">
|
|
|
<img
|
|
<img
|
|
|
- src={product.image}
|
|
|
|
|
- alt={product.country}
|
|
|
|
|
|
|
+ src={area.imgUrl}
|
|
|
|
|
+ alt={area.areaName1}
|
|
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
|
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
|
|
|
/>
|
|
/>
|
|
|
<div className="absolute top-6 left-6 flex items-start space-x-4">
|
|
<div className="absolute top-6 left-6 flex items-start space-x-4">
|
|
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-full overflow-hidden border-2 border-white/50 shadow-lg">
|
|
<div className="w-12 h-12 md:w-16 md:h-16 rounded-full overflow-hidden border-2 border-white/50 shadow-lg">
|
|
|
<img
|
|
<img
|
|
|
- src={`https://flagcdn.com/w160/${product.flag}.png`}
|
|
|
|
|
- alt={product.country}
|
|
|
|
|
|
|
+ src={`${area.iconUrl}`}
|
|
|
|
|
+ alt={area.areaName1}
|
|
|
className="w-full h-full object-cover scale-150"
|
|
className="w-full h-full object-cover scale-150"
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="text-white">
|
|
<div className="text-white">
|
|
|
<h1 className="text-2xl md:text-4xl font-black drop-shadow-md">
|
|
<h1 className="text-2xl md:text-4xl font-black drop-shadow-md">
|
|
|
- SIM {product.country}
|
|
|
|
|
|
|
+ SIM {area.areaName1}
|
|
|
</h1>
|
|
</h1>
|
|
|
<p className="text-sm md:text-lg font-bold text-white/90">
|
|
<p className="text-sm md:text-lg font-bold text-white/90">
|
|
|
Verified: <span className="text-[#EE0434]">High Speed</span>
|
|
Verified: <span className="text-[#EE0434]">High Speed</span>
|
|
@@ -124,10 +276,14 @@ const ProductDetailView: React.FC = () => {
|
|
|
{daysOptions.map((day) => (
|
|
{daysOptions.map((day) => (
|
|
|
<button
|
|
<button
|
|
|
key={day}
|
|
key={day}
|
|
|
- onClick={() => setSelectedDays(day)}
|
|
|
|
|
|
|
+ onClick={() =>
|
|
|
|
|
+ daysActiveOptions.includes(day) && handleSelectDay(day)
|
|
|
|
|
+ }
|
|
|
className={`min-w-[50px] md:min-w-[70px] h-10 md:h-14 rounded-xl md:rounded-2xl font-bold text-base md:text-xl transition-all border-2 ${
|
|
className={`min-w-[50px] md:min-w-[70px] h-10 md:h-14 rounded-xl md:rounded-2xl font-bold text-base md:text-xl transition-all border-2 ${
|
|
|
selectedDays === day
|
|
selectedDays === day
|
|
|
? "border-[#EE0434] text-white bg-[#EE0434] shadow-md"
|
|
? "border-[#EE0434] text-white bg-[#EE0434] shadow-md"
|
|
|
|
|
+ : daysActiveOptions.includes(day)
|
|
|
|
|
+ ? "border-[#ffffff] text-black bg-[#ffffff] shadow-md"
|
|
|
: "border-slate-100 text-slate-300"
|
|
: "border-slate-100 text-slate-300"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
@@ -145,41 +301,72 @@ const ProductDetailView: React.FC = () => {
|
|
|
{dataOptions.map((data) => (
|
|
{dataOptions.map((data) => (
|
|
|
<button
|
|
<button
|
|
|
key={data}
|
|
key={data}
|
|
|
- onClick={() => setSelectedData(data)}
|
|
|
|
|
|
|
+ onClick={() =>
|
|
|
|
|
+ dataActiveOptions.includes(data) && handleSelectData(data)
|
|
|
|
|
+ }
|
|
|
className={`h-10 md:h-14 rounded-xl md:rounded-2xl font-bold text-sm md:text-xl transition-all border-2 ${
|
|
className={`h-10 md:h-14 rounded-xl md:rounded-2xl font-bold text-sm md:text-xl transition-all border-2 ${
|
|
|
selectedData === data
|
|
selectedData === data
|
|
|
? "border-[#EE0434] text-white bg-[#EE0434] shadow-md"
|
|
? "border-[#EE0434] text-white bg-[#EE0434] shadow-md"
|
|
|
|
|
+ : dataActiveOptions.includes(data)
|
|
|
|
|
+ ? "border-[#ffffff] text-black bg-[#ffffff] shadow-md"
|
|
|
: "border-slate-100 text-slate-300"
|
|
: "border-slate-100 text-slate-300"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- {data}
|
|
|
|
|
|
|
+ {data === "0" ? "Unlimited" : data + " MB"}
|
|
|
</button>
|
|
</button>
|
|
|
))}
|
|
))}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div className="space-y-4">
|
|
|
|
|
- <div className="flex p-1 bg-slate-50 rounded-2xl border border-slate-100">
|
|
|
|
|
- <button
|
|
|
|
|
- onClick={() => setSimType("eSIM")}
|
|
|
|
|
- className={`flex-1 py-3 md:py-4 rounded-xl font-black text-sm md:text-2xl transition-all ${
|
|
|
|
|
- simType === "eSIM"
|
|
|
|
|
- ? "bg-[#EE0434] text-white shadow-lg"
|
|
|
|
|
- : "text-slate-300"
|
|
|
|
|
- }`}
|
|
|
|
|
- >
|
|
|
|
|
- eSIM
|
|
|
|
|
- </button>
|
|
|
|
|
- <button
|
|
|
|
|
- onClick={() => setSimType("Physical")}
|
|
|
|
|
- className={`flex-1 py-3 md:py-4 rounded-xl font-black text-sm md:text-2xl transition-all ${
|
|
|
|
|
- simType === "Physical"
|
|
|
|
|
- ? "bg-[#EE0434] text-white shadow-lg"
|
|
|
|
|
- : "text-slate-300"
|
|
|
|
|
- }`}
|
|
|
|
|
- >
|
|
|
|
|
- Physical SIM
|
|
|
|
|
- </button>
|
|
|
|
|
|
|
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
|
|
|
+ <div className="space-y-4">
|
|
|
|
|
+ <h3 className="text-lg md:text-xl font-black text-slate-900 tracking-tight">
|
|
|
|
|
+ SIM Type
|
|
|
|
|
+ </h3>
|
|
|
|
|
+ <div className="flex p-1 bg-slate-50 rounded-2xl border border-slate-100">
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() => setSimType("eSIM")}
|
|
|
|
|
+ className={`flex-1 py-3 md:py-4 rounded-xl font-black text-sm md:text-2xl transition-all ${
|
|
|
|
|
+ simType === "eSIM"
|
|
|
|
|
+ ? "bg-[#EE0434] text-white shadow-lg"
|
|
|
|
|
+ : "text-slate-300"
|
|
|
|
|
+ }`}
|
|
|
|
|
+ >
|
|
|
|
|
+ eSIM
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() => setSimType("Physical")}
|
|
|
|
|
+ className={`flex-1 py-3 md:py-4 rounded-xl font-black text-sm md:text-2xl transition-all ${
|
|
|
|
|
+ simType === "Physical"
|
|
|
|
|
+ ? "bg-[#EE0434] text-white shadow-lg"
|
|
|
|
|
+ : "text-slate-300"
|
|
|
|
|
+ }`}
|
|
|
|
|
+ >
|
|
|
|
|
+ Physical SIM
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="space-y-4">
|
|
|
|
|
+ <h3 className="text-lg md:text-xl font-black text-slate-900 tracking-tight">
|
|
|
|
|
+ Quantity
|
|
|
|
|
+ </h3>
|
|
|
|
|
+ <div className="flex items-center space-x-4 p-2 bg-slate-50 rounded-2xl border border-slate-100 h-[68px] md:h-[76px]">
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() => handleQuantityChange(-1)}
|
|
|
|
|
+ className="w-12 h-full bg-white rounded-xl shadow-sm text-slate-600 font-bold text-2xl hover:bg-slate-100 transition-colors"
|
|
|
|
|
+ >
|
|
|
|
|
+ -
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <span className="flex-1 text-center font-black text-2xl text-slate-900">
|
|
|
|
|
+ {quantity}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() => handleQuantityChange(1)}
|
|
|
|
|
+ className="w-12 h-full bg-white rounded-xl shadow-sm text-slate-600 font-bold text-2xl hover:bg-slate-100 transition-colors"
|
|
|
|
|
+ >
|
|
|
|
|
+ +
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -197,8 +384,19 @@ const ProductDetailView: React.FC = () => {
|
|
|
${prices.final}
|
|
${prices.final}
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <button className="w-full bg-[#EE0434] text-white py-4 rounded-full font-black text-lg shadow-xl hover:scale-105 transition-all">
|
|
|
|
|
- Buy now
|
|
|
|
|
|
|
+ <button
|
|
|
|
|
+ disabled={selectedData && selectedDays && loading.isSmallLoading}
|
|
|
|
|
+ className={`w-full py-4 md:py-5 rounded-2xl font-black text-xl md:text-2xl shadow-lg transition-all flex items-center justify-center space-x-3 ${
|
|
|
|
|
+ selectedData && selectedDays && !loading.isSmallLoading
|
|
|
|
|
+ ? "bg-[#EE0434] text-white hover:scale-[1.01] active:scale-[0.98]"
|
|
|
|
|
+ : "bg-slate-100 text-slate-300 cursor-not-allowed"
|
|
|
|
|
+ }`}
|
|
|
|
|
+ onClick={handleBuyNow}
|
|
|
|
|
+ >
|
|
|
|
|
+ {loading.isSmallLoading && (
|
|
|
|
|
+ <div className="w-5 h-5 border-3 border-white/30 border-t-red-500 rounded-full animate-spin"></div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ <span>Buy now</span>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|