Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { noAuthFetcher } from "@/lib/fetcher";
import Decimal from "decimal.js";
import Image from "next/image";

export function CartForm({ code }: { code: string }) {
export function CartForm({ referrer }: { referrer: string }) {
const router = useRouter();
const params = useParams();
const fundraiserId = params.id as string;
Expand Down Expand Up @@ -59,7 +59,9 @@ export function CartForm({ code }: { code: string }) {
const removals: string[] = [];

cart.forEach((cartItem) => {
const availabilityItem = items.find((item) => item.id === cartItem.item.id);
const availabilityItem = items.find(
(item) => item.id === cartItem.item.id,
);

if (!availabilityItem || availabilityItem.offsale) {
removeItem(fundraiserId, cartItem.item);
Expand Down Expand Up @@ -117,13 +119,17 @@ export function CartForm({ code }: { code: string }) {
const handleIncrement = (item: typeof CompleteItemSchema._type) => {
const cartItem = cartWithImages.find((ci) => ci.item.id === item.id);
if (cartItem) {
const availabilityItem = items?.find((availability) => availability.id === item.id);
const availabilityItem = items?.find(
(availability) => availability.id === item.id,
);
if (
availabilityItem?.available !== null &&
availabilityItem?.available !== undefined &&
cartItem.quantity + 1 > availabilityItem.available
) {
toast.error(`Only ${availabilityItem.available} available for ${item.name}`);
toast.error(
`Only ${availabilityItem.available} available for ${item.name}`,
);
return;
}
updateQuantity(fundraiserId, item, cartItem.quantity + 1);
Expand All @@ -142,8 +148,8 @@ export function CartForm({ code }: { code: string }) {
};

const handleCheckout = async () => {
const nextCheckoutPath = code
? `/buyer/fundraiser/${fundraiserId}/checkout?code=${code}`
const nextCheckoutPath = referrer
? `/buyer/fundraiser/${fundraiserId}/checkout?referrer=${referrer}`
: `/buyer/fundraiser/${fundraiserId}/checkout`;

// Check if user is already authenticated
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/app/buyer/fundraiser/[id]/cart/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export default async function CartPage({
searchParams,
}: {
params: Promise<{ id: string }>;
searchParams: Promise<{ code?: string }>;
searchParams: Promise<{ referrer?: string }>;
}) {
await connection();

const supabase = await createClient();
const id = (await params).id;
const { code } = await searchParams;
const { referrer } = await searchParams;

const nextPath =
typeof code === "string" && code.length > 0
? `/buyer/fundraiser/${id}/cart?code=${encodeURIComponent(code)}`
typeof referrer === "string" && referrer.length > 0
? `/buyer/fundraiser/${id}/cart?referrer=${encodeURIComponent(referrer)}`
: `/buyer/fundraiser/${id}/cart`;

// protect page (must use supabase.auth.getUser() according to docs)
Expand All @@ -40,7 +40,7 @@ export default async function CartPage({

return (
<div className=" md:overflow-y-clip">
<CartForm code={code ? code : ""} />
<CartForm referrer={referrer ? referrer : ""} />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,19 @@ import {
} from "@/components/ui/sheet";
import Image from "next/image";
import { ReferrersModal } from "./ReferrersModal";
import {
formatCapacityIssueMessage,
getCapacityIssues,
} from "@/lib/capacity";
import { formatCapacityIssueMessage, getCapacityIssues } from "@/lib/capacity";
import { cn } from "@/lib/utils";

export function CheckoutForm({
token,
fundraiser,
userProfile,
code,
referrer,
}: {
token: string;
fundraiser: z.infer<typeof CompleteFundraiserSchema>;
userProfile: z.infer<typeof UserSchema>;
code: string;
referrer: string;
}) {
const router = useRouter();
const isMobile = useIsMobile();
Expand All @@ -82,12 +80,11 @@ export function CheckoutForm({
isLoading: isAvailabilityLoading,
mutate: refreshAvailability,
} = useItemsAvailability(fundraiser.id);
const initialReferralId = fundraiser.referrals.some((r) => r.id === code)
? code
const initialReferralId = fundraiser.referrals.some((r) => r.id === referrer)
? referrer
: "none";
const [selectedReferralId, setSelectedReferralId] = useState<string>(
initialReferralId,
);
const [selectedReferralId, setSelectedReferralId] =
useState<string>(initialReferralId);
const [paymentMethod, setPaymentMethod] = useState<"VENMO" | "OTHER">(
"VENMO",
);
Expand All @@ -113,8 +110,9 @@ export function CheckoutForm({
const selectedReferralName =
selectedReferralId && selectedReferralId !== "none"
? fundraiser.referrals.find((r) => r.id === selectedReferralId)?.referrer
.name || "No Referral"
: "No Referral";
.name || "Refer a club member"
: "Refer a club member";
const selectedReferral = selectedReferralName != "Refer a club member";

const orderTotal = cartWithImages
.reduce(
Expand Down Expand Up @@ -208,7 +206,9 @@ export function CheckoutForm({
availabilityItem?.available !== undefined &&
newQuantity > availabilityItem.available
) {
toast.error(`Only ${availabilityItem.available} available for ${item.name}`);
toast.error(
`Only ${availabilityItem.available} available for ${item.name}`,
);
return;
}
}
Expand Down Expand Up @@ -285,7 +285,12 @@ export function CheckoutForm({
{/* Referral */}
<button
type="button"
className="flex items-center justify-between py-1 w-full"
className={cn(
"flex items-center justify-between w-full rounded-xl py-3 transition-all",
!selectedReferral
? "px-4 bg-green-50 border border-green-200 shadow-sm hover:bg-green-100"
: "bg-white",
)}
onClick={() => {
setPendingReferralId(null);
setReferralSearch("");
Expand All @@ -294,16 +299,30 @@ export function CheckoutForm({
aria-label={`Select referral. Currently: ${selectedReferralName}`}
>
<div className="flex gap-3 items-center text-left">
<User className="h-5 w-5 text-black" aria-hidden="true" />
<p className="text-base leading-6">
{selectedReferralName != "No Referral" && (
<span>Referrer: </span>
<User
className={cn(
"h-5 w-5",
!selectedReferral ? "text-green-600" : "text-black",
)}
aria-hidden="true"
/>
<p
className={cn(
"leading-6",
!selectedReferral ? "text-green-600" : "text-base",
)}
>
{selectedReferral && <span>Referrer: </span>}
{selectedReferralName}
</p>
</div>
{approvedReferrals.length > 0 && (
<ChevronRight className="h-5 w-5 text-black" />
<ChevronRight
className={cn(
"h-5 w-5",
!selectedReferral ? "text-green-600" : "text-black",
)}
/>
)}
</button>
</div>
Expand Down Expand Up @@ -735,7 +754,12 @@ export function CheckoutForm({
{/* Referral Button */}
<button
type="button"
className="flex items-center justify-between w-full mt-2"
className={cn(
"flex items-center justify-between w-full rounded-xl py-3 transition-all",
!selectedReferral
? "px-4 bg-green-50 border border-green-200 shadow-sm hover:bg-green-100"
: "bg-white",
)}
onClick={() => {
setPendingReferralId(null);
setReferralSearch("");
Expand All @@ -745,18 +769,35 @@ export function CheckoutForm({
>
<div className="flex gap-3 items-center text-left">
<User
className="h-5 w-5 text-black"
className={cn(
"h-5 w-5",
!selectedReferral
? "text-green-600"
: "text-black",
)}
aria-hidden="true"
/>
<p className="text-base leading-6">
{selectedReferralName != "No Referral" && (
<span>Referrer: </span>
<p
className={cn(
"leading-6",
!selectedReferral
? "text-green-600"
: "text-base",
)}
>
{selectedReferral && <span>Referrer: </span>}
{selectedReferralName}
</p>
</div>
{approvedReferrals.length > 0 && (
<ChevronRight className="h-5 w-5 text-black" />
<ChevronRight
className={cn(
"h-5 w-5",
!selectedReferral
? "text-green-600"
: "text-black",
)}
/>
)}
</button>

Expand Down
10 changes: 5 additions & 5 deletions frontend/src/app/buyer/fundraiser/[id]/checkout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ export default async function CheckoutPage({
searchParams,
}: {
params: Promise<{ id: string }>;
searchParams: Promise<{ code?: string }>;
searchParams: Promise<{ referrer?: string }>;
}) {
await connection();

const supabase = await createClient();

const id = (await params).id;
const { code } = await searchParams;
const { referrer } = await searchParams;

const nextPath =
typeof code === "string" && code.length > 0
? `/buyer/fundraiser/${id}/checkout?code=${encodeURIComponent(code)}`
typeof referrer === "string" && referrer.length > 0
? `/buyer/fundraiser/${id}/checkout?referrer=${encodeURIComponent(referrer)}`
: `/buyer/fundraiser/${id}/checkout`;

// protect page (must use supabase.auth.getUser() according to docs)
Expand Down Expand Up @@ -60,7 +60,7 @@ export default async function CheckoutPage({
fundraiser={fundraiser}
token={session.access_token}
userProfile={userProfile}
code={code ? code : ""}
referrer={referrer ? referrer : ""}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ export function FundraiserCartSidebar({
// will auto-start Google sign-in and return to the checkout page
const nextPath = isMobile
? referralId
? `/buyer/fundraiser/${fundraiserId}/cart?code=${referralId}`
? `/buyer/fundraiser/${fundraiserId}/cart?referrer=${referralId}`
: `/buyer/fundraiser/${fundraiserId}/cart`
: referralId
? `/buyer/fundraiser/${fundraiserId}/checkout?code=${referralId}`
? `/buyer/fundraiser/${fundraiserId}/checkout?referrer=${referralId}`
: `/buyer/fundraiser/${fundraiserId}/checkout`;

router.push(`/login?next=${encodeURIComponent(nextPath)}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ import { toast } from "sonner";
export function FundraiserItemsPanel({
isPast,
fundraiserId,
fundraiserName,
items,
}: {
isPast: boolean;
fundraiserId: string;
fundraiserName: string;
items: z.infer<typeof ItemWithAvailabilitySchema>[];
}) {
const { addItem, removeItem, updateQuantity } = useCartStore();
// fixes nextjs hydration issue: https://github.com/pmndrs/zustand/issues/938#issuecomment-1481801942
const cart = useStore(useCartStore, (state) => state.carts[fundraiserId]);

const handleIncrement = (item: z.infer<typeof ItemWithAvailabilitySchema>) => {
const handleIncrement = (
item: z.infer<typeof ItemWithAvailabilitySchema>,
) => {
const cartItem = cart?.find((cartItem) => cartItem.item.id === item.id);
const currentQty = cartItem?.quantity ?? 0;

Expand All @@ -34,11 +38,13 @@ export function FundraiserItemsPanel({
if (cartItem) {
updateQuantity(fundraiserId, item, currentQty + 1);
} else {
addItem(fundraiserId, item, 1);
addItem(fundraiserId, item, 1, fundraiserName);
}
};

const handleDecrement = (item: z.infer<typeof ItemWithAvailabilitySchema>) => {
const handleDecrement = (
item: z.infer<typeof ItemWithAvailabilitySchema>,
) => {
const cartItem = cart?.find((cartItem) => cartItem.item.id === item.id);
if (cartItem) {
if (cartItem.quantity > 1) {
Expand All @@ -49,7 +55,10 @@ export function FundraiserItemsPanel({
}
};

const handleSetQuantity = (item: z.infer<typeof ItemWithAvailabilitySchema>, quantity: number) => {
const handleSetQuantity = (
item: z.infer<typeof ItemWithAvailabilitySchema>,
quantity: number,
) => {
if (quantity <= 0) {
removeItem(fundraiserId, item);
return;
Expand All @@ -64,7 +73,7 @@ export function FundraiserItemsPanel({
if (cartItem) {
updateQuantity(fundraiserId, item, quantity);
} else {
addItem(fundraiserId, item, quantity);
addItem(fundraiserId, item, quantity, fundraiserName);
}
};

Expand All @@ -78,8 +87,8 @@ export function FundraiserItemsPanel({
<div className="grid grid-cols-2 gap-6">
{items.map((item) => {
const amount =
cart?.find((cartItem) => cartItem.item.id === item.id)?.quantity ||
0;
cart?.find((cartItem) => cartItem.item.id === item.id)
?.quantity || 0;
const isOutOfStock = item.available !== null && item.available <= 0;

return (
Expand All @@ -91,7 +100,9 @@ export function FundraiserItemsPanel({
amount={amount}
increment={() => handleIncrement(item)}
decrement={() => handleDecrement(item)}
setCartQuantity={(quantity) => handleSetQuantity(item, quantity)}
setCartQuantity={(quantity) =>
handleSetQuantity(item, quantity)
}
available={item.available}
isOutOfStock={isOutOfStock}
isPast={isPast}
Expand Down
Loading