mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-04-20 07:38:20 +00:00
feat: add loading skeleton for devices table and improve payment processing logic
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 4m30s
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 4m30s
This commit is contained in:
parent
067acef49c
commit
0d578c9add
@ -8,6 +8,7 @@ import type {
|
|||||||
Payment,
|
Payment,
|
||||||
} from "@/lib/backend-types";
|
} from "@/lib/backend-types";
|
||||||
import type { User } from "@/lib/types/user";
|
import type { User } from "@/lib/types/user";
|
||||||
|
import { checkSession } from "@/utils/session";
|
||||||
import { tryCatch } from "@/utils/tryCatch";
|
import { tryCatch } from "@/utils/tryCatch";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
@ -200,27 +201,15 @@ type VerifyPaymentType = {
|
|||||||
type?: "TRANSFER" | "WALLET";
|
type?: "TRANSFER" | "WALLET";
|
||||||
};
|
};
|
||||||
|
|
||||||
class InsufficientFundsError extends Error {
|
|
||||||
constructor() {
|
|
||||||
super("Insufficient funds in wallet");
|
|
||||||
this.name = "InsufficientFundsError";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function processWalletPayment({
|
export async function processWalletPayment({
|
||||||
payment,
|
payment,
|
||||||
amount,
|
amount,
|
||||||
}: { payment: Payment | undefined; amount: number }) {
|
}: { payment: Payment | undefined; amount: number }) {
|
||||||
|
await checkSession();
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session?.user || !payment) {
|
const walletBalance = session?.user?.wallet_balance ?? 0;
|
||||||
throw new Error("User or payment not found");
|
console.log("processing wallet payment >>>", walletBalance);
|
||||||
}
|
if (!payment) return;
|
||||||
|
|
||||||
const walletBalance = session.user.wallet_balance ?? 0;
|
|
||||||
if (walletBalance < amount) {
|
|
||||||
throw new InsufficientFundsError();
|
|
||||||
}
|
|
||||||
|
|
||||||
const [updatePaymentError, _] = await tryCatch(
|
const [updatePaymentError, _] = await tryCatch(
|
||||||
updatePayment({
|
updatePayment({
|
||||||
id: payment.id,
|
id: payment.id,
|
||||||
@ -235,9 +224,10 @@ export async function processWalletPayment({
|
|||||||
}
|
}
|
||||||
console.log("Wallet balance before update:", walletBalance);
|
console.log("Wallet balance before update:", walletBalance);
|
||||||
const updated_balance = walletBalance - amount;
|
const updated_balance = walletBalance - amount;
|
||||||
|
if (!session?.user?.id) return;
|
||||||
const [walletUpdateError, response] = await tryCatch(
|
const [walletUpdateError, response] = await tryCatch(
|
||||||
updateWalletBalance({
|
updateWalletBalance({
|
||||||
id: session.user.id,
|
id: session?.user?.id,
|
||||||
wallet_balance: Number.parseFloat(updated_balance?.toFixed(2) ?? "0"),
|
wallet_balance: Number.parseFloat(updated_balance?.toFixed(2) ?? "0"),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
78
app/(dashboard)/devices/device-table-skeleton.tsx
Normal file
78
app/(dashboard)/devices/device-table-skeleton.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCaption,
|
||||||
|
TableCell,
|
||||||
|
TableFooter,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export default function DevicesTableSkeleton() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<Table className="overflow-scroll">
|
||||||
|
<TableCaption>Table of all devices.</TableCaption>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Device Name</TableHead>
|
||||||
|
<TableHead>MAC Address</TableHead>
|
||||||
|
<TableHead>#</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody className="overflow-scroll">
|
||||||
|
{Array.from({ length: 10 }).map((_, i) => (
|
||||||
|
<TableRow key={`${i + 1}`}>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="w-full h-10 rounded" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="w-full h-10 rounded" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="w-full h-10 rounded" />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
<TableFooter>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={2}>
|
||||||
|
<Skeleton className="w-full h-4 rounded" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-muted-foreground">
|
||||||
|
<Skeleton className="w-20 h-4 rounded" />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableFooter>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<div className="sm:hidden my-4">
|
||||||
|
{Array.from({ length: 10 }).map((_, i) => (
|
||||||
|
<DeviceCardSkeleton key={`${i + 1}`} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeviceCardSkeleton() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex text-sm justify-between items-center my-2 p-4 border rounded-md bg-gray-100",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="font-semibold flex w-full flex-col items-start gap-2 mb-2 relative">
|
||||||
|
<Skeleton className="w-32 h-6" />
|
||||||
|
<Skeleton className="w-36 h-6" />
|
||||||
|
<Skeleton className="w-32 h-4" />
|
||||||
|
<Skeleton className="w-40 h-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -4,6 +4,7 @@ import Search from "@/components/search";
|
|||||||
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense } from "react";
|
||||||
|
import DevicesTableSkeleton from "./device-table-skeleton";
|
||||||
|
|
||||||
export default async function Devices({
|
export default async function Devices({
|
||||||
searchParams,
|
searchParams,
|
||||||
@ -11,11 +12,10 @@ export default async function Devices({
|
|||||||
searchParams: Promise<{
|
searchParams: Promise<{
|
||||||
query: string;
|
query: string;
|
||||||
page: number;
|
page: number;
|
||||||
sortBy: string;
|
|
||||||
status: string;
|
|
||||||
}>;
|
}>;
|
||||||
}) {
|
}) {
|
||||||
const query = (await searchParams)?.query || "";
|
const query = (await searchParams)?.query || "";
|
||||||
|
const page = (await searchParams)?.page || 1;
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -29,7 +29,7 @@ export default async function Devices({
|
|||||||
>
|
>
|
||||||
<Search />
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
<Suspense key={query} fallback={"loading...."}>
|
<Suspense key={query || page} fallback={<DevicesTableSkeleton />}>
|
||||||
<DevicesTable parentalControl={false} searchParams={searchParams} />
|
<DevicesTable parentalControl={false} searchParams={searchParams} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@ import { deviceCartAtom } from "@/lib/atoms";
|
|||||||
import type { Device } from "@/lib/backend-types";
|
import type { Device } from "@/lib/backend-types";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { Hourglass } from "lucide-react";
|
import { HandCoins } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import AddDevicesToCartButton from "./add-devices-to-cart-button";
|
import AddDevicesToCartButton from "./add-devices-to-cart-button";
|
||||||
import BlockDeviceDialog from "./block-device-dialog";
|
import BlockDeviceDialog from "./block-device-dialog";
|
||||||
@ -67,7 +67,8 @@ export default function ClickableRow({
|
|||||||
{device.has_a_pending_payment && (
|
{device.has_a_pending_payment && (
|
||||||
<Link href={`/payments/${device.pending_payment_id}`}>
|
<Link href={`/payments/${device.pending_payment_id}`}>
|
||||||
<span className="bg-muted rounded px-2 p-1 mt-2 flex hover:underline items-center justify-center gap-2 text-muted-foreground text-yellow-600">
|
<span className="bg-muted rounded px-2 p-1 mt-2 flex hover:underline items-center justify-center gap-2 text-muted-foreground text-yellow-600">
|
||||||
Payment Pending <Hourglass size={14} />
|
Payment Pending{" "}
|
||||||
|
<HandCoins className="animate-pulse" size={14} />
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
@ -25,7 +25,6 @@ export async function DevicesTable({
|
|||||||
searchParams: Promise<{
|
searchParams: Promise<{
|
||||||
query: string;
|
query: string;
|
||||||
page: number;
|
page: number;
|
||||||
sortBy: string;
|
|
||||||
}>;
|
}>;
|
||||||
parentalControl?: boolean;
|
parentalControl?: boolean;
|
||||||
}) {
|
}) {
|
||||||
@ -89,10 +88,6 @@ export async function DevicesTable({
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableFooter>
|
</TableFooter>
|
||||||
</Table>
|
</Table>
|
||||||
<Pagination
|
|
||||||
totalPages={meta?.last_page}
|
|
||||||
currentPage={meta?.current_page}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden my-4">
|
<div className="sm:hidden my-4">
|
||||||
{data?.map((device) => (
|
{data?.map((device) => (
|
||||||
@ -103,6 +98,10 @@ export async function DevicesTable({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<Pagination
|
||||||
|
totalPages={meta?.last_page}
|
||||||
|
currentPage={meta?.current_page}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user