Skip to content

Commit

Permalink
Dashboard verbiage and warnings (#3332)
Browse files Browse the repository at this point in the history
* ordering

* total grant in the bottom

* rename and capitalize

* enumerate grant inputs
  • Loading branch information
ap-justin authored Sep 25, 2024
1 parent 30bb609 commit 5e74e9b
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 55 deletions.
1 change: 0 additions & 1 deletion src/pages/Admin/Charity/Dashboard/Loaded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ export function Loaded({ classes = "", ...props }: Props) {
amount={props.balances.payoutsPending}
periodNext={period.next}
periodRemaining={period.distance}
grantFromBal={mov["liq-cash"] + mov["lock-cash"]}
allocation={props.allocation}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Admin/Charity/Dashboard/Movements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function Movements({ classes = "", ...props }: Props) {
return (
<div className={`p-4 grid rounded border border-gray-l4 ${classes}`}>
<h4 className="grid border-b border-gray-l4 w-full pb-2">
Pending transactions
Pending Transactions
</h4>
<div className="grid grid-cols-[auto_auto_auto_auto_auto_1fr] mt-4 gap-y-2">
{movs.map((entry) => {
Expand Down
33 changes: 2 additions & 31 deletions src/pages/Admin/Charity/Dashboard/Schedule/Schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@ import { useAdminContext } from "pages/Admin/Context";
import type { ReactNode } from "react";
import type { Allocation } from "types/aws";
import { Edit } from "./Edit";
import {
MIN_PROCESSING_AMOUNT,
allocationOptions,
toAllocOptValue,
} from "./common";
import { allocationOptions, toAllocOptValue } from "./common";

interface Props {
amount: number;
allocation: Allocation;
classes?: string;
periodNext: string;
periodRemaining: string;
grantFromBal: number;
disabled?: boolean;
}
export function Schedule(props: Props) {
Expand All @@ -32,7 +27,7 @@ export function Schedule(props: Props) {
return (
<div className="p-4 grid rounded border border-gray-l4 mt-4">
<div className="grid border-b border-gray-l4 w-full pb-2">
<h4 className="mb-1">Donations received</h4>
<h4 className="mb-1">Current Month Donations</h4>
<p className="font-heading font-medium">$ {humanize(props.amount)}</p>
</div>
<div className="flex items-center mt-4 gap-x-2">
Expand Down Expand Up @@ -78,28 +73,6 @@ export function Schedule(props: Props) {
}
pct={props.allocation.cash}
amount={props.amount}
tooltip={(val) =>
val !== 0 &&
/** include additional grant from bal */
val + props.grantFromBal < MIN_PROCESSING_AMOUNT && (
<Tooltip
tip={
<Content className="max-w-xs text-sm bg-navy-d4 text-gray-l4 p-3 rounded-lg">
Grant amount of $ {humanize(val)} is less than minimum
processing amount of ${MIN_PROCESSING_AMOUNT} and would be
carried over to the next month.
<Arrow />
</Content>
}
>
<Icon
type="Info"
size={16}
className="inline mr-auto text-amber"
/>
</Tooltip>
)
}
/>
<Row
icon={
Expand All @@ -126,7 +99,6 @@ interface IRow {
pct: number;
icon: ReactNode;
title: ReactNode;
tooltip?: (val: number) => ReactNode;
}
function Row(props: IRow) {
const val = props.amount * (props.pct / 100);
Expand All @@ -141,7 +113,6 @@ function Row(props: IRow) {
<span className="text-left font-heading font-medium">
{humanize(val)}
</span>
{props.tooltip?.(val)}
</div>
);
}
102 changes: 80 additions & 22 deletions src/pages/Admin/Charity/Dashboard/Summary.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Icon from "components/Icon";
import { Arrow, Content, Tooltip } from "components/Tooltip";
import { humanize } from "helpers";
import type { ReactNode } from "react";
import type { Allocation, BalanceMovement, EndowmentBalances } from "types/aws";
import { MIN_PROCESSING_AMOUNT } from "./Schedule/common";

interface Props {
balances: EndowmentBalances;
Expand All @@ -13,28 +15,50 @@ interface Props {
export function Summary({ classes = "", ...props }: Props) {
const liqDonation = props.balances.payoutsPending * (props.alloc.liq / 100);
const lockDonation = props.balances.payoutsPending * (props.alloc.lock / 100);
const grantDonation =
props.balances.payoutsPending * (props.alloc.cash / 100);

const liqItems = [
["Donation", liqDonation],
["Grant", -props.mov["liq-cash"]],
["Investment", -props.mov["liq-lock"]],
["Savings", props.mov["lock-liq"]],
].filter(([, v]) => Math.abs(+v) > 0) as [string, number][];
["0", "from Donation", liqDonation] as const,
["1", "from Investment", props.mov["lock-liq"]] as const,
["2", "to Investment", -props.mov["liq-lock"]] as const,
["3", "to Grant", -props.mov["liq-cash"]] as const,
]
.toSorted(([ka], [kb]) => ka.localeCompare(kb))
.filter(([, , v]) => Math.abs(+v) > 0)
.map(([, k, v]) => [k, v]);

const lockItems = [
["Donation", lockDonation],
["Grant", -props.mov["lock-cash"]],
["Savings", -props.mov["lock-liq"]],
["Investment", props.mov["liq-lock"]],
].filter(([, v]) => Math.abs(+v) > 0) as [string, number][];
["0", "from Donation", lockDonation] as const,
["1", "from Savings", props.mov["liq-lock"]] as const,
["2", "to Savings", -props.mov["lock-liq"]] as const,
["3", "to Grant", -props.mov["lock-cash"]] as const,
]
.toSorted(([ka], [kb]) => ka.localeCompare(kb))
.filter(([, , v]) => Math.abs(+v) > 0)
.map(([, k, v]) => [k, v]);

const grantItems = [
["0", "from Donations", grantDonation] as const,
["1", "from Savings", props.mov["liq-cash"]] as const,
["2", "from Investment", props.mov["lock-cash"]] as const,
]
.toSorted(([ka], [kb]) => ka.localeCompare(kb))
.filter(([, , v]) => Math.abs(+v) > 0)
.map(([, k, v]) => [k, v]);

//no changes
if (liqItems.length === 0 && lockItems.length === 0) return null;
if (
liqItems.length === 0 &&
lockItems.length === 0 &&
grantItems.length === 0
)
return null;

return (
<div className={`${classes} p-4 rounded border border-gray-l4`}>
<h4 className="mb-6 flex items-center gap-2">
<span>Ending balances</span>
<span>Projected Month End Balances</span>
<Tooltip
tip={
<Content className="bg-navy-d4 text-gray-l2 text-xs p-2 rounded">
Expand All @@ -48,15 +72,43 @@ export function Summary({ classes = "", ...props }: Props) {
</Tooltip>
</h4>
<div className="grid grid-cols-[auto_auto_auto_1fr]">
<Balance
title="Grants"
balance={null}
changes={grantItems as [string, number][]}
classes="mb-4"
tooltip={(change) => {
if (change === 0) return null;
if (change >= MIN_PROCESSING_AMOUNT) return null;
return (
<Tooltip
tip={
<Content className="max-w-xs text-sm bg-navy-d4 text-gray-l4 p-3 rounded-lg">
Total Grant is less than minimum processing amount of $
{MIN_PROCESSING_AMOUNT} and would be carried over to the
next month.
<Arrow />
</Content>
}
>
<Icon
type="Info"
size={16}
className="inline mr-auto text-amber"
/>
</Tooltip>
);
}}
/>
<Balance
title="Savings"
balance={props.balances.liq ?? 0}
changes={liqItems}
changes={liqItems as [string, number][]}
/>
<Balance
title="Investments"
balance={props.balances.sustainabilityFundBal}
changes={lockItems}
changes={lockItems as [string, number][]}
classes="mt-6"
/>
</div>
Expand All @@ -66,31 +118,37 @@ export function Summary({ classes = "", ...props }: Props) {

interface IItem {
title: string;
balance: number;
balance: number | null;
changes: [string, number][];
classes?: string;
tooltip?: (change: number) => ReactNode;
}

function Balance({ classes = "", ...props }: IItem) {
const change = props.changes.reduce((sum, [, v]) => v + sum, 0);

const headerAmount = props.balance === null ? change : props.balance + change;
const changeAmount = props.balance === null ? null : change;

return (
<div
className={`grid grid-cols-subgrid col-span-full gap-y-2 divide-y divide-gray-l4 ${classes}`}
>
<div className="grid grid-cols-subgrid items-center col-span-full">
<p className="text-sm font-semibold mr-2">{props.title}</p>
<span className="font-heading mr-2">
$ {humanize(props.balance + change)}
</span>
{change ? (
<p className="mr-2 flex items-center gap-1">
<span className="font-heading">$ {humanize(headerAmount)}</span>
{props.tooltip?.(change)}
</p>
{changeAmount !== null ? (
<p
className={`${
change > 0 ? "text-green" : "text-red"
changeAmount > 0 ? "text-green" : "text-red"
} flex items-center font-heading text-sm`}
>
&#40;
<Icon type={change < 0 ? "Dash" : "Plus"} size={14} />
<span> $ {humanize(Math.abs(change))} &#41;</span>
<Icon type={changeAmount < 0 ? "Dash" : "Plus"} size={14} />
<span> $ {humanize(Math.abs(changeAmount))} &#41;</span>
</p>
) : (
<div />
Expand Down

0 comments on commit 5e74e9b

Please sign in to comment.