mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-04-25 10:55:41 +00:00
feat: add age validation in signup and update payment verification logic
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 5m17s
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 5m17s
This commit is contained in:
parent
f7122cb252
commit
0c093f1303
@ -121,6 +121,15 @@ export async function signup(_actionState: ActionState, formData: FormData) {
|
|||||||
errors: parsedData.error.flatten(),
|
errors: parsedData.error.flatten(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const age =
|
||||||
|
new Date().getFullYear() - new Date(parsedData.data.dob).getFullYear();
|
||||||
|
if (age < 18) {
|
||||||
|
return {
|
||||||
|
message: "You must be at least 18 years old to register.",
|
||||||
|
payload: formData,
|
||||||
|
db_error: "dob",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const idCardExists = await checkIdOrPhone({
|
const idCardExists = await checkIdOrPhone({
|
||||||
id_card: parsedData.data.id_card,
|
id_card: parsedData.data.id_card,
|
||||||
|
@ -115,80 +115,31 @@ export async function cancelPayment({ id }: { id: string }) {
|
|||||||
return { message: "Payment successfully canceled." };
|
return { message: "Payment successfully canceled." };
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePayment = Pick<
|
type UpdatePayment = {
|
||||||
Payment,
|
id: string;
|
||||||
"id" | "paid" | "paid_at" | "method" | "number_of_months"
|
method: "TRANSFER" | "WALLET";
|
||||||
>;
|
benefName?: string;
|
||||||
export async function updatePayment({
|
accountNo?: string;
|
||||||
id,
|
absAmount?: string;
|
||||||
method,
|
time?: string;
|
||||||
paid,
|
|
||||||
paid_at,
|
|
||||||
number_of_months,
|
|
||||||
}: UpdatePayment) {
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
const response = await fetch(
|
|
||||||
`${process.env.SARLINK_API_BASE_URL}/api/billing/payment/${id}/update/`,
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Token ${session?.apiToken}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
method,
|
|
||||||
paid,
|
|
||||||
paid_at,
|
|
||||||
number_of_months,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = (await response.json()) as ApiError;
|
|
||||||
const errorMessage =
|
|
||||||
errorData.message || errorData.detail || "An error occurred.";
|
|
||||||
const error = new Error(errorMessage);
|
|
||||||
(error as ApiError & { details?: ApiError }).details = errorData; // Attach the errorData to the error object
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const payment = (await response.json()) as Payment;
|
|
||||||
return payment;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TUpdateWalletBalance = Pick<User, "id" | "wallet_balance">;
|
|
||||||
export async function updateWalletBalance({
|
|
||||||
id,
|
|
||||||
wallet_balance,
|
|
||||||
}: TUpdateWalletBalance) {
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
console.log("wallet bal in server action", wallet_balance);
|
|
||||||
const response = await fetch(
|
|
||||||
`${process.env.SARLINK_API_BASE_URL}/api/auth/update-wallet/${id}/`,
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Token ${session?.apiToken}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
wallet_balance: Number.parseFloat(wallet_balance?.toFixed(2) ?? "0"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = (await response.json()) as ApiError;
|
|
||||||
const errorMessage =
|
|
||||||
errorData.message || errorData.detail || "An error occurred.";
|
|
||||||
const error = new Error(errorMessage);
|
|
||||||
(error as ApiError & { details?: ApiError }).details = errorData; // Attach the errorData to the error object
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const message = (await response.json()) as {
|
|
||||||
message: "Wallet balance updated successfully.";
|
|
||||||
};
|
};
|
||||||
return message;
|
export async function verifyPayment({ id, method }: UpdatePayment) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
const response = await fetch(
|
||||||
|
`${process.env.SARLINK_API_BASE_URL}/api/billing/payment/${id}/verify/`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Token ${session?.apiToken}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
method,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
revalidatePath("/payments/[paymentsId]");
|
||||||
|
return handleApiResponse<Payment>(response, "updatePayment");
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerifyPaymentType = {
|
type VerifyPaymentType = {
|
||||||
@ -217,43 +168,6 @@ export async function getProfile() {
|
|||||||
return handleApiResponse<User>(response, "getProfile");
|
return handleApiResponse<User>(response, "getProfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processWalletPayment({
|
|
||||||
payment,
|
|
||||||
amount,
|
|
||||||
}: { payment: Payment | undefined; amount: number }) {
|
|
||||||
await checkSession();
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
const walletBalance = session?.user?.wallet_balance ?? 0;
|
|
||||||
console.log("processing wallet payment >>>", walletBalance);
|
|
||||||
if (!payment) return;
|
|
||||||
const [updatePaymentError, _] = await tryCatch(
|
|
||||||
updatePayment({
|
|
||||||
id: payment.id,
|
|
||||||
method: "WALLET",
|
|
||||||
paid: true,
|
|
||||||
paid_at: new Date().toISOString(),
|
|
||||||
number_of_months: payment.number_of_months,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (updatePaymentError) {
|
|
||||||
throw new Error(updatePaymentError.message);
|
|
||||||
}
|
|
||||||
console.log("Wallet balance before update:", walletBalance);
|
|
||||||
const updated_balance = walletBalance - amount;
|
|
||||||
if (!session?.user?.id) return;
|
|
||||||
const [walletUpdateError, response] = await tryCatch(
|
|
||||||
updateWalletBalance({
|
|
||||||
id: session?.user?.id,
|
|
||||||
wallet_balance: Number.parseFloat(updated_balance?.toFixed(2) ?? "0"),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (walletUpdateError) {
|
|
||||||
throw new Error(walletUpdateError.message);
|
|
||||||
}
|
|
||||||
revalidatePath("/payments/[paymentsId]", "page");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerifyPaymentResponse =
|
type VerifyPaymentResponse =
|
||||||
| {
|
| {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
@ -230,6 +230,11 @@ export default function SignUpForm() {
|
|||||||
{actionState?.errors?.fieldErrors?.dob}
|
{actionState?.errors?.fieldErrors?.dob}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{actionState?.db_error === "dob" && (
|
||||||
|
<span className="text-sm inline-block text-red-500">
|
||||||
|
{actionState?.message}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="accNo" className="text-sm">
|
<label htmlFor="accNo" className="text-sm">
|
||||||
|
@ -19,7 +19,7 @@ export function DeviceCartDrawer() {
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size={"lg"}
|
size={"lg"}
|
||||||
className="bg-sarLinkOrange absolute bottom-10 w-fit z-20 left-1/2 transform -translate-x-1/2"
|
className="bg-sarLinkOrange fixed bottom-20 w-80 uppercase h-12 z-20 left-1/2 transform -translate-x-1/2"
|
||||||
onClick={() => router.push("/devices-to-pay")}
|
onClick={() => router.push("/devices-to-pay")}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
@ -27,94 +27,4 @@ export function DeviceCartDrawer() {
|
|||||||
Pay {devices.length > 0 && `(${devices.length})`} Device
|
Pay {devices.length > 0 && `(${devices.length})`} Device
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
// <>
|
|
||||||
// <Drawer open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
// <DrawerTrigger asChild>
|
|
||||||
// <Button size={"lg"} className="bg-sarLinkOrange absolute bottom-10 w-fit z-20 left-1/2 transform -translate-x-1/2" onClick={() => setIsOpen(!isOpen)} variant="outline">
|
|
||||||
// <MonitorSmartphone />
|
|
||||||
// Pay {devices.length > 0 && `(${devices.length})`} Device
|
|
||||||
// </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">
|
|
||||||
// <pre>{JSON.stringify(isOpen, null, 2)}</pre>
|
|
||||||
// {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);
|
|
||||||
// toast.promise(
|
|
||||||
// createPayment(data).then((result) => {
|
|
||||||
// if (result.success) {
|
|
||||||
// setDeviceCart([]);
|
|
||||||
// setMonths(1);
|
|
||||||
// setDisabled(false);
|
|
||||||
// if (isOpen) router.push(`/payments/${result.paymentId}`);
|
|
||||||
// setIsOpen(!isOpen);
|
|
||||||
// return "Payment created!";
|
|
||||||
// }
|
|
||||||
// }),
|
|
||||||
// {
|
|
||||||
// loading: "Processing payment...",
|
|
||||||
// success: "Payment created!",
|
|
||||||
// error: (err) => err.message || "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([]);
|
|
||||||
// setIsOpen(!isOpen);
|
|
||||||
// }}
|
|
||||||
// variant="outline"
|
|
||||||
// >
|
|
||||||
// Clear Selection
|
|
||||||
// </Button>
|
|
||||||
// </DrawerFooter>
|
|
||||||
// </div>
|
|
||||||
// </DrawerContent>
|
|
||||||
// </Drawer>
|
|
||||||
// </>
|
|
||||||
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { processWalletPayment } from "@/actions/payment";
|
import { verifyPayment } from "@/actions/payment";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -84,9 +84,9 @@ export default function DevicesToPay({
|
|||||||
disabled={verifying}
|
disabled={verifying}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setVerifying(true);
|
setVerifying(true);
|
||||||
await processWalletPayment({
|
await verifyPayment({
|
||||||
amount: payment?.amount ?? 0,
|
method: "WALLET",
|
||||||
payment: payment,
|
id: payment?.id ?? "",
|
||||||
});
|
});
|
||||||
setVerifying(false);
|
setVerifying(false);
|
||||||
}}
|
}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user