Enhance payment processing and device management features

- Updated `package.json` to add a new script for pushing Prisma database changes.
- Refactored payment processing functions to include payment method handling for both wallet and transfer options.
- Improved `DevicesTable` and `AdminDevicesTable` components to support new payment method display and user association.
- Updated Prisma schema to introduce a new `PaymentType` enum and modified the `Payment` model to include a `method` field.
- Enhanced UI components to improve user experience in displaying payment and device information.

These changes improve the overall functionality and maintainability of the application, particularly in payment processing and device management.
This commit is contained in:
i701 2025-01-08 23:04:30 +05:00
parent 0a63e4337e
commit 1a195d2307
9 changed files with 118 additions and 47 deletions

View File

@ -78,11 +78,15 @@ async function processWalletPayment(
data: {
paid: true,
paidAt: new Date(),
method: "WALLET",
devices: {
updateMany: {
where: { paymentId: payment.id },
data: { isActive: true, expiryDate: expiryDate },
},
updateMany: payment.devices.map((device) => ({
where: { id: device.id },
data: {
isActive: true,
expiryDate: expiryDate,
},
})),
},
},
}),
@ -136,11 +140,15 @@ async function verifyExternalPayment(
data: {
paid: true,
paidAt: new Date(),
method: "TRANSFER",
devices: {
updateMany: {
where: { paymentId: payment.id },
data: { isActive: true, expiryDate: expiryDate },
},
updateMany: payment.devices.map((device) => ({
where: { id: device.id },
data: {
isActive: true,
expiryDate: expiryDate,
},
})),
},
},
});
@ -177,16 +185,19 @@ export async function verifyPayment(data: VerifyPaymentType) {
]);
if (data.type === "WALLET") {
console.log("WALLET");
await processWalletPayment(user, payment, Number(data.absAmount));
redirect("/payments");
}
if (data.type === "TRANSFER") {
console.log({ data, payment });
const verificationResult = await verifyExternalPayment(data, payment);
await updateDevices(payment);
const verificationResult = await verifyExternalPayment(data, payment);
await updateDevices(payment);
revalidatePath("/payment[paymentId]");
revalidatePath("/payment[paymentId]");
return verificationResult;
return verificationResult;
}
} catch (error) {
console.error("Payment verification failed:", error);
throw error; // Re-throw to handle at a higher level

View File

@ -75,7 +75,10 @@ export async function AdminDevicesTable({
},
],
},
include: {
User: true,
payments: true,
},
skip: offset,
take: limit,
orderBy: {
@ -97,6 +100,7 @@ export async function AdminDevicesTable({
<TableHeader>
<TableRow>
<TableHead>Device Name</TableHead>
<TableHead>User</TableHead>
<TableHead>MAC Address</TableHead>
<TableHead>isActive</TableHead>
<TableHead>blocked</TableHead>
@ -115,14 +119,18 @@ export async function AdminDevicesTable({
>
{device.name}
</Link>
<span className="text-muted-foreground">
Active until{" "}
{new Date().toLocaleDateString("en-US", {
month: "short",
day: "2-digit",
year: "numeric",
})}
</span>
{device.isActive && (
<span className="text-muted-foreground">
Active until{" "}
{new Date().toLocaleDateString("en-US", {
month: "short",
day: "2-digit",
year: "numeric",
})}
</span>
)}
{device.blocked && (
<div className="p-2 rounded border my-2">
<span>Comment: </span>
@ -134,6 +142,8 @@ export async function AdminDevicesTable({
</div>
</TableCell>
<TableCell className="font-medium">{device.User?.name}</TableCell>
<TableCell className="font-medium">{device.mac}</TableCell>
<TableCell>
{device.isActive ? "Active" : "Inactive"}
@ -159,7 +169,7 @@ export async function AdminDevicesTable({
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={5}>
<TableCell colSpan={7}>
{query.length > 0 && (
<p className="text-sm text-muted-foreground">
Showing {devices.length} locations for &quot;{query}

View File

@ -139,14 +139,13 @@ export async function UsersPaymentsTable({
<TableCaption>Table of all users.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>ID Card</TableHead>
<TableHead>Atoll</TableHead>
<TableHead>Island</TableHead>
<TableHead>House Name</TableHead>
<TableHead>Status</TableHead>
<TableHead>Dob</TableHead>
<TableHead>Phone Number</TableHead>
<TableHead>Devices paid</TableHead>
<TableHead>User</TableHead>
<TableHead>Amount</TableHead>
<TableHead>Duration</TableHead>
<TableHead>Payment Status</TableHead>
<TableHead>Payment Method</TableHead>
<TableHead>Paid At</TableHead>
<TableHead>Action</TableHead>
</TableRow>
</TableHeader>
@ -156,7 +155,15 @@ export async function UsersPaymentsTable({
className={`${payment.paid && "title-bg dark:bg-black"}`}
key={payment.id}
>
<TableCell className="font-medium">{payment.user.name}</TableCell>
<TableCell className="font-medium">
<ol className="list-disc list-inside text-sm">
{payment.devices.map((device) => (
<li key={device.id} className="text-sm text-muted-foreground">
{device.name}
</li>
))}
</ol>
</TableCell>
<TableCell className="font-medium">{payment.user.id_card}</TableCell>
<TableCell>{payment.user?.name}</TableCell>
<TableCell>{payment.user?.name}</TableCell>

View File

@ -6,10 +6,10 @@ import Link from "next/link";
import { signup } from "@/actions/auth-actions";
import { cn } from "@/lib/utils";
import type { Island, Prisma } from "@prisma/client";
import { Loader } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useSearchParams } from "next/navigation";
import { useActionState } from "react";
import * as React from "react";
import { useActionState } from "react";
import {
Select,
@ -308,7 +308,7 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) {
</div>
<Button disabled={isPending} className="mt-4 w-full" type="submit">
{isPending ? <Loader className="animate-spin" /> : "Submit"}
{isPending ? <Loader2 className="animate-spin" /> : "Submit"}
</Button>
</div>

View File

@ -51,8 +51,10 @@ export async function DevicesTable({
},
],
NOT: {
payment: {
paid: false
payments: {
some: {
paid: false
}
}
},
isActive: isAdmin ? undefined : parentalControl,
@ -82,8 +84,10 @@ export async function DevicesTable({
},
],
NOT: {
payment: {
paid: false
payments: {
some: {
paid: false
}
},
},
isActive: parentalControl,

View File

@ -7,7 +7,8 @@
"build": "bunx prisma migrate deploy && bunx prisma generate && bunx prisma db push && next build",
"start": "next start",
"lint": "next lint",
"studio": "bunx prisma studio"
"studio": "bunx prisma studio",
"push": "bunx prisma db push"
},
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"

View File

@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "PaymentType" AS ENUM ('WALLET', 'TRANSFER');
-- AlterTable
ALTER TABLE "Payment" ADD COLUMN "method" "PaymentType" NOT NULL DEFAULT 'TRANSFER';

View File

@ -0,0 +1,28 @@
/*
Warnings:
- You are about to drop the column `paymentId` on the `Device` table. All the data in the column will be lost.
*/
-- DropForeignKey
ALTER TABLE "Device" DROP CONSTRAINT "Device_paymentId_fkey";
-- AlterTable
ALTER TABLE "Device" DROP COLUMN "paymentId";
-- CreateTable
CREATE TABLE "_DeviceToPayment" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_DeviceToPayment_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE INDEX "_DeviceToPayment_B_index" ON "_DeviceToPayment"("B");
-- AddForeignKey
ALTER TABLE "_DeviceToPayment" ADD CONSTRAINT "_DeviceToPayment_A_fkey" FOREIGN KEY ("A") REFERENCES "Device"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_DeviceToPayment" ADD CONSTRAINT "_DeviceToPayment_B_fkey" FOREIGN KEY ("B") REFERENCES "Payment"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -115,6 +115,11 @@ enum Blocker {
PARENT
}
enum PaymentType {
WALLET
TRANSFER
}
model Device {
id String @id @default(cuid())
name String
@ -129,20 +134,20 @@ model Device {
updatedAt DateTime @updatedAt
User User? @relation(fields: [userId], references: [id])
userId String?
payment Payment? @relation(fields: [paymentId], references: [id])
paymentId String?
payments Payment[]
}
model Payment {
id String @id @default(cuid())
id String @id @default(cuid())
numberOfMonths Int
amount Float
paid Boolean @default(false)
user User @relation(fields: [userId], references: [id])
paid Boolean @default(false)
user User @relation(fields: [userId], references: [id])
paidAt DateTime?
method PaymentType @default(TRANSFER)
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
devices Device[]
userId String
}