Vitron-Front/app/components/cart/QuantityControl.tsx
2026-04-29 01:44:16 +03:30

191 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from "react";
import { Plus, Minus, Trash2 } from "lucide-react";
import { Dialog, DialogContent, DialogOverlay } from "../ui/dialog";
import { Button } from "../ui/button";
interface QuantityControlProps {
quantity: number;
maxQuantity: number;
onIncrease: () => void;
onDecrease: () => void;
onRemove?: () => void;
isPending: boolean;
allowManualEdit?: boolean;
onQuantityChange?: (quantity: number) => void;
}
export const QuantityControl = ({
quantity,
maxQuantity,
onIncrease,
onDecrease,
onRemove,
isPending,
allowManualEdit = false,
onQuantityChange,
}: QuantityControlProps) => {
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(quantity.toString());
const handleEditStart = () => {
if (allowManualEdit) {
setIsEditing(true);
setEditValue(quantity.toString());
}
};
const handleEditConfirm = () => {
const newQuantity = parseInt(editValue);
if (!isNaN(newQuantity) && newQuantity > 0 && newQuantity <= maxQuantity) {
onQuantityChange?.(newQuantity);
}
setIsEditing(false);
};
const handleEditCancel = () => {
setIsEditing(false);
setEditValue(quantity.toString());
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
handleEditConfirm();
} else if (e.key === "Escape") {
handleEditCancel();
}
};
return (
<div className="flex gap-6 items-center rounded-[100px] bg-WHITE3 py-2 px-4">
<Plus
onClick={() => {
if (quantity < maxQuantity) {
onIncrease();
}
}}
className={`${quantity >= maxQuantity ? "text-GRAY" : "text-BLACK"} cursor-pointer`}
/>
{isEditing ? (
<input
type="number"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onBlur={handleEditConfirm}
onKeyDown={handleKeyDown}
className="px-2 text-B14 font-bold w-12 text-center bg-transparent border-none outline-none"
min="1"
max={maxQuantity}
/>
) : (
<span
className={`px-2 text-B14 font-bold ${allowManualEdit ? "cursor-pointer hover:bg-WHITE3 rounded" : ""}`}
onClick={handleEditStart}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
handleEditStart();
}
}}
role={allowManualEdit ? "button" : undefined}
tabIndex={allowManualEdit ? 0 : undefined}
>
{quantity}
</span>
)}
{quantity > 1 || !onRemove ? (
<Minus
size={24}
onClick={() => {
if (quantity > 1 || !onRemove) {
onDecrease();
}
}}
className={`${(quantity <= 1 && onRemove) || (quantity <= 0 && !onRemove) ? "text-GRAY" : "text-BLACK"} cursor-pointer`}
/>
) : (
<>
<Trash2
size={24}
className="text-RED cursor-pointer"
onClick={() => {
setOpenDeleteModal(true);
}}
/>
<RemoveCartModal
isPending={isPending}
onRemove={onRemove || (() => {})}
onOpenChange={setOpenDeleteModal}
open={openDeleteModal}
/>
</>
)}
</div>
);
};
interface RemoveCartModalProps {
isPending: boolean;
onRemove: () => void;
open: boolean;
onOpenChange: (open: boolean) => void;
}
const RemoveCartModal = ({
isPending,
onRemove,
open,
onOpenChange,
}: RemoveCartModalProps) => {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogOverlay />
<DialogContent className="p-4 bg-WHITE rounded-[15px] w-[calc(100%-40px)]">
<div className="flex flex-col gap-10">
<div className={"flex flex-col gap-3 items-center w-full"}>
<Trash2 className="text-RED text-6" />
<p className="text-B12 font-bold mt-1">حذف محصول از سبد خرید</p>
<p className="text-R12">این محصول از سبد شما حذف شود؟</p>
</div>
<div className="flex flex-row gap-6 w-full">
<Button
className="w-[100%]"
size={"sm"}
variant="dark"
onClick={() => onOpenChange(false)}
>
انصراف
</Button>
<Button
disabled={isPending}
className="w-[100%]"
size={"sm"}
variant="primary"
onClick={() => onRemove()}
>
{isPending ? (
<>
<div className="relative w-5 h-5 mr-2">
<div className="absolute top-0 left-0 w-full h-full border-2 border-t-RED border-r-transparent border-b-transparent border-l-transparent rounded-full animate-spin"></div>
<div
className="absolute top-0.5 left-0.5 w-4 h-4 border-2 border-t-transparent border-r-RED border-b-transparent border-l-transparent rounded-full animate-spin"
style={{
animationDirection: "reverse",
animationDuration: "0.7s",
}}
></div>
</div>
درحال حذف...
</>
) : (
"حذف از سبد خرید"
)}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
);
};