Skip to content

Commit

Permalink
feat: add financials to account page
Browse files Browse the repository at this point in the history
  • Loading branch information
Carsten Koch committed Oct 31, 2024
1 parent 2ec25ae commit 7b0de11
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 21 deletions.
4 changes: 4 additions & 0 deletions components/accounts/AccountDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Account, useAccountsContext } from "@/api/ContextAccounts";
import { FC } from "react";
import CrmLink from "../crm/CrmLink";
import { Accordion } from "../ui/accordion";
import AccountFinancials from "./AccountFinancials";
import AccountIntroduction from "./AccountIntroduction";
import AccountNotes from "./AccountNotes";
import AccountPayerAccounts from "./AccountPayerAccounts";
Expand All @@ -18,6 +19,7 @@ type AccountDetailsProps = {
showProjects?: boolean;
showContacts?: boolean;
showAwsAccounts?: boolean;
showFinancials?: boolean;
showTerritories?: boolean;
updateFormControl?: {
open: boolean;
Expand All @@ -32,6 +34,7 @@ const AccountDetails: FC<AccountDetailsProps> = ({
showProjects,
updateFormControl,
showAwsAccounts,
showFinancials,
showTerritories,
showSubsidaries = true,
}) => {
Expand Down Expand Up @@ -72,6 +75,7 @@ const AccountDetails: FC<AccountDetailsProps> = ({
<AccountNotes accountId={account.id} />
<AccountPayerAccounts {...{ account, showAwsAccounts }} />
<AccountTerritories {...{ account, showTerritories }} />
<AccountFinancials {...{ account, showFinancials }} />
</Accordion>
</>
);
Expand Down
75 changes: 75 additions & 0 deletions components/accounts/AccountFinancials.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Account } from "@/api/ContextAccounts";
import {
RevenueMonth,
setAccountColumnDataFromMrr,
setAccountColumnDefFromMrr,
setLastMonthsRevenue,
setTotalRevenueFromRevenueMonth,
} from "@/helpers/analytics/account-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 AccountFinancialsProps = {
account: Account;
showFinancials?: boolean;
};

const AccountFinancials: FC<AccountFinancialsProps> = ({
account,
showFinancials,
}) => {
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(() => {
setAccountColumnDefFromMrr(mrr, noOfMonths, setColumnDef);
}, [mrr, noOfMonths]);

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

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

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

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

export default withMrrFilter(AccountFinancials);
2 changes: 1 addition & 1 deletion components/analytics/analytics-table-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const getColumnDef = (noOfMonths: number): ColumnDef<AccountMrr>[] => [
...getMonthlyMrrColumnDef(noOfMonths),
];

const getMonthName = (noOfMonths: number, id: number) =>
export const getMonthName = (noOfMonths: number, id: number) =>
flow(
identity<number>,
substract(id),
Expand Down
2 changes: 1 addition & 1 deletion docs/releases/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Tableau Daten können nun importiert werden. Dazu werden die Kunden mit ihren Payer Accounts gezeigt und den jeweiligen Umsätzen pro Monat. Es wird auf Probleme hingewiesen, wenn die Payer Accounts nicht zugeordnet werden konnten. Meist liegt das daran, dass der Kundenname nicht übereinstimmt oder dass der Payer Account dem Kunden noch zugeordnet werden muss. Wenn die Payer ID neu ist, kann sie mit einem Klick erstellt und einem Account zugeordnet werden.
- Es werden auch Perioden vergleichen, so dass sich leicht Wachstum oder dergleichen erkennen lässt.
- Von den Kunden und den Payer Accounts kann man direkt auf die Detailseite springen.
- Auf der Kundenseite werden nun auch Umsätze angezeigt.

## Packages aktualisiert

Expand All @@ -22,7 +23,6 @@ Von "Current" auf "Latest" aktualisiert:
## In Arbeit

- Seite für einen Payer Account einführen. Auch dort soll der Umsatz der vergangenen Monate angezeigt werden können.
- Auf der Kundenseite sollen auch die Umsätze angezeigt werden.

## Geplant

Expand Down
132 changes: 132 additions & 0 deletions helpers/analytics/account-data.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Mrr } from "@/api/useMrr";
import {
AccountMrr,
getMonthName,
MonthMrr,
} from "@/components/analytics/analytics-table-column";
import RenderMonthMrr from "@/components/analytics/render-month-mrr";
import RenderPayerHeader from "@/components/analytics/render-payer-header";
import { ColumnDef } from "@tanstack/react-table";
import {
filter,
flow,
identity,
map,
sortBy,
sum,
takeRight,
times,
uniq,
} from "lodash/fp";
import { Dispatch, SetStateAction } from "react";
import { byAccount, byMonth, parseMonthToInt } from "./analytics";
import {
getLastMonthMrrByAccountAndPayer,
getUniqMonths,
mapPayerMrrData,
} from "./prep-table-data";

export type RevenueMonth = {
month: Date;
mrr: number;
};

export const setAccountColumnDataFromMrr = (
account: string,
mrr: Mrr[] | undefined,
noOfMonths: number,
setColumnData: Dispatch<SetStateAction<AccountMrr[]>>
) =>
!mrr
? []
: flow(
identity<Mrr[]>,
filter(byAccount(account)),
map("awsAccountNumber"),
uniq,
map(mapPayerMrrData(mrr, account, noOfMonths)),
sortBy(getLastMonthMrrByAccountAndPayer(mrr, account)),
setColumnData
)(mrr);

export const setAccountColumnDefFromMrr = (
mrr: Mrr[] | undefined,
noOfMonths: number,
setColumnDef: Dispatch<SetStateAction<ColumnDef<AccountMrr>[]>>
) =>
!mrr ? [] : flow(identity<number>, getColumnDef, setColumnDef)(noOfMonths);

export const setLastMonthsRevenue = (
noOfMonths: number,
account: string,
mrr: Mrr[] | undefined,
setRevenue: Dispatch<SetStateAction<RevenueMonth[]>>
) =>
!mrr
? []
: flow(
identity<Mrr[]>,
getUniqMonths,
sortBy(parseMonthToInt),
takeRight(noOfMonths),
map(getMonthAccountRevenue(account, mrr)),
setRevenue
)(mrr);

export const setTotalRevenueFromRevenueMonth = (
revenueLastMonths: RevenueMonth[],
setTotalRevenue: Dispatch<SetStateAction<number>>
) =>
flow(
identity<RevenueMonth[]>,
map("mrr"),
sum,
setTotalRevenue
)(revenueLastMonths);

const getColumnDef = (noOfMonths: number): ColumnDef<AccountMrr>[] => [
{ accessorKey: "id" },
{ accessorKey: "isReseller" },
{
accessorKey: "accountOrPayer",
header: "Payer",
cell: ({ row, getValue }) => (
<RenderPayerHeader
id={row.getValue("id")}
label={getValue<string>()}
isReseller={row.getValue("isReseller")}
/>
),
},
...getMonthlyMrrColumnDef(noOfMonths),
];

const getMonthAccountRevenue =
(account: string, mrr: Mrr[]) =>
(month: string): RevenueMonth =>
flow(
identity<Mrr[]>,
filter(byAccount(account)),
filter(byMonth(month)),
map("mrr"),
sum,
(mrrTotal) => ({ month: new Date(`${month}-01`), mrr: mrrTotal })
)(mrr);

const getMonthlyMrrColumnDef = (noOfMonths: number): ColumnDef<AccountMrr>[] =>
flow(
identity<number>,
times(identity),
map(
(id: number): ColumnDef<AccountMrr> => ({
accessorKey: `month${id}Mrr`,
header: getMonthName(noOfMonths, id),
cell: ({ getValue }) => (
<RenderMonthMrr
monthMrr={getValue<MonthMrr>()}
noOfMonths={noOfMonths}
/>
),
})
)
)(noOfMonths);
36 changes: 18 additions & 18 deletions helpers/analytics/prep-table-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ export const setColumnDefFromMrr = (
export const getNoOfMonths = (mrr: Mrr[] | undefined) =>
flow(identity<Mrr[] | undefined>, getUniqMonths, size)(mrr);

export const mapPayerMrrData =
(mrr: Mrr[], account: string, noOfMonths: number) =>
(payer: string): AccountMrr => ({
id: payer,
accountOrPayer: payer,
isReseller: getResellerState(mrr, account, payer, noOfMonths),
...getMrrMonths(mrr, account, payer, noOfMonths),
children: [],
});

export const getLastMonthMrrByAccountAndPayer =
(mrr: Mrr[], account: string) =>
({ accountOrPayer }: AccountMrr) =>
getLastMonthMrr(mrr, account, accountOrPayer);

export const getUniqMonths = (mrr: Mrr[] | undefined) =>
flow(identity<Mrr[] | undefined>, map("month"), uniq)(mrr);

const mapCompanyMrrData =
(mrr: Mrr[], noOfMonths: number) =>
(account: string): AccountMrr => ({
Expand Down Expand Up @@ -94,16 +112,6 @@ const getResellerState = (
some((mrr) => mrr.isReseller)
)(mrr);

const mapPayerMrrData =
(mrr: Mrr[], account: string, noOfMonths: number) =>
(payer: string): AccountMrr => ({
id: payer,
accountOrPayer: payer,
isReseller: getResellerState(mrr, account, payer, noOfMonths),
...getMrrMonths(mrr, account, payer, noOfMonths),
children: [],
});

const takeNoOfMonths = (noOfMonths: number) =>
flow(
identity<Mrr[]>,
Expand Down Expand Up @@ -146,11 +154,6 @@ const getLastMonthMrrByAccount =
({ accountOrPayer }: AccountMrr) =>
getLastMonthMrr(mrr, accountOrPayer, undefined);

const getLastMonthMrrByAccountAndPayer =
(mrr: Mrr[], account: string) =>
({ accountOrPayer }: AccountMrr) =>
getLastMonthMrr(mrr, account, accountOrPayer);

const getPayerMrrDataByAccount = (
mrr: Mrr[],
account: string,
Expand Down Expand Up @@ -220,6 +223,3 @@ const diffInCalMonths = (month: string) =>
const getLastMonth = (mrr: Mrr[]) =>
flow(identity<Mrr[]>, getUniqMonths, sortBy(parseMonthToInt), last)(mrr) ??
"";

const getUniqMonths = (mrr: Mrr[] | undefined) =>
flow(identity<Mrr[] | undefined>, map("month"), uniq)(mrr);
3 changes: 2 additions & 1 deletion pages/accounts/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Account, useAccountsContext } from "@/api/ContextAccounts";
import AccountDetails from "@/components/accounts/AccountDetails";
import MainLayout from "@/components/layouts/MainLayout";
import { flow } from "lodash/fp";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { flow } from "lodash/fp";

const AccountDetailPage = () => {
const router = useRouter();
Expand Down Expand Up @@ -43,6 +43,7 @@ const AccountDetailPage = () => {
showProjects
showContacts
showAwsAccounts
showFinancials
showTerritories
updateFormControl={{
open: updateAccountFormOpen,
Expand Down

0 comments on commit 7b0de11

Please sign in to comment.