From d7f1a4f6b254ab8af9cd85e000ccd6d379eed335 Mon Sep 17 00:00:00 2001 From: Joao Ramos Date: Sun, 25 Feb 2024 16:59:21 +0000 Subject: [PATCH] Added integration for paypal payments --- src/hooks/usePaypalPayments.tsx | 23 + src/interfaces/paypal.ts | 13 + src/pages/api/payments/paypal.ts | 30 + src/pages/api/paypal/approve.ts | 19 + src/pages/payment-record.tsx | 2299 +++++++++++++++++------------- 5 files changed, 1424 insertions(+), 960 deletions(-) create mode 100644 src/hooks/usePaypalPayments.tsx create mode 100644 src/pages/api/payments/paypal.ts diff --git a/src/hooks/usePaypalPayments.tsx b/src/hooks/usePaypalPayments.tsx new file mode 100644 index 00000000..8d94eda5 --- /dev/null +++ b/src/hooks/usePaypalPayments.tsx @@ -0,0 +1,23 @@ +import {PaypalPayment} from "@/interfaces/paypal"; +import axios from "axios"; +import {useEffect, useState} from "react"; + +export default function usePaypalPayments() { + const [payments, setPayments] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + + const getData = () => { + setIsLoading(true); + axios + .get("/api/payments/paypal") + .then((response) => { + return setPayments(response.data); + }) + .finally(() => setIsLoading(false)); + }; + + useEffect(getData, []); + + return {payments, isLoading, isError, reload: getData}; +} diff --git a/src/interfaces/paypal.ts b/src/interfaces/paypal.ts index 3c1770b9..0606761d 100644 --- a/src/interfaces/paypal.ts +++ b/src/interfaces/paypal.ts @@ -35,3 +35,16 @@ export interface Payment { corporateTransfer?: string; commissionTransfer?: string; } + + +export interface PaypalPayment { + orderId: string; + userId: string; + status: string; + createdAt: Date; + value: number; + currency: string; + subscriptionDuration: number; + subscriptionDurationUnit: DurationUnit; + subscriptionExpirationDate: Date; +} \ No newline at end of file diff --git a/src/pages/api/payments/paypal.ts b/src/pages/api/payments/paypal.ts new file mode 100644 index 00000000..7c7976e5 --- /dev/null +++ b/src/pages/api/payments/paypal.ts @@ -0,0 +1,30 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from "next"; +import { app } from "@/firebase"; +import { + getFirestore, + getDocs, + collection, +} from "firebase/firestore"; +import { withIronSessionApiRoute } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; + +const db = getFirestore(app); + +export default withIronSessionApiRoute(handler, sessionOptions); + +async function get(req: NextApiRequest, res: NextApiResponse) { + const payments = await getDocs(collection(db, "paypalpayments")); + + const data = payments.docs.map((doc) => doc.data()); + res.status(200).json(data); +} + +async function handler(req: NextApiRequest, res: NextApiResponse) { + if (!req.session.user) { + res.status(401).json({ ok: false }); + return; + } + + if (req.method === "GET") await get(req, res); +} diff --git a/src/pages/api/paypal/approve.ts b/src/pages/api/paypal/approve.ts index 0ca4226d..9b105c88 100644 --- a/src/pages/api/paypal/approve.ts +++ b/src/pages/api/paypal/approve.ts @@ -53,6 +53,25 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { {merge: true}, ); + try { + await setDoc( + doc(db, 'paypalpayments', v4()), + { + orderId: id, + userId: req.session.user.id, + status: request.data.status, + createdAt: new Date().toISOString(), + value: request.data.purchase_units[0].payments.captures[0].amount.value, + currency: request.data.purchase_units[0].payments.captures[0].amount.currency_code, + subscriptionDuration: duration, + subscriptionDurationUnit: duration_unit, + subscriptionExpirationDate: updatedExpirationDate.toISOString(), + } + ); + } catch(err) { + console.error('Failed to insert paypal payment!', err); + } + if (user.type === "corporate") { const snapshot = await getDocs(collection(db, "groups")); const groups: Group[] = ( diff --git a/src/pages/payment-record.tsx b/src/pages/payment-record.tsx index 51ea0962..ec4cc59f 100644 --- a/src/pages/payment-record.tsx +++ b/src/pages/payment-record.tsx @@ -1,19 +1,28 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; -import {withIronSessionSsr} from "iron-session/next"; -import {sessionOptions} from "@/lib/session"; +import { withIronSessionSsr } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; import useUser from "@/hooks/useUser"; -import {toast, ToastContainer} from "react-toastify"; +import { toast, ToastContainer } from "react-toastify"; import Layout from "@/components/High/Layout"; -import {shouldRedirectHome} from "@/utils/navigation.disabled"; +import { shouldRedirectHome } from "@/utils/navigation.disabled"; import usePayments from "@/hooks/usePayments"; -import {Payment} from "@/interfaces/paypal"; -import {CellContext, createColumnHelper, flexRender, getCoreRowModel, HeaderGroup, useReactTable} from "@tanstack/react-table"; -import {CURRENCIES} from "@/resources/paypal"; -import {BsTrash} from "react-icons/bs"; +import usePaypalPayments from "@/hooks/usePaypalPayments"; +import { Payment, PaypalPayment } from "@/interfaces/paypal"; +import { + CellContext, + createColumnHelper, + flexRender, + getCoreRowModel, + HeaderGroup, + Table, + useReactTable, +} from "@tanstack/react-table"; +import { CURRENCIES } from "@/resources/paypal"; +import { BsTrash } from "react-icons/bs"; import axios from "axios"; -import {useEffect, useState, useMemo} from "react"; -import {AgentUser, CorporateUser, User} from "@/interfaces/user"; +import { useEffect, useState, useMemo } from "react"; +import { AgentUser, CorporateUser, User } from "@/interfaces/user"; import UserCard from "@/components/UserCard"; import Modal from "@/components/Modal"; import clsx from "clsx"; @@ -25,1019 +34,1389 @@ import Input from "@/components/Low/Input"; import ReactDatePicker from "react-datepicker"; import moment from "moment"; import PaymentAssetManager from "@/components/PaymentAssetManager"; -import {toFixedNumber} from "@/utils/number"; -import {CSVLink} from "react-csv"; +import { toFixedNumber } from "@/utils/number"; +import { CSVLink } from "react-csv"; +import { Tab } from "@headlessui/react"; -export const getServerSideProps = withIronSessionSsr(({req, res}) => { - const user = req.session.user; +export const getServerSideProps = withIronSessionSsr(({ req, res }) => { + const user = req.session.user; - if (!user || !user.isVerified) { - res.setHeader("location", "/login"); - res.statusCode = 302; - res.end(); - return { - props: { - user: null, - }, - }; - } + if (!user || !user.isVerified) { + res.setHeader("location", "/login"); + res.statusCode = 302; + res.end(); + return { + props: { + user: null, + }, + }; + } - if (shouldRedirectHome(user) || !["admin", "developer"].includes(user.type)) { - res.setHeader("location", "/"); - res.statusCode = 302; - res.end(); - return { - props: { - user: null, - }, - }; - } + if (shouldRedirectHome(user) || !["admin", "developer"].includes(user.type)) { + res.setHeader("location", "/"); + res.statusCode = 302; + res.end(); + return { + props: { + user: null, + }, + }; + } - return { - props: {user: req.session.user}, - }; + return { + props: { user: req.session.user }, + }; }, sessionOptions); const columnHelper = createColumnHelper(); +const paypalColumnHelper = createColumnHelper(); -const PaymentCreator = ({onClose, reload, showComission = false}: {onClose: () => void; reload: () => void; showComission: boolean}) => { - const [corporate, setCorporate] = useState(); - const [date, setDate] = useState(new Date()); +const PaymentCreator = ({ + onClose, + reload, + showComission = false, +}: { + onClose: () => void; + reload: () => void; + showComission: boolean; +}) => { + const [corporate, setCorporate] = useState(); + const [date, setDate] = useState(new Date()); - const {users} = useUsers(); + const { users } = useUsers(); - const price = corporate?.corporateInformation?.payment?.value || 0; - const commission = corporate?.corporateInformation?.payment?.commission || 0; - const currency = corporate?.corporateInformation?.payment?.currency || "EUR"; + const price = corporate?.corporateInformation?.payment?.value || 0; + const commission = corporate?.corporateInformation?.payment?.commission || 0; + const currency = corporate?.corporateInformation?.payment?.currency || "EUR"; - const referralAgent = useMemo(() => { - if (corporate?.corporateInformation?.referralAgent) { - return users.find((u) => u.id === corporate.corporateInformation.referralAgent); - } + const referralAgent = useMemo(() => { + if (corporate?.corporateInformation?.referralAgent) { + return users.find( + (u) => u.id === corporate.corporateInformation.referralAgent + ); + } - return undefined; - }, [corporate?.corporateInformation?.referralAgent, users]); + return undefined; + }, [corporate?.corporateInformation?.referralAgent, users]); - const submit = () => { - axios - .post(`/api/payments`, { - corporate: corporate?.id, - agent: referralAgent?.id, - agentCommission: commission, - agentValue: toFixedNumber((commission! / 100) * price!, 2), - currency, - value: price, - isPaid: false, - date: date.toISOString(), - }) - .then(() => { - toast.success("New payment has been created successfully!"); - reload(); - onClose(); - }) - .catch(() => { - toast.error("Something went wrong, please try again later!"); - }); - }; + const submit = () => { + axios + .post(`/api/payments`, { + corporate: corporate?.id, + agent: referralAgent?.id, + agentCommission: commission, + agentValue: toFixedNumber((commission! / 100) * price!, 2), + currency, + value: price, + isPaid: false, + date: date.toISOString(), + }) + .then(() => { + toast.success("New payment has been created successfully!"); + reload(); + onClose(); + }) + .catch(() => { + toast.error("Something went wrong, please try again later!"); + }); + }; - return ( -
-

New Payment

-
-
- - u.type === "corporate") as CorporateUser[] + ).map((user) => ({ + value: user.id, + meta: user, + label: `${ + user.corporateInformation?.companyInformation?.name || user.name + } - ${user.email}`, + }))} + defaultValue={{ value: "undefined", label: "Select an account" }} + onChange={(value) => + setCorporate((value as any)?.meta ?? undefined) + } + menuPortalTarget={document?.body} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
-
- -
- {}} type="number" value={price} defaultValue={0} className="col-span-3" disabled /> - {}} type="number" defaultValue={0} value={commission} disabled /> -
-
- - c.currency === currency)?.label}`} - onChange={() => null} - type="text" - defaultValue={0} - disabled - /> -
-
- )} +
+ +
+ {}} + type="number" + value={price} + defaultValue={0} + className="col-span-3" + disabled + /> + {}} + type="number" + defaultValue={0} + value={commission} + disabled + /> +
+
+ + c.currency === currency)?.label + }`} + onChange={() => null} + type="text" + defaultValue={0} + disabled + /> +
+
+ )} -
-
- - setDate(date ?? new Date())} - /> -
+
+
+ + setDate(date ?? new Date())} + /> +
-
- - null} - type="text" - defaultValue={"No country manager"} - disabled - /> -
-
-
- - -
-
-
- ); +
+ + null} + type="text" + defaultValue={"No country manager"} + disabled + /> +
+
+
+ + +
+ + + ); }; const IS_PAID_OPTIONS = [ - { - value: null, - label: "All", - }, - { - value: false, - label: "Unpaid", - }, - { - value: true, - label: "Paid", - }, + { + value: null, + label: "All", + }, + { + value: false, + label: "Unpaid", + }, + { + value: true, + label: "Paid", + }, ]; const IS_FILE_SUBMITTED_OPTIONS = [ - { - value: null, - label: "All", - }, - { - value: false, - label: "Submitted", - }, - { - value: true, - label: "Not Submitted", - }, + { + value: null, + label: "All", + }, + { + value: false, + label: "Submitted", + }, + { + value: true, + label: "Not Submitted", + }, ]; -const CSV_WHITELISTED_KEYS = ["corporateId", "corporate", "date", "amount", "agent", "agentCommission", "agentValue", "isPaid"]; +const CSV_WHITELISTED_KEYS = [ + "corporateId", + "corporate", + "date", + "amount", + "agent", + "agentCommission", + "agentValue", + "isPaid", +]; interface SimpleCSVColumn { - key: string; - label: string; - index: number; + key: string; + label: string; + index: number; } export default function PaymentRecord() { - const [selectedCorporateUser, setSelectedCorporateUser] = useState(); - const [selectedAgentUser, setSelectedAgentUser] = useState(); - const [isCreatingPayment, setIsCreatingPayment] = useState(false); - const [filters, setFilters] = useState<{filter: (p: Payment) => boolean; id: string}[]>([]); - const [displayPayments, setDisplayPayments] = useState([]); + const [selectedCorporateUser, setSelectedCorporateUser] = useState(); + const [selectedAgentUser, setSelectedAgentUser] = useState(); + const [isCreatingPayment, setIsCreatingPayment] = useState(false); + const [filters, setFilters] = useState< + { filter: (p: Payment) => boolean; id: string }[] + >([]); + const [displayPayments, setDisplayPayments] = useState([]); - const [corporate, setCorporate] = useState(); - const [agent, setAgent] = useState(); + const [corporate, setCorporate] = useState(); + const [agent, setAgent] = useState(); - const {user} = useUser({redirectTo: "/login"}); - const {users, reload: reloadUsers} = useUsers(); - const {payments: originalPayments, reload: reloadPayment} = usePayments(); - const [startDate, setStartDate] = useState(moment("01/01/2023").toDate()); - const [endDate, setEndDate] = useState(moment().endOf("day").toDate()); - const [paid, setPaid] = useState(IS_PAID_OPTIONS[0].value); - const [commissionTransfer, setCommissionTransfer] = useState(IS_FILE_SUBMITTED_OPTIONS[0].value); - const [corporateTransfer, setCorporateTransfer] = useState(IS_FILE_SUBMITTED_OPTIONS[0].value); - const reload = () => { - reloadUsers(); - reloadPayment(); - }; + const { user } = useUser({ redirectTo: "/login" }); + const { users, reload: reloadUsers } = useUsers(); + const { payments: originalPayments, reload: reloadPayment } = usePayments(); + const { payments: paypalPayments, reload: reloadPaypalPayment } = + usePaypalPayments(); + const [startDate, setStartDate] = useState( + moment("01/01/2023").toDate() + ); + const [endDate, setEndDate] = useState( + moment().endOf("day").toDate() + ); + const [paid, setPaid] = useState(IS_PAID_OPTIONS[0].value); + const [commissionTransfer, setCommissionTransfer] = useState( + IS_FILE_SUBMITTED_OPTIONS[0].value + ); + const [corporateTransfer, setCorporateTransfer] = useState( + IS_FILE_SUBMITTED_OPTIONS[0].value + ); + const reload = () => { + reloadUsers(); + reloadPayment(); + }; - const payments = useMemo(() => { - return originalPayments.filter((p: Payment) => { - const date = moment(p.date); - return date.isAfter(startDate) && date.isBefore(endDate); - }); - }, [originalPayments, startDate, endDate]); + const payments = useMemo(() => { + return originalPayments.filter((p: Payment) => { + const date = moment(p.date); + return date.isAfter(startDate) && date.isBefore(endDate); + }); + }, [originalPayments, startDate, endDate]); - useEffect(() => { - setDisplayPayments( - filters - .map((f) => f.filter) - .reduce((d, f) => d.filter(f), payments) - .sort((a, b) => moment(b.date).diff(moment(a.date))), - ); - }, [payments, filters]); + useEffect(() => { + setDisplayPayments( + filters + .map((f) => f.filter) + .reduce((d, f) => d.filter(f), payments) + .sort((a, b) => moment(b.date).diff(moment(a.date))) + ); + }, [payments, filters]); - useEffect(() => { - if (user && user.type === "agent") { - setAgent(user); - } - }, [user]); + useEffect(() => { + if (user && user.type === "agent") { + setAgent(user); + } + }, [user]); - useEffect(() => { - setFilters((prev) => [ - ...prev.filter((x) => x.id !== "agent-filter"), - ...(!agent ? [] : [{id: "agent-filter", filter: (p: Payment) => p.agent === agent.id}]), - ]); - }, [agent]); + useEffect(() => { + setFilters((prev) => [ + ...prev.filter((x) => x.id !== "agent-filter"), + ...(!agent + ? [] + : [ + { + id: "agent-filter", + filter: (p: Payment) => p.agent === agent.id, + }, + ]), + ]); + }, [agent]); - useEffect(() => { - setFilters((prev) => [ - ...prev.filter((x) => x.id !== "corporate-filter"), - ...(!corporate ? [] : [{id: "corporate-filter", filter: (p: Payment) => p.corporate === corporate.id}]), - ]); - }, [corporate]); + useEffect(() => { + setFilters((prev) => [ + ...prev.filter((x) => x.id !== "corporate-filter"), + ...(!corporate + ? [] + : [ + { + id: "corporate-filter", + filter: (p: Payment) => p.corporate === corporate.id, + }, + ]), + ]); + }, [corporate]); - useEffect(() => { - setFilters((prev) => [ - ...prev.filter((x) => x.id !== "paid"), - ...(typeof paid !== "boolean" ? [] : [{id: "paid", filter: (p: Payment) => p.isPaid === paid}]), - ]); - }, [paid]); + useEffect(() => { + setFilters((prev) => [ + ...prev.filter((x) => x.id !== "paid"), + ...(typeof paid !== "boolean" + ? [] + : [{ id: "paid", filter: (p: Payment) => p.isPaid === paid }]), + ]); + }, [paid]); - useEffect(() => { - setFilters((prev) => [ - ...prev.filter((x) => x.id !== "commissionTransfer"), - ...(typeof commissionTransfer !== "boolean" - ? [] - : [{id: "commissionTransfer", filter: (p: Payment) => !p.commissionTransfer === commissionTransfer}]), - ]); - }, [commissionTransfer]); + useEffect(() => { + setFilters((prev) => [ + ...prev.filter((x) => x.id !== "commissionTransfer"), + ...(typeof commissionTransfer !== "boolean" + ? [] + : [ + { + id: "commissionTransfer", + filter: (p: Payment) => + !p.commissionTransfer === commissionTransfer, + }, + ]), + ]); + }, [commissionTransfer]); - useEffect(() => { - setFilters((prev) => [ - ...prev.filter((x) => x.id !== "corporateTransfer"), - ...(typeof corporateTransfer !== "boolean" - ? [] - : [{id: "corporateTransfer", filter: (p: Payment) => !p.corporateTransfer === corporateTransfer}]), - ]); - }, [corporateTransfer]); + useEffect(() => { + setFilters((prev) => [ + ...prev.filter((x) => x.id !== "corporateTransfer"), + ...(typeof corporateTransfer !== "boolean" + ? [] + : [ + { + id: "corporateTransfer", + filter: (p: Payment) => + !p.corporateTransfer === corporateTransfer, + }, + ]), + ]); + }, [corporateTransfer]); - useEffect(() => { - if (user && user.type === "corporate") return setCorporate(user); - if (user && user.type === "agent") return setAgent(user); - }, [user]); + useEffect(() => { + if (user && user.type === "corporate") return setCorporate(user); + if (user && user.type === "agent") return setAgent(user); + }, [user]); - const updatePayment = (payment: Payment, key: string, value: any) => { - axios - .patch(`api/payments/${payment.id}`, {...payment, [key]: value}) - .then(() => toast.success("Updated the payment")) - .finally(reload); - }; + const updatePayment = (payment: Payment, key: string, value: any) => { + axios + .patch(`api/payments/${payment.id}`, { ...payment, [key]: value }) + .then(() => toast.success("Updated the payment")) + .finally(reload); + }; - const deletePayment = (id: string) => { - if (!confirm(`Are you sure you want to delete this payment?`)) return; + const deletePayment = (id: string) => { + if (!confirm(`Are you sure you want to delete this payment?`)) return; - axios - .delete(`/api/payments/${id}`) - .then(() => toast.success(`Deleted the "${id}" payment`)) - .catch((reason) => { - if (reason.response.status === 404) { - toast.error("Exam not found!"); - return; - } + axios + .delete(`/api/payments/${id}`) + .then(() => toast.success(`Deleted the "${id}" payment`)) + .catch((reason) => { + if (reason.response.status === 404) { + toast.error("Exam not found!"); + return; + } - if (reason.response.status === 403) { - toast.error("You do not have permission to delete an approved payment record!"); - return; - } + if (reason.response.status === 403) { + toast.error( + "You do not have permission to delete an approved payment record!" + ); + return; + } - toast.error("Something went wrong, please try again later."); - }) - .finally(reload); - }; + toast.error("Something went wrong, please try again later."); + }) + .finally(reload); + }; - const getFileAssetsColumns = () => { - if (user) { - const containerClassName = "flex gap-2 text-mti-purple-light hover:text-mti-purple-dark ease-in-out duration-300 cursor-pointer"; - switch (user.type) { - case "corporate": - return [ - columnHelper.accessor("corporateTransfer", { - header: "Corporate transfer", - id: "corporateTransfer", - cell: (info) => ( -
- -
- ), - }), - ]; - case "agent": - return [ - columnHelper.accessor("commissionTransfer", { - header: "Commission transfer", - id: "commissionTransfer", - cell: (info) => ( -
- -
- ), - }), - ]; - case "admin": - return [ - columnHelper.accessor("corporateTransfer", { - header: "Corporate transfer", - id: "corporateTransfer", - cell: (info) => ( -
- -
- ), - }), - columnHelper.accessor("commissionTransfer", { - header: "Commission transfer", - id: "commissionTransfer", - cell: (info) => ( -
- -
- ), - }), - ]; - case "developer": - return [ - columnHelper.accessor("corporateTransfer", { - header: "Corporate transfer", - id: "corporateTransfer", - cell: (info) => ( -
- -
- ), - }), - columnHelper.accessor("commissionTransfer", { - header: "Commission transfer", - id: "commissionTransfer", - cell: (info) => ( -
- -
- ), - }), - ]; - default: - return []; - } - } + const getFileAssetsColumns = () => { + if (user) { + const containerClassName = + "flex gap-2 text-mti-purple-light hover:text-mti-purple-dark ease-in-out duration-300 cursor-pointer"; + switch (user.type) { + case "corporate": + return [ + columnHelper.accessor("corporateTransfer", { + header: "Corporate transfer", + id: "corporateTransfer", + cell: (info) => ( +
+ +
+ ), + }), + ]; + case "agent": + return [ + columnHelper.accessor("commissionTransfer", { + header: "Commission transfer", + id: "commissionTransfer", + cell: (info) => ( +
+ +
+ ), + }), + ]; + case "admin": + return [ + columnHelper.accessor("corporateTransfer", { + header: "Corporate transfer", + id: "corporateTransfer", + cell: (info) => ( +
+ +
+ ), + }), + columnHelper.accessor("commissionTransfer", { + header: "Commission transfer", + id: "commissionTransfer", + cell: (info) => ( +
+ +
+ ), + }), + ]; + case "developer": + return [ + columnHelper.accessor("corporateTransfer", { + header: "Corporate transfer", + id: "corporateTransfer", + cell: (info) => ( +
+ +
+ ), + }), + columnHelper.accessor("commissionTransfer", { + header: "Commission transfer", + id: "commissionTransfer", + cell: (info) => ( +
+ +
+ ), + }), + ]; + default: + return []; + } + } - return []; - }; + return []; + }; - const columHelperValue = (key: string, info: any) => { - switch (key) { - case "agentCommission": { - const value = info.getValue(); - return {value: `${value}%`}; - } - case "agent": { - const user = users.find((x) => x.id === info.row.original.agent) as AgentUser; - return { - value: user?.name, - user, - }; - } - case "agentValue": - case "amount": { - const value = info.getValue(); - const numberValue = toFixedNumber(value, 2); - return {value: numberValue}; - } - case "date": { - const value = info.getValue(); - return {value: moment(value).format("DD/MM/YYYY")}; - } - case "corporate": { - const specificValue = info.row.original.corporate; - const user = users.find((x) => x.id === specificValue) as CorporateUser; - return { - user, - value: user?.corporateInformation.companyInformation.name || user?.name, - }; - } - case "currency": { - return { - value: info.row.original.currency, - }; - } - case "isPaid": - case "corporateId": - default: { - const value = info.getValue(); - return {value}; - } - } - }; + const columHelperValue = (key: string, info: any) => { + switch (key) { + case "agentCommission": { + const value = info.getValue(); + return { value: `${value}%` }; + } + case "agent": { + const user = users.find( + (x) => x.id === info.row.original.agent + ) as AgentUser; + return { + value: user?.name, + user, + }; + } + case "agentValue": + case "amount": { + const value = info.getValue(); + const numberValue = toFixedNumber(value, 2); + return { value: numberValue }; + } + case "date": { + const value = info.getValue(); + return { value: moment(value).format("DD/MM/YYYY") }; + } + case "corporate": { + const specificValue = info.row.original.corporate; + const user = users.find((x) => x.id === specificValue) as CorporateUser; + return { + user, + value: + user?.corporateInformation.companyInformation.name || user?.name, + }; + } + case "currency": { + return { + value: info.row.original.currency, + }; + } + case "isPaid": + case "corporateId": + default: { + const value = info.getValue(); + return { value }; + } + } + }; - const hiddenToCorporateColumns = () => { - if (user && user.type !== "corporate") - return [ - columnHelper.accessor("agent", { - header: "Country Manager", - id: "agent", - cell: (info) => { - const {user, value} = columHelperValue(info.column.id, info); - return ( -
setSelectedAgentUser(user)}> - {value} -
- ); - }, - }), - columnHelper.accessor("agentCommission", { - header: "Commission", - id: "agentCommission", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); - return <>{value}; - }, - }), - columnHelper.accessor("agentValue", { - header: "Commission Value", - id: "agentValue", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); - const currency = CURRENCIES.find((x) => x.currency === info.row.original.currency)?.label; - const finalValue = `${value} ${currency}`; - return {finalValue}; - }, - }), - ]; - return []; - }; + const hiddenToCorporateColumns = () => { + if (user && user.type !== "corporate") + return [ + columnHelper.accessor("agent", { + header: "Country Manager", + id: "agent", + cell: (info) => { + const { user, value } = columHelperValue(info.column.id, info); + return ( +
setSelectedAgentUser(user)} + > + {value} +
+ ); + }, + }), + columnHelper.accessor("agentCommission", { + header: "Commission", + id: "agentCommission", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return <>{value}; + }, + }), + columnHelper.accessor("agentValue", { + header: "Commission Value", + id: "agentValue", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + const currency = CURRENCIES.find( + (x) => x.currency === info.row.original.currency + )?.label; + const finalValue = `${value} ${currency}`; + return {finalValue}; + }, + }), + ]; + return []; + }; - const defaultColumns = [ - columnHelper.accessor("corporate", { - header: "Corporate ID", - id: "corporateId", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); - return value; - }, - }), - columnHelper.accessor("corporate", { - header: "Corporate", - id: "corporate", - cell: (info) => { - const {user, value} = columHelperValue(info.column.id, info); - return ( -
setSelectedCorporateUser(user)}> - {value} -
- ); - }, - }), - columnHelper.accessor("date", { - header: "Date", - id: "date", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); - return {value}; - }, - }), - columnHelper.accessor("value", { - header: "Amount", - id: "amount", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); - const currency = CURRENCIES.find((x) => x.currency === info.row.original.currency)?.label; - const finalValue = `${value} ${currency}`; - return {finalValue}; - }, - }), - ...hiddenToCorporateColumns(), - columnHelper.accessor("isPaid", { - header: "Paid", - id: "isPaid", - cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const defaultColumns = [ + columnHelper.accessor("corporate", { + header: "Corporate ID", + id: "corporateId", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return value; + }, + }), + columnHelper.accessor("corporate", { + header: "Corporate", + id: "corporate", + cell: (info) => { + const { user, value } = columHelperValue(info.column.id, info); + return ( +
setSelectedCorporateUser(user)} + > + {value} +
+ ); + }, + }), + columnHelper.accessor("date", { + header: "Date", + id: "date", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return {value}; + }, + }), + columnHelper.accessor("value", { + header: "Amount", + id: "amount", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + const currency = CURRENCIES.find( + (x) => x.currency === info.row.original.currency + )?.label; + const finalValue = `${value} ${currency}`; + return {finalValue}; + }, + }), + ...hiddenToCorporateColumns(), + columnHelper.accessor("isPaid", { + header: "Paid", + id: "isPaid", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); - return ( - { - if (user?.type === agent || user?.type === "corporate" || value) return null; - if (!info.row.original.commissionTransfer || !info.row.original.corporateTransfer) - return alert("All files need to be uploaded to consider it paid!"); - if (!confirm(`Are you sure you want to consider this payment paid?`)) return null; + return ( + { + if (user?.type === agent || user?.type === "corporate" || value) + return null; + if ( + !info.row.original.commissionTransfer || + !info.row.original.corporateTransfer + ) + return alert( + "All files need to be uploaded to consider it paid!" + ); + if ( + !confirm(`Are you sure you want to consider this payment paid?`) + ) + return null; - return updatePayment(info.row.original, "isPaid", e); - }}> - - - ); - }, - }), - ...getFileAssetsColumns(), - { - header: "", - id: "actions", - cell: ({row}: {row: {original: Payment}}) => { - return ( -
- {user?.type !== "agent" && ( -
deletePayment(row.original.id)}> - -
- )} -
- ); - }, - }, - ]; + return updatePayment(info.row.original, "isPaid", e); + }} + > + +
+ ); + }, + }), + ...getFileAssetsColumns(), + { + header: "", + id: "actions", + cell: ({ row }: { row: { original: Payment } }) => { + return ( +
+ {user?.type !== "agent" && ( +
deletePayment(row.original.id)} + > + +
+ )} +
+ ); + }, + }, + ]; - const table = useReactTable({ - data: displayPayments, - columns: defaultColumns, - getCoreRowModel: getCoreRowModel(), - }); + const table = useReactTable({ + data: displayPayments, + columns: defaultColumns, + getCoreRowModel: getCoreRowModel(), + }); - const getUserModal = () => { - if (user) { - if (selectedCorporateUser) { - return ( - setSelectedCorporateUser(undefined)}> - <> - {selectedCorporateUser && ( -
- { - setSelectedCorporateUser(undefined); - if (shouldReload) reload(); - }} - user={selectedCorporateUser} - disabled - /> -
- )} - -
- ); - } + const paypalColumns = [ + paypalColumnHelper.accessor("orderId", { + header: "Order ID", + id: "orderId", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return {value}; + }, + }), + paypalColumnHelper.accessor("status", { + header: "Status", + id: "status", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return {value}; + }, + }), + paypalColumnHelper.accessor("userId", { + header: "User Name", + id: "name", + cell: (info) => { + const { value } = columHelperValue("userId", info); - if (selectedAgentUser) { - return ( - setSelectedAgentUser(undefined)}> - <> - {selectedAgentUser && ( -
- { - setSelectedAgentUser(undefined); - if (shouldReload) reload(); - }} - user={selectedAgentUser} - /> -
- )} - -
- ); - } - } + const user = users.find((x) => x.id === value) as User; + return {user?.name}; + }, + }), + paypalColumnHelper.accessor("userId", { + header: "Email", + id: "email", + cell: (info) => { + const { value } = columHelperValue("userId", info); - return null; - }; + const user = users.find((x) => x.id === value) as User; + return {user?.email}; + }, + }), + paypalColumnHelper.accessor("value", { + header: "Amount", + id: "value", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + const currency = CURRENCIES.find( + (x) => x.currency === info.row.original.currency + )?.label; + const finalValue = `${value} ${currency}`; + return {finalValue}; + }, + }), + paypalColumnHelper.accessor("createdAt", { + header: "Date", + id: "createdAt", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return {moment(value).format("DD/MM/YYYY")}; + }, + }), + paypalColumnHelper.accessor("subscriptionExpirationDate", { + header: "Expiration Date", + id: "subscriptionExpirationDate", + cell: (info) => { + const { value } = columHelperValue(info.column.id, info); + return {moment(value).format("DD/MM/YYYY")}; + }, + }), + ]; - const getCSVData = () => { - const columns = table.getHeaderGroups().reduce((accm: SimpleCSVColumn[], group: HeaderGroup) => { - const whitelistedColumns = group.headers.filter((header) => CSV_WHITELISTED_KEYS.includes(header.id)); + const paypalTable = useReactTable({ + data: paypalPayments, + columns: paypalColumns, + getCoreRowModel: getCoreRowModel(), + }); + const getUserModal = () => { + if (user) { + if (selectedCorporateUser) { + return ( + setSelectedCorporateUser(undefined)} + > + <> + {selectedCorporateUser && ( +
+ { + setSelectedCorporateUser(undefined); + if (shouldReload) reload(); + }} + user={selectedCorporateUser} + disabled + /> +
+ )} + +
+ ); + } - const data = whitelistedColumns.map((data) => ({ - key: data.column.columnDef.id, - label: data.column.columnDef.header, - })) as SimpleCSVColumn[]; + if (selectedAgentUser) { + return ( + setSelectedAgentUser(undefined)} + > + <> + {selectedAgentUser && ( +
+ { + setSelectedAgentUser(undefined); + if (shouldReload) reload(); + }} + user={selectedAgentUser} + /> +
+ )} + +
+ ); + } + } - return [...accm, ...data]; - }, []); + return null; + }; - const {rows} = table.getRowModel(); + const getCSVData = () => { + const columns = table + .getHeaderGroups() + .reduce((accm: SimpleCSVColumn[], group: HeaderGroup) => { + const whitelistedColumns = group.headers.filter((header) => + CSV_WHITELISTED_KEYS.includes(header.id) + ); - const finalColumns = [ - ...columns, - { - key: "currency", - label: "Currency", - }, - ]; + const data = whitelistedColumns.map((data) => ({ + key: data.column.columnDef.id, + label: data.column.columnDef.header, + })) as SimpleCSVColumn[]; - return { - columns: finalColumns, - rows: rows.map((row) => { - return finalColumns.reduce((accm, {key}) => { - const {value} = columHelperValue(key, { - row, - getValue: () => row.getValue(key), - }); - return { - ...accm, - [key]: value, - }; - }, {}); - }), - }; - }; + return [...accm, ...data]; + }, []); - const {rows: csvRows, columns: csvColumns} = getCSVData(); - return ( - <> - - Payment Record | EnCoach - - - - - - {user && ( - - {getUserModal()} - setIsCreatingPayment(false)}> - setIsCreatingPayment(false)} - reload={reload} - showComission={user.type === "developer" || user.type === "admin"} - /> - + const { rows } = table.getRowModel(); -
-

Payment Record

-
- {(user.type === "developer" || user.type === "admin" || user.type === "agent" || user.type === "corporate") && ( - - )} - {(user.type === "developer" || user.type === "admin") && ( - - )} -
-
-
-
- - u.type === "agent") as AgentUser[]).map((user) => ({ - value: user.id, - meta: user, - label: `${user.name} - ${user.email}`, - }))} - value={agent ? {value: agent?.id, label: `${agent.name} - ${agent.email}`} : undefined} - onChange={(value) => setAgent(value !== null ? (value as any).meta : undefined)} - menuPortalTarget={document?.body} - styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), - control: (styles) => ({ - ...styles, - paddingLeft: "4px", - border: "none", - outline: "none", - ":focus": { - outline: "none", - }, - }), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - /> -
- )} -
- - e.value === commissionTransfer)} - onChange={(value) => { - if (value) return setCommissionTransfer(value.value); - setCommissionTransfer(null); - }} - menuPortalTarget={document?.body} - styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), - control: (styles) => ({ - ...styles, - paddingLeft: "4px", - border: "none", - outline: "none", - ":focus": { - outline: "none", - }, - }), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - /> -
- )} -
- - u.type === "corporate") as CorporateUser[] + ).map((user) => ({ + value: user.id, + meta: user, + label: `${ + user.corporateInformation?.companyInformation?.name || + user.name + } - ${user.email}`, + }))} + defaultValue={ + user.type === "corporate" + ? { + value: user.id, + meta: user, + label: `${ + user.corporateInformation?.companyInformation?.name || + user.name + } - ${user.email}`, + } + : undefined + } + isDisabled={user.type === "corporate"} + onChange={(value) => + setCorporate((value as any)?.meta ?? undefined) + } + menuPortalTarget={document?.body} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
+ {user.type !== "corporate" && ( +
+ + e.value === paid)} + onChange={(value) => { + if (value) return setPaid(value.value); + setPaid(null); + }} + menuPortalTarget={document?.body} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
+
+ + + moment(date).isSameOrBefore(moment(new Date())) + } + onChange={([initialDate, finalDate]: [Date, Date]) => { + setStartDate(initialDate ?? moment("01/01/2023").toDate()); + if (finalDate) { + // basicly selecting a final day works as if I'm selecting the first + // minute of that day. this way it covers the whole day + setEndDate(moment(finalDate).endOf("day").toDate()); + return; + } + setEndDate(null); + }} + /> +
+ {user.type !== "corporate" && ( +
+ + e.value === corporateTransfer + )} + onChange={(value) => { + if (value) return setCorporateTransfer(value.value); + setCorporateTransfer(null); + }} + menuPortalTarget={document?.body} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
+
+ + + + clsx( + "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", + "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", + "transition duration-300 ease-in-out", + selected + ? "bg-white shadow" + : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark" + ) + } + > + Payments + + + clsx( + "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", + "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", + "transition duration-300 ease-in-out", + selected + ? "bg-white shadow" + : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark" + ) + } + > + Paypal + + + + + {renderTable(table as Table)} + + + {renderTable(paypalTable as Table)} + + + +
+ )} + + ); }