sarlink-portal/components/device-cart.tsx
i701 75ad431160 Enhance payment processing and device management features
- Introduced wallet payment option in verifyPayment function to allow users to pay using their wallet balance.
- Added new BlockDeviceDialog component for managing device blocking and unblocking actions.
- Updated DeviceCard component to display device status and integrate blocking functionality.
- Refactored DevicesTable to utilize DeviceCard for better UI representation of devices.
- Implemented Wallet component to manage wallet balance and top-up functionality.
- Enhanced API routes and Prisma schema to support wallet transactions and device blocking reasons.
- Improved overall user experience with responsive design adjustments and new UI elements.

These changes improve user control over payments and device management, enhancing the overall functionality of the application.
2024-12-25 17:21:04 +05:00

189 lines
5.8 KiB
TypeScript

"use client";
import { createPayment } from "@/actions/payment";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
cartDrawerOpenAtom,
deviceCartAtom,
numberOfMonths,
} from "@/lib/atoms";
import { authClient } from "@/lib/auth-client";
import type { PaymentType } from "@/lib/types";
import type { BillFormula, Device } from "@prisma/client";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import {
CircleDollarSign,
Loader2,
MonitorSmartphone,
Trash2,
} from "lucide-react";
import { usePathname, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import NumberInput from "./number-input";
export function DeviceCartDrawer({
billFormula,
}: {
billFormula: BillFormula | null;
}) {
const baseAmount = billFormula?.baseAmount || 100;
const discountPercentage = billFormula?.discountPercentage || 75;
const session = authClient.useSession();
const pathname = usePathname();
const devices = useAtomValue(deviceCartAtom);
const setDeviceCart = useSetAtom(deviceCartAtom);
const [months, setMonths] = useAtom(numberOfMonths);
const [isOpen, setIsOpen] = useAtom(cartDrawerOpenAtom);
const [message, setMessage] = useState("");
const [disabled, setDisabled] = useState(false);
const [total, setTotal] = useState(0);
const router = useRouter();
useEffect(() => {
if (months === 7) {
setMessage("You will get 1 month free.");
} else if (months === 12) {
setMessage("You will get 2 months free.");
} else {
setMessage("");
}
setTotal(baseAmount + ((devices.length + 1) - 1) * discountPercentage);
}, [months, devices.length, baseAmount, discountPercentage]);
if (pathname === "/payment") {
return null;
}
const data: PaymentType = {
numberOfMonths: months,
userId: session?.data?.user.id ?? "",
deviceIds: devices.map((device) => device.id),
amount: Number.parseFloat(total.toFixed(2)),
paid: false,
};
return (
<Drawer open={isOpen} onOpenChange={setIsOpen}>
<DrawerTrigger asChild>
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
<MonitorSmartphone />
{devices.length > 0 && `(${devices.length})`}
</Button>
</DrawerTrigger>
<DrawerContent>
<div className="mx-auto w-full max-w-sm">
<DrawerHeader>
<DrawerTitle>Selected Devices</DrawerTitle>
<DrawerDescription>Selected devices pay.</DrawerDescription>
</DrawerHeader>
<div className="flex max-h-[calc(100svh-400px)] flex-col overflow-auto px-4 pb-4 gap-4">
{devices.map((device) => (
<DeviceCard key={device.id} device={device} />
))}
</div>
<div className="px-4 flex flex-col gap-4">
<NumberInput
label="Set No of Months"
value={months}
onChange={(value) => setMonths(value)}
maxAllowed={12}
isDisabled={devices.length === 0}
/>
{message && (
<span className="title-bg text-lime-800 bg-lime-100/50 dark:text-lime-100 rounded text-center p-2 w-full">
{message}
</span>
)}
</div>
<DrawerFooter>
<Button
onClick={async () => {
setDisabled(true)
const payment = await createPayment(data)
setDisabled(false)
setDeviceCart([])
setMonths(1)
if (payment) {
router.push(`/payments/${payment.id}`);
setTimeout(() => setIsOpen(!isOpen), 2000);
} else {
toast.error("Something went wrong.")
}
}}
className="w-full"
disabled={devices.length === 0 || disabled}
>
{disabled ? (
<>
<Loader2 className="ml-2 animate-spin" />
</>
) : (
<>
Go to payment
<CircleDollarSign />
</>
)}
</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
<Button
onClick={() => {
setDeviceCart([]);
}}
variant="outline"
>
Reset
</Button>
</DrawerFooter>
</div>
</DrawerContent>
</Drawer>
);
}
function DeviceCard({ device }: { device: Device }) {
const setDeviceCart = useSetAtom(deviceCartAtom);
return (
<div className="relative flex h-full w-full items-center pr-4 justify-between rounded-lg border border-input bg-background shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20">
<div>
<label
htmlFor="input-33"
className="block px-3 pt-2 text-xs font-medium text-foreground"
>
{device.name}
</label>
<input
className="flex h-10 opacity-50 w-full bg-transparent px-3 pb-2 text-sm text-foreground placeholder:text-muted-foreground/70 focus-visible:outline-none"
value={device.mac}
readOnly
disabled
placeholder={"MAC Address"}
/>
</div>
<Button
onClick={() => {
setDeviceCart((prev) => prev.filter((d) => d.id !== device.id));
}}
variant={"destructive"}
>
Remove
<Trash2 />
</Button>
</div>
);
}