199 lines
5.8 KiB
JavaScript
199 lines
5.8 KiB
JavaScript
import { useState, useEffect } from "react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { toast } from "sonner";
|
|
import { itemOfferApi, tipsApi } from "@/utils/api";
|
|
import { t } from "@/utils";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { FaCheck } from "react-icons/fa";
|
|
import { useNavigate } from "@/components/Common/useNavigate";
|
|
|
|
const MakeOfferModal = ({ isOpen, onClose, productDetails }) => {
|
|
const { navigate } = useNavigate();
|
|
const [offerAmount, setOfferAmount] = useState("");
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [error, setError] = useState("");
|
|
const [tips, setTips] = useState([]);
|
|
const [isLoadingTips, setIsLoadingTips] = useState(true);
|
|
const [canProceed, setCanProceed] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
fetchTips();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const fetchTips = async () => {
|
|
try {
|
|
setIsLoadingTips(true);
|
|
const response = await tipsApi.tips();
|
|
if (response?.data?.error === false) {
|
|
const tipsData = response.data.data || [];
|
|
setTips(tipsData);
|
|
// If no tips found, automatically set canProceed to true
|
|
if (!tipsData.length) {
|
|
setCanProceed(true);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching tips:", error);
|
|
// If error occurs, show make offer interface
|
|
setCanProceed(true);
|
|
} finally {
|
|
setIsLoadingTips(false);
|
|
}
|
|
};
|
|
|
|
const validateOffer = () => {
|
|
if (!offerAmount.trim()) {
|
|
setError(t("offerAmountRequired"));
|
|
return false;
|
|
}
|
|
|
|
const amount = Number(offerAmount);
|
|
if (amount >= productDetails?.price) {
|
|
setError(t("offerMustBeLessThanSellerPrice"));
|
|
return false;
|
|
}
|
|
|
|
setError("");
|
|
return true;
|
|
};
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
|
|
if (!validateOffer()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsSubmitting(true);
|
|
const response = await itemOfferApi.offer({
|
|
item_id: productDetails?.id,
|
|
amount: Number(offerAmount),
|
|
});
|
|
|
|
if (response?.data?.error === false) {
|
|
toast.success(t("offerSentSuccessfully"));
|
|
onClose();
|
|
navigate("/chat?activeTab=buying&chatid=" + response?.data?.data?.id);
|
|
} else {
|
|
toast.error(t("unableToSendOffer"));
|
|
}
|
|
} catch (error) {
|
|
toast.error(t("unableToSendOffer"));
|
|
console.error(error);
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const handleChange = (e) => {
|
|
setOfferAmount(e.target.value);
|
|
setError(""); // Clear error when user starts typing
|
|
};
|
|
|
|
const handleContinue = () => {
|
|
setCanProceed(true);
|
|
};
|
|
|
|
const renderMakeOfferForm = () => (
|
|
<div className="space-y-4">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-4xl font-normal">
|
|
{t("makeAn")}
|
|
<span className="text-primary"> {t("offer")}</span>
|
|
</DialogTitle>
|
|
<DialogDescription>{t("openToOffers")}</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="flex flex-col gap-2 bg-muted py-6 px-3 rounded-md justify-center items-center">
|
|
<span>{t("sellerPrice")}</span>
|
|
<span className="text-2xl font-medium">
|
|
{productDetails?.formatted_price}
|
|
</span>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-3">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="offerAmount" className="requiredInputLabel">
|
|
{t("yourOffer")}
|
|
</Label>
|
|
<Input
|
|
id="offerAmount"
|
|
type="number"
|
|
value={offerAmount}
|
|
onChange={handleChange}
|
|
placeholder={t("typeOfferPrice")}
|
|
min={0}
|
|
className={error ? "border-red-500 focus-visible:ring-red-500" : ""}
|
|
/>
|
|
{error && <span className="text-red-500 text-sm">{error}</span>}
|
|
</div>
|
|
<Button
|
|
type="submit"
|
|
className="py-2 px-4 rounded-md"
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting ? t("sending") : t("sendOffer")}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
);
|
|
|
|
const renderTipsSection = () => (
|
|
<>
|
|
<DialogHeader>
|
|
<DialogTitle className="text-4xl text-center font-normal">
|
|
{t("safety")}
|
|
<span className="text-primary"> {t("tips")}</span>
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
{isLoadingTips ? (
|
|
<div className="space-y-3">
|
|
{Array.from({ length: 12 }).map((_, i) => (
|
|
<div key={i} className="flex items-center space-x-4">
|
|
<Skeleton className="h-4 w-[5%]" />
|
|
<Skeleton className="h-4 w-[95%]" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{tips.map((tip, index) => (
|
|
<div key={tip?.id} className="flex items-center gap-2">
|
|
<div className="p-2 text-white bg-primary rounded-full">
|
|
<FaCheck size={18} />
|
|
</div>
|
|
<p className="">{tip?.description}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
<Button onClick={handleContinue}>{t("continue")}</Button>
|
|
</>
|
|
);
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
<DialogContent
|
|
onInteractOutside={(e) => e.preventDefault()}
|
|
className="sm:py-[50px] sm:px-[90px]"
|
|
>
|
|
{!canProceed ? renderTipsSection() : renderMakeOfferForm()}
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export default MakeOfferModal;
|