Skip to content

Commit

Permalink
movement data
Browse files Browse the repository at this point in the history
  • Loading branch information
ap-justin committed Sep 10, 2024
1 parent 1b05c6f commit 2ae2fe7
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 91 deletions.
98 changes: 9 additions & 89 deletions src/pages/Admin/Charity/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { skipToken } from "@reduxjs/toolkit/query";
import ContentLoader from "components/ContentLoader";
import Icon from "components/Icon";
import QueryLoader from "components/QueryLoader";
import { Arrow, Content } from "components/Tooltip";
import { humanize } from "helpers";
import { useEndowBalanceQuery } from "services/apes";
import type { EndowmentBalances } from "types/aws";
import { useAdminContext } from "../../Context";
import Seo from "../Seo";
import Figure from "./Figure";
import { PayoutHistory } from "./PayoutHistory";
import { Schedule } from "./Schedule";
import { monthPeriod } from "./monthPeriod";

import { Loaded } from "./Loaded";

export default function Dashboard() {
const { id } = useAdminContext();
Expand All @@ -34,90 +28,16 @@ export default function Dashboard() {
);
}

function Loaded({
classes = "",
...props
}: EndowmentBalances & { classes?: string }) {
const { id } = useAdminContext();
const period = monthPeriod();
return (
<div className={`${classes} mt-6`}>
<h3 className="uppercase mb-4 font-black">Account Balances</h3>
<div className="grid gap-4 @lg:grid-cols-2">
<Figure
title="Savings"
tooltip={
<Content className="bg-navy-d4 text-gray-l4 text-sm max-w-xs p-4 rounded-lg">
Funds held in Fidelity Government Money Market (SPAXX) consisting
of cash, US Government Securities and Repurchase Agreements
<Arrow />
</Content>
}
icon={<Icon size={21} type="PiggyBank" strokeWidth={1.5} />}
amount={`$ ${humanize(props.donationsBal - props.payoutsMade, 2)}`}
/>
<Figure
title="Investments"
tooltip={
<Content className="bg-navy-d4 text-gray-l4 text-sm max-w-xs p-4 rounded-lg shadow-lg">
<span className="block mb-2">
Funds invested in a diversified portfolio comprising
</span>
<div>
<p>50% - Domestic and international equities</p>
<p>30% - Fixed income</p>
<p>15% - Crypto</p>
<p>5% - Cash</p>
</div>
<Arrow />
</Content>
}
icon={<Icon type="Stocks" size={16} />}
amount={`$ ${humanize(props.sustainabilityFundBal, 2)}`}
/>
<Figure
title="Contributions count"
icon={<Icon type="Users" size={17} />}
amount={props.contributionsCount.toString()}
/>
</div>

<div className="w-full mt-16 h-1.5 bg-gray-l5 rounded-full shadow-inner" />

<h3 className="my-4 font-medium flex items-center">
<span className="text-sm uppercase font-normal">Period</span>
<span className="ml-2 uppercase text-sm">
{period.from} - {period.to}
</span>
<p className="text-sm text-navy-l3 ml-auto">
<span>Ends in </span>
<span className="p-1 px-2 bg-navy-d4 text-gray-l4 text-xs rounded ml-1">
in {period.distance}
</span>
</p>
</h3>
<Schedule
amount={props.payoutsPending}
periodNext={period.next}
periodRemaining={period.distance}
/>

<div className="w-full mt-16 h-1.5 bg-gray-l5 rounded-full shadow-inner" />
<PayoutHistory endowId={id} classes="mt-2" />
</div>
);
}

