Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fb fundraisers #3272

Draft
wants to merge 47 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
65d65b5
Disable tracking in `/donate-widget` (#3163)
ap-justin Jul 19, 2024
dd17977
Auth redirect (#3165)
ap-justin Jul 23, 2024
c7b094d
Email bugs (#3184)
ap-justin Jul 25, 2024
47d7f73
Chariot prod key requirements (#3198)
ap-justin Aug 3, 2024
46a4d3f
BG-1511: Fix Oauth stuck on /auth-redirector (#3204)
ap-justin Aug 5, 2024
b7d5944
Cherry fixes (#3219)
ap-justin Aug 8, 2024
82a4bab
Auth redirect (#3165)
ap-justin Jul 23, 2024
7625167
Fund opt in setting (#3182)
ap-justin Jul 25, 2024
bba8426
Create fund UI Iteration 1 (#3185)
ap-justin Jul 31, 2024
b153616
List funds (#3192)
ap-justin Jul 31, 2024
581fd30
Add fundraise target (#3194)
ap-justin Jul 31, 2024
f4cc54b
Rc v2.5 update (#3220)
ap-justin Aug 8, 2024
e6589d5
Fund editor, fund profile (#3196)
ap-justin Aug 12, 2024
8ff9dc5
Chariot prod key requirements (#3198)
ap-justin Aug 3, 2024
e0bd7f7
lint fix
ap-justin Aug 12, 2024
43fdf86
revert linkedin
ap-justin Sep 2, 2024
0095b69
update icons
ap-justin Sep 4, 2024
661870c
Disable tracking in `/donate-widget` (#3163)
ap-justin Jul 19, 2024
5586630
Auth redirect (#3165)
ap-justin Jul 23, 2024
da155af
Email bugs (#3184)
ap-justin Jul 25, 2024
58461c9
Chariot prod key requirements (#3198)
ap-justin Aug 3, 2024
ee036f2
BG-1511: Fix Oauth stuck on /auth-redirector (#3204)
ap-justin Aug 5, 2024
63feb76
Cherry fixes (#3219)
ap-justin Aug 8, 2024
2d9d2af
Auth redirect (#3165)
ap-justin Jul 23, 2024
3461cc8
Create fund UI Iteration 1 (#3185)
ap-justin Jul 31, 2024
2353509
List funds (#3192)
ap-justin Jul 31, 2024
dbc72d1
Add fundraise target (#3194)
ap-justin Jul 31, 2024
af9104b
Rc v2.5 update (#3220)
ap-justin Aug 8, 2024
66eea3d
Fund editor, fund profile (#3196)
ap-justin Aug 12, 2024
90a9a33
Chariot prod key requirements (#3198)
ap-justin Aug 3, 2024
738233c
Create fund UI Iteration 1 (#3185)
ap-justin Jul 31, 2024
c82f480
Fund editor, fund profile (#3196)
ap-justin Aug 12, 2024
7e20ad1
npo funds page
ap-justin Aug 12, 2024
04b6dee
expiration in active status
ap-justin Aug 12, 2024
119c06a
remove extra index.ts file
ap-justin Sep 2, 2024
f806bef
linkedin tracking
ap-justin Sep 2, 2024
8293068
remove duplicate
ap-justin Sep 14, 2024
8d6e728
updated
ap-justin Oct 3, 2024
f058e6c
convert to valibot
ap-justin Oct 4, 2024
69cfe98
fix file and date schema
ap-justin Oct 5, 2024
e734896
use fundraiser lib
ap-justin Oct 5, 2024
fa9084a
params input str
ap-justin Oct 5, 2024
abba679
dummy file obj
ap-justin Oct 5, 2024
9f2f6e2
endow actions
ap-justin Oct 6, 2024
2d381cf
npo actions
ap-justin Oct 6, 2024
f8f7b7d
approval prompts
ap-justin Oct 6, 2024
9a17cfe
use subgrid
ap-justin Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
},
"dependencies": {
"@better-giving/assets": "1.0.18",
"@better-giving/fundraiser": "1.0.0-rc.7",
"@better-giving/registration": "1.0.24",
"@better-giving/types": "1.0.0-rc.2",
"@gsap/react": "2.1.1",
"@headlessui/react": "2.1.0",
"@hookform/error-message": "2.0.1",
Expand Down
2 changes: 2 additions & 0 deletions src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useScrollTop from "hooks/useScrollTop";
import NProgress from "nprogress";
import { adminRoute } from "pages/Admin";
import { routes as blogRoutes } from "pages/Blog";
import { fundsRoute } from "pages/Funds";
import { legalRoutes } from "pages/Legal";
import OAuthRedirector from "pages/OAuthRedirector";
import { profileRoute } from "pages/Profile";
Expand Down Expand Up @@ -38,6 +39,7 @@ const widgetRoutes: RO[] = [
const _appRoutes: RO[] = [
adminRoute,
regRoute,
fundsRoute,
userDashboardRoute,
...blogRoutes,
...legalRoutes,
Expand Down
13 changes: 5 additions & 8 deletions src/App/Header/UserMenu/EndowmentLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,25 @@ export function BookmarkLink({ endowId }: IBookmarkLink) {
error: <_Link id={endowId} route="profile" />,
}}
>
{(endow) => <_Link {...endow} id={endowId} route="profile" />}
{(endow) => <_Link {...endow} id={endowId} route={appRoutes.profile} />}
</QueryLoader>
);
}

export function EndowmentLink({ endowID, logo, name }: UserEndow) {
return <_Link id={endowID} logo={logo} name={name} route="admin" />;
return <_Link id={endowID} logo={logo} name={name} route={appRoutes.admin} />;
}

type LinkProps = {
id: number;
id: number | string;
name?: string;
logo?: string;
route: "admin" | "profile";
route: string;
};
const _Link = (props: LinkProps) => (
<MenuItem
as={Link}
to={
(props.route === "admin" ? appRoutes.admin : appRoutes.marketplace) +
`/${props.id}`
}
to={props.route + `/${props.id}`}
className="hover:text-blue-d1 text-sm flex items-center gap-2"
>
<Image src={props.logo} className="object-cover h-[20px] w-[20px]" />
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
Search,
Settings,
Shield,
Split,
Sprout,
SquareArrowOutUpRight,
Star,
Expand Down Expand Up @@ -130,6 +131,7 @@ export const icons = {
Save: Save,
Search,
SecurityScan: Shield,
Split,
Sprout,
Star,
StickyNote,
Expand Down
2 changes: 2 additions & 0 deletions src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum appRoutes {
nonprofit_info = "/nonprofit",
donor_info = "/donor",
wp_plugin = "/wp-plugin",
funds = "/funds",
}

export const adminRoutes = {
Expand All @@ -38,6 +39,7 @@ export const adminRoutes = {
settings: "settings",
members: "members",
media: "media",
funds: "funds",
} as const;

export enum donateWidgetRoutes {
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/uploadFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { version as v } from "services/helpers";
import { isEmpty } from "./isEmpty";
import { jwtToken } from "./jwt-token";

export type Bucket = "endow-profiles" | "endow-reg" | "bg-user";
export type Bucket = "endow-profiles" | "endow-reg" | "bg-user" | "bg-funds";
export const bucketURL = "s3.amazonaws.com";

const SPACES = /\s+/g;
Expand Down
124 changes: 124 additions & 0 deletions src/pages/Admin/Charity/Funds/FundItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import type { FundItem as TFundItem } from "@better-giving/fundraiser";
import Icon from "components/Icon";
import Prompt from "components/Prompt";
import { appRoutes } from "constants/routes";
import { useAuthenticatedUser } from "contexts/Auth";
import { useErrorContext } from "contexts/ErrorContext";
import { useModalContext } from "contexts/ModalContext";
import { Link } from "react-router-dom";
import {
useApproveMutation,
useOptOutMutation,
} from "services/aws/endow-funds";

export const FundItem = (props: TFundItem & { endowId: number }) => {
const user = useAuthenticatedUser();
const isActive = new Date().toISOString() <= props.expiration && props.active;
const isEditor = user.funds.includes(props.id);
const [optOut, { isLoading: isOptingOut }] = useOptOutMutation();
const [approve, { isLoading: isApproving }] = useApproveMutation();
const { showModal } = useModalContext();
const { handleError } = useErrorContext();

const isApproved = props.approvers.includes(props.endowId);

return (
<div className="grid grid-cols-subgrid col-span-6 items-center rounded border border-gray-l4">
<img
src={props.logo}
className="col-start-1 row-span-2 object-cover w-28 aspect-square border-r border-gray-l4"
/>
<Link
to={`${appRoutes.funds}/${props.id}`}
className="mr-4 pl-4 p-1.5 font-medium text-navy-l1 hover:text-blue-d1"
>
<span className="font-heading">{props.name}</span>

<span
className={`ml-1 relative bottom-px uppercase text-center text-2xs rounded-full px-3 py-0.5 ${
isActive ? "text-green bg-green-l4" : "text-red bg-red-l4"
}`}
>
{isActive ? "active" : "closed"}
</span>
</Link>

<div className="grid grid-cols-subgrid col-start-2 col-span-5 border-t border-gray-l4 gap-x-6 p-3 items-center">
<Link
aria-disabled={!isActive || !isEditor}
className={`flex items-center gap-x-1 text-sm hover:text-blue-d1 text-blue aria-disabled:pointer-events-none aria-disabled:text-gray ${
isEditor ? "" : "invisible"
}`}
to={`${appRoutes.funds}/${props.id}/edit`}
>
<Icon type="ArrowRight" size={16} />
<span>Edit</span>
</Link>
{/** fund item won't show once NPO opted out of it: so no need to hide this button */}
<button
className="font-heading bg-amber enabled:hover:bg-amber-d1 text-white rounded-full px-4 py-2 text-xs flex items-center gap-1 disabled:bg-gray-l1"
disabled={isOptingOut}
type="button"
onClick={async () => {
console.log(props.id);
try {
await optOut({
fundId: props.id,
endowId: props.endowId,
}).unwrap();
showModal(Prompt, {
type: "success",
children:
"You have successfully opted out of this fund. Changes will take effect shortly.",
});
} catch (err) {
handleError(err, { context: "opting out of fund" });
}
}}
>
<Icon
type={isOptingOut ? "Loading" : "Split"}
size={12}
className={isOptingOut ? "animate-spin" : "rotate-90"}
/>
<span>{isOptingOut ? "Opting out.." : "Opt out"}</span>
</button>
{!isApproved ? (
<button
className="font-heading bg-blue-d1 text-white rounded-full px-4 py-2 text-xs flex items-center gap-1 disabled:bg-gray-l1"
disabled={isApproving}
type="button"
onClick={async () => {
console.log(props.id);
try {
await approve({
fundId: props.id,
endowId: props.endowId,
}).unwrap();
showModal(Prompt, {
type: "success",
children:
"You have successfully approved this fund. Changes will take effect shortly.",
});
} catch (err) {
handleError(err, { context: "approving fund" });
}
}}
>
<Icon
type={isApproving ? "Loading" : "Verified"}
size={16}
className={isApproving ? "animate-spin" : ""}
/>
<span>{isApproving ? "Approving.." : "Approve"}</span>
</button>
) : (
<div className="flex items-center gap-1 text-green">
<Icon type="Verified" className="size-4" />
<p className="text-xs">Approved</p>
</div>
)}
</div>
</div>
);
};
50 changes: 50 additions & 0 deletions src/pages/Admin/Charity/Funds/Funds.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import ContentLoader from "components/ContentLoader";
import QueryLoader from "components/QueryLoader";
import { useFundsEndowMemberOfQuery } from "services/aws/endow-funds";
import { useAdminContext } from "../../Context";
import { FundItem } from "./FundItem";

export function Funds() {
const { id } = useAdminContext();
const query = useFundsEndowMemberOfQuery({ endowId: id });
return (
<div className="grid gap-y-4 grid-cols-[auto_1fr_auto_auto_auto_auto] justify-items-start">
<h3 className="text-3xl mb-2 col-span-full">My Fundraisers</h3>

<QueryLoader
messages={{
loading: (
<>
<Skeleton />
<Skeleton />
<Skeleton />
</>
),
error: "Failed to get fundraisers",
empty: "No fundraisers found.",
}}
queryState={query}
>
{(funds) => (
<>
{funds.map((fund) => (
<FundItem key={fund.id} {...fund} endowId={id} />
))}
</>
)}
</QueryLoader>
</div>
);
}

export function Skeleton() {
return (
<div
className="flex items-center gap-x-2 w-full col-span-full"
aria-disabled={true}
>
<ContentLoader className="size-10 rounded-full" />
<ContentLoader className="w-full h-10 rounded" />
</div>
);
}
1 change: 1 addition & 0 deletions src/pages/Admin/Charity/Funds/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Funds as Component } from "./Funds";
4 changes: 3 additions & 1 deletion src/pages/Admin/Charity/Settings/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function Form(props: Props) {
hide_bg_tip: props.hide_bg_tip ?? false,
programDonateDisabled: !(props.progDonationsAllowed ?? true),
donateMethods: fill(props.donateMethods),
fundOptIn: props.fund_opt_in ?? false,
},
});

Expand All @@ -63,7 +64,7 @@ export default function Form(props: Props) {
reset();
}}
onSubmit={handleSubmit(
async ({ programDonateDisabled, donateMethods, ...fv }) => {
async ({ programDonateDisabled, donateMethods, fundOptIn, ...fv }) => {
if (props.id === BG_ID && fv.hide_bg_tip === false) {
return displayError(
"BG donation flow should not show BG tip screen"
Expand All @@ -72,6 +73,7 @@ export default function Form(props: Props) {

await updateEndow({
...fv,
fund_opt_in: fundOptIn,
progDonationsAllowed: !programDonateDisabled,
id: props.id,
donateMethods: donateMethods
Expand Down
21 changes: 11 additions & 10 deletions src/pages/Admin/Charity/Settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { FormError, FormSkeleton } from "components/admin";
import { useEndowment } from "services/aws/useEndowment";
import type { EndowmentSettingsAttributes } from "types/aws";
import { useAdminContext } from "../../Context";
import Form from "./Form";

type K = EndowmentSettingsAttributes;
const fields = Object.keys({
receiptMsg: "",
hide_bg_tip: "",
progDonationsAllowed: "",
donateMethods: "",
fund_opt_in: "",
} satisfies { [k in K]: "" }) as K[];

export default function Settings() {
const { id } = useAdminContext();
const {
data: endow,
isLoading,
isError,
} = useEndowment({ id }, [
"receiptMsg",
"hide_bg_tip",
"progDonationsAllowed",
"donateMethods",
]);
const { data: endow, isLoading, isError } = useEndowment({ id }, fields);

if (isLoading) {
return <FormSkeleton classes="max-w-4xl justify-self-center mt-6" />;
Expand Down
1 change: 1 addition & 0 deletions src/pages/Admin/Charity/Settings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { TDonateMethod } from "types/components";
export type FV = {
receiptMsg: string;
hide_bg_tip: boolean;
fundOptIn: boolean;
programDonateDisabled: boolean;
donateMethods: TDonateMethod[];
};
1 change: 1 addition & 0 deletions src/pages/Admin/Charity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const charityRoute: RouteObject = {
],
},
{ path: adminRoutes.widget_config, element: <EndowWidget /> },
{ path: adminRoutes.funds, lazy: () => import("./Funds") },
{ index: true, lazy: () => import("./Dashboard") },
...mediaRoutes,
],
Expand Down
8 changes: 8 additions & 0 deletions src/pages/Admin/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ const linkGroup3: LinkGroup = {
size: 25,
},
},
{
title: "Fundraisers",
to: sidebarRoutes.funds,
icon: {
type: "Heart",
size: 21,
},
},
],
};

Expand Down
Loading