Skip to content

Commit

Permalink
feat: payer ID page added, enabled linking of resellers and financial…
Browse files Browse the repository at this point in the history
… data for resellers
  • Loading branch information
Carsten Koch committed Oct 31, 2024
1 parent 7b0de11 commit 3424ee1
Show file tree
Hide file tree
Showing 21 changed files with 897 additions and 76 deletions.
21 changes: 8 additions & 13 deletions api/ContextAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@ import {
calcOrder,
getQuotaFromTerritoryOrSubsidaries,
} from "@/helpers/accounts";
import {
createPayerAccountLink,
deletePayerAccountLink,
getOrCreatePayerAccount,
} from "@/helpers/payers/api-actions";
import { transformNotesVersion } from "@/helpers/ui-notes-writer";
import { JSONContent } from "@tiptap/core";
import { SelectionSet, generateClient } from "aws-amplify/data";
import {
filter,
find,
flow,
identity,
join,
map,
sortBy,
sum,
find,
identity,
} from "lodash/fp";
import { FC, ReactNode, createContext, useContext } from "react";
import useSWR from "swr";
import { handleApiErrors } from "./globals";
import {
getOrCreatePayerAccount,
createPayerAccountLink,
getAccountPayerAccountId,
} from "@/helpers/payers/api-actions";
const client = generateClient<Schema>();