function LoaderSkeleton() {
return (
<div className="grid gap-4 @lg:grid-cols-2">
<div className="col-span-2 grid @2xl:grid-cols-3 gap-4">
<ContentLoader className="h-14" />
<ContentLoader className="h-14" />
<ContentLoader className="h-14" />
</div>
<ContentLoader className="h-60 w-full" />
<ContentLoader className="h-60 w-full" />
<ContentLoader className="h-40" />
<ContentLoader className="h-40" />
<ContentLoader className="h-40" />
<ContentLoader className="h-2 col-span-full" />

<ContentLoader className="h-60 col-span-full" />
<ContentLoader className="h-60 col-span-full" />
</div>
);
}
2 changes: 2 additions & 0 deletions src/pages/Admin/Charity/Dashboard/Figure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Props = {
amount: string;
/** must be wrapped by tooltip content */
tooltip?: ReactNode;
actions?: ReactNode;
};

export default function Figure(props: Props) {
Expand All @@ -30,6 +31,7 @@ export default function Figure(props: Props) {
<span className="ml-auto">{props.icon}</span>
</div>
<p className="text-2xl font-medium">{props.amount}</p>
{props.actions}
</div>
);
}
130 changes: 130 additions & 0 deletions src/pages/Admin/Charity/Dashboard/Loaded.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import Icon from "components/Icon";
import { Arrow, Content } from "components/Tooltip";
import { useModalContext } from "contexts/ModalContext";
import { humanize } from "helpers";
import { useAdminContext } from "pages/Admin/Context";
import type { EndowmentBalances } from "types/aws";
import Figure from "./Figure";
import { MoveFundForm } from "./MoveFundForm";
import { Movements } from "./Movements";
import { PayoutHistory } from "./PayoutHistory";
import { Schedule } from "./Schedule";
import { monthPeriod } from "./monthPeriod";

export function Loaded({
classes = "",
...props
}: EndowmentBalances & { classes?: string }) {
const { id } = useAdminContext();
const period = monthPeriod();
const { showModal } = useModalContext();

const mov = props.movementDetails ?? {
"liq-cash": 0,
"liq-lock": 0,
"lock-cash": 0,
};

const liqDeductions = Object.entries(mov).reduce(
(sum, [k, v]) => (k.startsWith("liq-") ? sum + v : sum),
0
);
const lockDeductions = Object.entries(mov).reduce(
(sum, [k, v]) => (k.startsWith("liq-") ? sum + v : sum),
0
);

const grantFromBal = Object.entries(mov).reduce(
(sum, [k, v]) => (k.endsWith("-cash") ? sum + v : sum),
0
);

return (
<div className={`${classes} mt-6`}>
<h3 className="uppercase mb-4 font-black">Account Balances</h3>
<div className="grid gap-4 @lg:grid-cols-2">
<Figure
title="Savings"
tooltip={
<Content className="bg-navy-d4 text-gray-l4 text-sm max-w-xs p-4 rounded-lg">
Funds held in Fidelity Government Money Market (SPAXX) consisting
of cash, US Government Securities and Repurchase Agreements
<Arrow />
</Content>
}
icon={<Icon size={21} type="PiggyBank" strokeWidth={1.5} />}
amount={`$ ${humanize(props.donationsBal - props.payoutsMade, 2)}`}
actions={
<div className="mt-8 flex justify-end">
<button
type="button"
onClick={() =>
showModal(MoveFundForm, {
type: "liq-cash",
balance: props.donationsBal - liqDeductions,
mov,
endowId: id,
effect: "append",
})
}
className="text-xs uppercase bg-blue-d1 text-white px-2 py-1 rounded-sm font-heading hover:bg-blue"
>
withdraw
</button>
</div>
}
/>
<Figure
title="Investments"
tooltip={
<Content className="bg-navy-d4 text-gray-l4 text-sm max-w-xs p-4 rounded-lg shadow-lg">
<span className="block mb-2">
Funds invested in a diversified portfolio comprising
</span>
<div>
<p>50% - Domestic and international equities</p>
<p>30% - Fixed income</p>
<p>15% - Crypto</p>
<p>5% - Cash</p>
</div>
<Arrow />
</Content>
}
icon={<Icon type="Stocks" size={16} />}
amount={`$ ${humanize(props.sustainabilityFundBal, 2)}`}
/>
<Figure
title="Contributions count"
icon={<Icon type="Users" size={17} />}
amount={props.contributionsCount.toString()}
/>
</div>

<div className="w-full mt-16 h-1.5 bg-gray-l5 rounded-full shadow-inner" />

<h3 className="my-4 font-medium flex items-center">
<span className="text-sm uppercase font-normal">Period</span>
<span className="ml-2 uppercase text-sm">
{period.from} - {period.to}
</span>
<p className="text-sm text-navy-l3 ml-auto">
<span>Ends in </span>
<span className="p-1 px-2 bg-navy-d4 text-gray-l4 text-xs rounded ml-1">
in {period.distance}
</span>
</p>
</h3>

<Movements {...mov} classes="mt-4" />
<Schedule
amount={props.payoutsPending}
periodNext={period.next}
periodRemaining={period.distance}
grantFromBal={grantFromBal}
/>

<div className="w-full mt-16 h-1.5 bg-gray-l5 rounded-full shadow-inner" />
<PayoutHistory endowId={id} classes="mt-2" />
</div>
);
}
86 changes: 86 additions & 0 deletions src/pages/Admin/Charity/Dashboard/MoveFundForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Field, Input, Label } from "@headlessui/react";
import { yupResolver } from "@hookform/resolvers/yup";
import Modal from "components/Modal";
import { useErrorContext } from "contexts/ErrorContext";
import { useModalContext } from "contexts/ModalContext";
import { humanize } from "helpers";
import { useForm } from "react-hook-form";
import { schema, stringNumber } from "schemas/shape";
import { useMoveFundsMutation } from "services/apes";
import type { BalanceMovement } from "types/aws";

