/* eslint-disable @next/next/no-img-element */ import Head from "next/head"; import { withIronSessionSsr } from "iron-session/next"; import { sessionOptions } from "@/lib/session"; import useUser from "@/hooks/useUser"; import { toast, ToastContainer } from "react-toastify"; import Layout from "@/components/High/Layout"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; import usePayments from "@/hooks/usePayments"; 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 UserCard from "@/components/UserCard"; import Modal from "@/components/Modal"; import clsx from "clsx"; import useUsers from "@/hooks/useUsers"; import Checkbox from "@/components/Low/Checkbox"; import Button from "@/components/Low/Button"; import Select from "react-select"; 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 { Tab } from "@headlessui/react"; import { useListSearch } from "@/hooks/useListSearch"; import { checkAccess, findAllowedEntities, getTypesOfUser } from "@/utils/permissions"; import { requestUser } from "@/utils/api"; import { mapBy, redirect, serialize } from "@/utils"; import { getEntities, getEntitiesWithRoles } from "@/utils/entities.be"; import { isAdmin } from "@/utils/users"; import { Entity, EntityWithRoles } from "@/interfaces/entity"; export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") if (shouldRedirectHome(user) || checkAccess(user, getTypesOfUser(["admin", "developer", "agent", "corporate", "mastercorporate"]))) { return redirect("/") } const entityIDs = mapBy(user.entities, 'id') const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDs) const allowedEntities = findAllowedEntities(user, entities, "view_payment_record") if (allowedEntities.length === 0) return redirect("/") return { props: serialize({ user, entities: allowedEntities }), }; }, 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 { users } = useUsers(); 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); } 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!"); }); }; return (

New Payment

{ }} 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())} />
null} type="text" defaultValue={"No country manager"} disabled />
); }; const IS_PAID_OPTIONS = [ { 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", }, ]; const CSV_PAYMENTS_WHITELISTED_KEYS = ["corporateId", "corporate", "date", "amount", "agent", "agentCommission", "agentValue", "isPaid"]; const CSV_PAYPAL_WHITELISTED_KEYS = ["orderId", "status", "name", "email", "value", "createdAt", "subscriptionExpirationDate"]; interface SimpleCSVColumn { key: string; label: string; index: number; } interface PaypalPaymentWithUserData extends PaypalPayment { name: string; email: string; } const paypalFilterRows = [["email"], ["name"], ["orderId"], ["value"]]; interface Props { user: User entities: EntityWithRoles[] } export default function PaymentRecord({ user, entities }: Props) { 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 [entity, setEntity] = useState(); const [agent, setAgent] = useState(); 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 [startDatePaymob, setStartDatePaymob] = useState(moment("01/01/2023").toDate()); const [endDatePaymob, setEndDatePaymob] = 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 [selectedIndex, setSelectedIndex] = useState(0); 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(() => { 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 !== "entity-filter"), ...(!entity ? [] : [ { id: "entity-filter", filter: (p: Payment) => p.entity === entity.id, }, ]), ]); }, [entity]); 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 !== "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]); 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; 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; } 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 []; } } 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?.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 finalValue = `${value} ${info.row.original.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); 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)}>
)}
); }, }, ]; const table = useReactTable({ data: displayPayments, columns: defaultColumns, getCoreRowModel: getCoreRowModel(), }); const updatedPaypalPayments = useMemo( () => paypalPayments .filter((p) => { const date = moment(p.createdAt); return date.isAfter(startDatePaymob) && date.isBefore(endDatePaymob); }) .map((p) => { const user = users.find((x) => x.id === p.userId) as User; return { ...p, name: user?.name, email: user?.email }; }), [paypalPayments, users, startDatePaymob, endDatePaymob], ); 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("name", { header: "User Name", id: "name", cell: (info) => { const { value } = columHelperValue(info.column.id, info); return {value}; }, }), paypalColumnHelper.accessor("email", { header: "Email", id: "email", cell: (info) => { const { value } = columHelperValue(info.column.id, info); return {value}; }, }), paypalColumnHelper.accessor("value", { header: "Amount", id: "value", cell: (info) => { const { value } = columHelperValue(info.column.id, info); const finalValue = `${value} ${info.row.original.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 { rows: filteredRows, renderSearch } = useListSearch(paypalFilterRows, updatedPaypalPayments); const paypalTable = useReactTable({ data: filteredRows.sort((a, b) => moment(b.createdAt).diff(moment(a.createdAt), "second")), columns: paypalColumns, getCoreRowModel: getCoreRowModel(), }); const getUserModal = () => { if (user) { if (selectedCorporateUser) { return ( setSelectedCorporateUser(undefined)}> <> {selectedCorporateUser && (
{ setSelectedCorporateUser(undefined); if (shouldReload) reload(); }} user={selectedCorporateUser} disabled disabledFields={{ countryManager: true }} />
)}
); } if (selectedAgentUser) { return ( setSelectedAgentUser(undefined)}> <> {selectedAgentUser && (
{ setSelectedAgentUser(undefined); if (shouldReload) reload(); }} user={selectedAgentUser} />
)}
); } } return null; }; const getCSVData = () => { const tables = [table, paypalTable]; const whitelists = [CSV_PAYMENTS_WHITELISTED_KEYS, CSV_PAYPAL_WHITELISTED_KEYS]; const currentTable = tables[selectedIndex]; const whitelist = whitelists[selectedIndex]; const columns = (currentTable.getHeaderGroups() as any[]).reduce((accm: any[], group: any) => { const whitelistedColumns = group.headers.filter((header: any) => whitelist.includes(header.id)); const data = whitelistedColumns.map((data: any) => ({ key: data.column.columnDef.id, label: data.column.columnDef.header, })) as SimpleCSVColumn[]; return [...accm, ...data]; }, []); const { rows } = currentTable.getRowModel(); const finalColumns = [ ...columns, { key: "currency", label: "Currency", }, ]; 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, }; }, {}); }), }; }; const { rows: csvRows, columns: csvColumns } = getCSVData(); const renderTable = (table: Table) => ( {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( ))} ))}
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
); return ( <> Payment Record | EnCoach {user && ( {getUserModal()} setIsCreatingPayment(false)}> setIsCreatingPayment(false)} reload={reload} showComission={checkAccess(user, ["developer", "admin"])} />

Payment Record

{checkAccess(user, ["developer", "admin", "agent", "corporate", "mastercorporate"]) && ( )} {checkAccess(user, ["developer", "admin"]) && ( )}
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 {checkAccess(user, ["developer", "admin"]) && ( 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", ) }> Paymob )}
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, }), }} />
)}