type UpdateAccountProps = {
Expand Down Expand Up @@ -440,12 +440,7 @@ export const AccountsContextProvider: FC<AccountsContextProviderProps> = ({
: { ...a, payerAccounts: a.payerAccounts.filter((p) => p !== payer) }
);
if (updated) mutate(updated, false);
const payerAccountId = await getAccountPayerAccountId(accountId, payer);
if (!payerAccountId) return;
const { data, errors } = await client.models.AccountPayerAccount.delete({
id: payerAccountId,
});
if (errors) handleApiErrors(errors, "Deleting payer failed");
const data = await deletePayerAccountLink(accountId, payer);
if (updated) mutate(updated);
if (!data) return;
toast({
Expand Down
2 changes: 2 additions & 0 deletions api/useMrr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const wipSelectionSet = [
"latestMonths.payerMrrs.companyName",
"latestMonths.payerMrrs.awsAccountNumber",
"latestMonths.payerMrrs.payerAccount.accounts.accountId",
"latestMonths.payerMrrs.payerAccount.resellerId",
"latestMonths.payerMrrs.isEstimated",
"latestMonths.payerMrrs.isReseller",
"latestMonths.payerMrrs.mrr",
Expand All @@ -37,6 +38,7 @@ export type Mrr = {
payerAccountAccountIds?: string[];
isEstimated: boolean;
isReseller: boolean;
resellerId?: string;
mrr: number;
lastYearMrr?: number;
lastPeriodMrr?: number;
Expand Down
122 changes: 122 additions & 0 deletions api/usePayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { type Schema } from "@/amplify/data/resource";
import {
createPayerAndAccountLink,
deletePayerAccountLink,
} from "@/helpers/payers/api-actions";
import { generateClient, SelectionSet } from "aws-amplify/data";
import { map } from "lodash/fp";
import useSWR from "swr";
import { handleApiErrors } from "./globals";
const client = generateClient<Schema>();

type PayerData = SelectionSet<
Schema["PayerAccount"]["type"],
typeof selectionSet
>;

const selectionSet = [
"awsAccountNumber",
"accounts.accountId",
"resellerId",
"mainContactId",
] as const;

export type Payer = {
accountNumber: string;
isReseller: boolean;
resellerId?: string;
accountIds: string[];
mainContactId?: string;
};

const mapPayer = ({
awsAccountNumber,
accounts,
resellerId,
mainContactId,
}: PayerData): Payer => ({
accountNumber: awsAccountNumber,
isReseller: !!resellerId,
resellerId: resellerId ?? undefined,
accountIds: map(({ accountId }) => accountId)(accounts) ?? [],
mainContactId: mainContactId ?? undefined,
});

const fetchPayer = (payerId?: string) => async () => {
if (!payerId) return;
const { data, errors } = await client.models.PayerAccount.get(
{
awsAccountNumber: payerId,
},
{ selectionSet }
);
if (errors) {
handleApiErrors(errors, "Loading Payer failed");
throw errors;
}
if (!data) return;

try {
return mapPayer(data);
} catch (error) {
console.error("fetchPayer", error);
throw error;
}
};

const usePayer = (payerId?: string) => {
const {
data: payer,
isLoading,
error,
mutate,
} = useSWR(`/api/payers/${payerId}`, fetchPayer(payerId));

const createPayerAccountLink = async (accountId: string | null) => {
if (!accountId) return;
if (!payer) return;
const updatedPayer = {
...payer,
accountIds: [...payer.accountIds, accountId],
} as Payer;
mutate(updatedPayer, false);
await createPayerAndAccountLink(accountId, payer.accountNumber);
mutate(updatedPayer);
};

const deletePayerAccount = async (accountId: string) => {
if (!payer) return;
const updated = {
...payer,
accountIds: payer.accountIds.filter((p) => p !== accountId),
} as Payer;
mutate(updated, false);
await deletePayerAccountLink(accountId, payer.accountNumber);
mutate(updated);
};

const attachReseller = async (resellerId: string | null) => {
if (!resellerId) return;
if (!payer) return;
const updatedPayer = { ...payer, resellerId } as Payer;
mutate(updatedPayer, false);
const { data, errors } = await client.models.PayerAccount.update({
awsAccountNumber: payer.accountNumber,
resellerId,
});
if (errors) handleApiErrors(errors, "Attaching reseller failed");
mutate(updatedPayer);
return data;
};

return {
payer,
isLoading,
error,
createPayerAccountLink,
deletePayerAccount,
attachReseller,
};
};

export default usePayer;
4 changes: 4 additions & 0 deletions components/accounts/AccountDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AccountPeople from "./AccountPeople";
import AccountProjects from "./AccountProjects";
import AccountTerritories from "./AccountTerritories";
import AccountUpdateForm from "./AccountUpdateForm";
import ResellerFinancials from "./ResellerFinancials";
import Subsidiaries from "./Subsidaries";

type AccountDetailsProps = {
Expand All @@ -21,6 +22,7 @@ type AccountDetailsProps = {
showAwsAccounts?: boolean;
showFinancials?: boolean;
showTerritories?: boolean;
showResellerFinancials?: boolean;
updateFormControl?: {
open: boolean;
setOpen: (val: boolean) => void;
Expand All @@ -36,6 +38,7 @@ const AccountDetails: FC<AccountDetailsProps> = ({
showAwsAccounts,
showFinancials,
showTerritories,
showResellerFinancials,
showSubsidaries = true,
}) => {
const { accounts, updateAccount } = useAccountsContext();
Expand Down Expand Up @@ -76,6 +79,7 @@ const AccountDetails: FC<AccountDetailsProps> = ({
<AccountPayerAccounts {...{ account, showAwsAccounts }} />
<AccountTerritories {...{ account, showTerritories }} />
<AccountFinancials {...{ account, showFinancials }} />
<ResellerFinancials {...{ account, showResellerFinancials }} />
</Accordion>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions components/accounts/AccountFinancials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
RevenueMonth,
setAccountColumnDataFromMrr,
setAccountColumnDefFromMrr,
setLastMonthsRevenue,
setLastMonthsAccountRevenue,
setTotalRevenueFromRevenueMonth,
} from "@/helpers/analytics/account-data";
import { formatDate, formatRevenue } from "@/helpers/functional";
Expand Down Expand Up @@ -47,7 +47,7 @@ const AccountFinancials: FC<AccountFinancialsProps> = ({
}, [mrr, noOfMonths, account]);

useEffect(() => {
setLastMonthsRevenue(3, account.name, mrr, setRevenueLastMonths);
setLastMonthsAccountRevenue(3, account.name, mrr, setRevenueLastMonths);
}, [mrr, account]);

useEffect(() => {
Expand Down
77 changes: 77 additions & 0 deletions components/accounts/ResellerFinancials.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Account } from "@/api/ContextAccounts";
import {
RevenueMonth,
setTotalRevenueFromRevenueMonth,
} from "@/helpers/analytics/account-data";
import { setColumnDefFromMrr } from "@/helpers/analytics/prep-table-data";
import {
setLastMonthsResellerRevenue,
setResellerColumnDataFromMrr,
} from "@/helpers/analytics/reseller-data";
import { formatDate, formatRevenue } from "@/helpers/functional";
import { ColumnDef } from "@tanstack/react-table";
import { flow } from "lodash/fp";
import { FC, useEffect, useState } from "react";
import AnalyticsTable from "../analytics/analytics-table";
import { AccountMrr } from "../analytics/analytics-table-column";
import MrrFilterBtnGrp from "../analytics/mrr-filter-btn-grp";
import { useMrrFilter, withMrrFilter } from "../analytics/useMrrFilter";
import DefaultAccordionItem from "../ui-elements/accordion/DefaultAccordionItem";

type ResellerFinancialsProps = {
account: Account;
showResellerFinancials?: boolean;
};

const ResellerFinancials: FC<ResellerFinancialsProps> = ({
account,
showResellerFinancials,
}) => {
const { mrrFilter, mrr } = useMrrFilter();
const [noOfMonths, setNoOfMonths] = useState(0);
const [columnDef, setColumnDef] = useState<ColumnDef<AccountMrr>[]>([]);
const [columnData, setColumnData] = useState<AccountMrr[]>([]);
const [revenueLastMonths, setRevenueLastMonths] = useState<RevenueMonth[]>(
[]
);
const [totalRevenue, setTotalRevenue] = useState(0);

useEffect(() => {
flow(parseInt, setNoOfMonths)(mrrFilter);
}, [mrrFilter]);

useEffect(() => {
setColumnDefFromMrr(mrr, noOfMonths, setColumnDef);
}, [mrr, noOfMonths]);

useEffect(() => {
setResellerColumnDataFromMrr(account.id, mrr, noOfMonths, setColumnData);
}, [account, mrr, noOfMonths]);

useEffect(() => {
setLastMonthsResellerRevenue(account.id, 3, mrr, setRevenueLastMonths);
}, [account, mrr]);

useEffect(() => {
setTotalRevenueFromRevenueMonth(revenueLastMonths, setTotalRevenue);
}, [revenueLastMonths]);

return (
<DefaultAccordionItem
value="reseller-financials"
triggerTitle="AWS Revenue (as Reseller)"
triggerSubTitle={revenueLastMonths.map(
({ month, mrr }) =>
`${formatDate("MMM yyyy")(month)}: ${formatRevenue(mrr)}`
)}
isVisible={!!showResellerFinancials && totalRevenue > 0}
>
<div className="space-y-6">
<MrrFilterBtnGrp />
<AnalyticsTable columns={columnDef} data={columnData} />
</div>
</DefaultAccordionItem>
);
};

export default withMrrFilter(ResellerFinancials);
5 changes: 1 addition & 4 deletions components/analytics/instructions-upload-srrp.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { FC } from "react";
import DefaultAccordionItem from "../ui-elements/accordion/DefaultAccordionItem";

type InstructionsUploadSrrpProps = {};

const InstructionsUploadSrrp: FC<InstructionsUploadSrrpProps> = ({}) => {
const InstructionsUploadSrrp = () => {
return (
<DefaultAccordionItem value="srrp" triggerTitle="SRRP">
DESCRIBE SRRP HERE (WIP)
Expand Down
55 changes: 55 additions & 0 deletions components/payers/account-accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Account, useAccountsContext } from "@/api/ContextAccounts";
import { find, flow, identity } from "lodash/fp";
import { FC, useEffect, useState } from "react";
import AccountDetails from "../accounts/AccountDetails";
import DefaultAccordionItem from "../ui-elements/accordion/DefaultAccordionItem";
import LoadingAccordionItem from "../ui-elements/accordion/LoadingAccordionItem";
import { Button } from "../ui/button";

type PayerAccountAccordionProps = {
accountId: string;
removeLinkToPayer: (accountId: string) => void;
};

const PayerAccountAccordion: FC<PayerAccountAccordionProps> = ({
accountId,
removeLinkToPayer,
}) => {
const { accounts } = useAccountsContext();
const [account, setAccount] = useState<Account | undefined>();

useEffect(() => {
flow(
identity<Account[] | undefined>,
find(["id", accountId]),
setAccount
)(accounts);
}, [accounts, accountId]);

return !account ? (
<LoadingAccordionItem
value={`account-${accountId}`}
sizeTitle="lg"
sizeSubtitle="base"
/>
) : (
<DefaultAccordionItem
value={accountId}
triggerTitle={account.name}
link={`/accounts/${accountId}`}
>
<div className="mb-6">
<Button
variant="outline"
size="sm"
onClick={() => removeLinkToPayer(accountId)}
>
Remove link to payer
</Button>
</div>
<AccountDetails account={account} />
</DefaultAccordionItem>
);
};

export default PayerAccountAccordion;
Loading

0 comments on commit 3424ee1

Please sign in to comment.