interface IMoveFundForm {
effect: "append" | "override";
type: keyof BalanceMovement;
endowId: number;
balance: number;
mov: BalanceMovement;
}

export function MoveFundForm(props: IMoveFundForm) {
const [moveFund, { isLoading }] = useMoveFundsMutation();
const { closeModal } = useModalContext();
const { handleError } = useErrorContext();
type FV = { amount: string };

const {
handleSubmit,
register,
formState: { errors, isSubmitting },
} = useForm<FV>({
defaultValues: { amount: "" },
resolver: yupResolver(
schema<FV>({
amount: stringNumber(
(s) => s.required("required"),
(n) => n.positive().max(props.balance, "can't be more than balance")
),
})
),
});

return (
<Modal
onSubmit={handleSubmit(async (fv) => {
try {
await moveFund({
endowId: props.endowId,
...props.mov,
[props.type]: props.mov[props.type] + +fv.amount,
}).unwrap();
closeModal();
} catch (err) {
handleError(err, { context: "moving funds" });
}
})}
as="form"
className="fixed-center z-10 grid gap-y-4 text-navy-d4 bg-white sm:w-full w-[90vw] sm:max-w-lg rounded-lg p-6"
>
<p className="flex items-center gap-2">
<span className="text-navy-l1 text-sm">Available balance</span>
<span className="font-semibold font-heading">
$ {humanize(props.balance)}
</span>
</p>
<Field className="grid">
<Label className="font-semibold mb-1">
Amount <span className="text-red">*</span>
</Label>
<Input
placeholder="e.g. $ 100"
{...register("amount")}
className="px-4 py-3 rounded-lg outline-blue-d1 border border-gray-l3"
/>
<span className="text-red text-xs text-right empty:hidden mt-1">
{errors.amount?.message}
</span>
</Field>
<button
disabled={isSubmitting || isLoading}
className="bg-blue-d1 hover:bg-blue disabled:bg-gray text-white rounded-full px-4 py-2 font-heading uppercase font-bold"
>
{isLoading ? "Submitting..." : "Submit"}
</button>
</Modal>
);
}
Loading

0 comments on commit 2ae2fe7

Please sign in to comment.