Skip to content
Merged
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
5 changes: 4 additions & 1 deletion app/grant/[id]/[slug]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { GrantTabProvider } from '@/components/Funding/GrantPageContent';
import { WorkHeaderGrant } from '@/components/work/WorkHeader/index';
import { RegisteredReportRouteTrackerLoader } from '@/components/work/RegisteredReportRouteTrackerLoader';
import { SearchHistoryTracker } from '@/components/work/SearchHistoryTracker';
import { getGrantBadgeAmount } from '@/types/grant';

interface Props {
params: Promise<{
Expand Down Expand Up @@ -61,6 +62,7 @@ export default async function GrantSlugLayout({ params, children }: Props) {
const isPending = grant?.status === 'PENDING';
const isActive =
grant?.status === 'OPEN' && (grant?.endDate ? isDeadlineInFuture(grant.endDate) : true);
const badgeAmountUsd = grant ? getGrantBadgeAmount(grant).usd : undefined;

const metadata = await MetadataService.get(work.unifiedDocumentId?.toString() || '');

Expand All @@ -72,12 +74,13 @@ export default async function GrantSlugLayout({ params, children }: Props) {
<WorkHeaderGrant
work={work}
metadata={metadata}
amountUsd={grant?.amount?.usd}
amountUsd={badgeAmountUsd}
grantId={grantId?.toString()}
isActive={isActive}
isPending={isPending}
organization={grant?.organization}
applicationVisibility={grant?.applicationVisibility}
fundingPool={grant?.fundingPool ?? null}
preTitle={
<RegisteredReportRouteTrackerLoader
currentStage="grant"
Expand Down
6 changes: 3 additions & 3 deletions components/Feed/items/FeedItemGrantWithApplicants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useCurrencyPreference } from '@/contexts/CurrencyPreferenceContext';
import { useExchangeRate } from '@/contexts/ExchangeRateContext';
import { formatCurrency } from '@/utils/currency';
import { Application } from '@/types/funding';
import { getGrantBadgeAmount } from '@/types/grant';

interface FeedItemGrantWithApplicantsProps {
entry: FeedEntry;
Expand Down Expand Up @@ -149,9 +150,8 @@ export const FeedItemGrantWithApplicants: FC<FeedItemGrantWithApplicantsProps> =
slug: content.slug,
});

const budgetAmount = showUSD
? Math.round(grant.amount?.usd || 0)
: Math.round(grant.amount?.rsc || 0);
const badgeAmount = getGrantBadgeAmount(grant);
const budgetAmount = showUSD ? Math.round(badgeAmount.usd) : Math.round(badgeAmount.rsc);

const allProposals = grant.applicants?.filter((a) => a.fundraise) ?? [];
const shown = expanded ? allProposals : allProposals.slice(0, VISIBLE_PROPOSALS);
Expand Down
5 changes: 2 additions & 3 deletions components/Fund/FundraiseProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { FC, useState } from 'react';
import { Progress } from '@/components/ui/Progress';
import { ContributorsButton } from '@/components/ui/ContributorsButton';
import { Clock } from 'lucide-react';
import { Clock, Coins } from 'lucide-react';
import { formatDeadline, formatExactTime } from '@/utils/date';
import { isFundraiseActive } from '@/components/Fund/lib/fundraiseUtils';
import type { Fundraise } from '@/types/funding';
Expand All @@ -12,7 +12,6 @@ import { Button } from '@/components/ui/Button';
import { cn } from '@/utils/styles';
import { CurrencyBadge } from '@/components/ui/CurrencyBadge';
import { ContributeToFundraiseModal } from '@/components/modals/ContributeToFundraiseModal';
import { Icon } from '../ui/icons';
import { useCurrencyPreference } from '@/contexts/CurrencyPreferenceContext';
import { useShareModalContext } from '@/contexts/ShareContext';
import { useRouter } from 'next/navigation';
Expand Down Expand Up @@ -257,7 +256,7 @@ export const FundraiseProgress: FC<FundraiseProgressProps> = ({
)}
onClick={handleContributeClick}
>
<Icon name="giveRSC" size={18} color="white" />
<Coins className="w-[18px] h-[18px]" />
Fund proposal
</Button>
) : onDetailsClick ? (
Expand Down
5 changes: 2 additions & 3 deletions components/Fund/FundraiseProgressV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FC, useState } from 'react';
import { Progress } from '@/components/ui/Progress';
import { AvatarStack } from '@/components/ui/AvatarStack';
import { ContributorsButton } from '@/components/ui/ContributorsButton';
import { Clock } from 'lucide-react';
import { Clock, Coins } from 'lucide-react';
import { formatDeadline, formatExactTime, formatDate } from '@/utils/date';
import { isFundraiseActive } from '@/components/Fund/lib/fundraiseUtils';
import type { Fundraise } from '@/types/funding';
Expand All @@ -13,7 +13,6 @@ import { Button } from '@/components/ui/Button';
import { cn } from '@/utils/styles';
import { CurrencyBadge } from '@/components/ui/CurrencyBadge';
import { ContributeToFundraiseModal } from '@/components/modals/ContributeToFundraiseModal';
import { Icon } from '../ui/icons';
import { useCurrencyPreference } from '@/contexts/CurrencyPreferenceContext';
import { useShareModalContext } from '@/contexts/ShareContext';
import { useRouter } from 'next/navigation';
Expand Down Expand Up @@ -347,7 +346,7 @@ export const FundraiseProgress: FC<FundraiseProgressProps> = ({
className="flex items-center gap-1.5 bg-orange-500 hover:bg-orange-600 text-white font-semibold transition-all duration-200 border-0"
onClick={handleContributeClick}
>
<Icon name="giveRSC" size={20} color="white" />
<Coins className="w-5 h-5" />
Fund this research
</Button>
) : onDetailsClick ? (
Expand Down
3 changes: 2 additions & 1 deletion components/Funding/GrantCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FC } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { FeedEntry, FeedGrantContent } from '@/types/feed';
import { getGrantBadgeAmount } from '@/types/grant';
import { cn } from '@/utils/styles';
import { buildWorkUrl } from '@/utils/url';
import { Users } from 'lucide-react';
Expand Down Expand Up @@ -31,7 +32,7 @@ export const GrantCard: FC<GrantCardProps> = ({ entry, className }) => {

const isClosed = grant.status === 'CLOSED';
const applicantCount = grant.applicants?.length ?? 0;
const amount = grant.amount?.usd ?? 0;
const amount = getGrantBadgeAmount(grant).usd;

return (
<Link
Expand Down
6 changes: 4 additions & 2 deletions components/Funding/GrantCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { FC, useRef, useState, useEffect } from 'react';
import Link from 'next/link';
import { FeedEntry, FeedGrantContent } from '@/types/feed';
import { getGrantBadgeAmount } from '@/types/grant';
import { Carousel } from '@/components/ui/Carousel';
import { FundingProposalCard } from './FundingProposalCard';
import { ProposalCardSkeleton } from '@/components/skeletons/ProposalCardSkeleton';
Expand Down Expand Up @@ -80,6 +81,7 @@ export const GrantCarousel: FC<GrantCarouselProps> = ({

const hasFetchedProposals = hasBeenVisible && (entries.length > 0 || !isLoading);
const showSkeleton = !hasFetchedProposals;
const badgeUsd = getGrantBadgeAmount(grantData).usd;

return (
<section ref={sectionRef} className={cn('py-5', className)}>
Expand All @@ -90,7 +92,7 @@ export const GrantCarousel: FC<GrantCarouselProps> = ({
{getShortTitle(content.grant.shortTitle, content.title)}
</h2>
</Link>
{grantData.amount?.usd && (
{badgeUsd > 0 && (
<span
className={cn(
'inline-flex items-center px-2 py-0.5 rounded-md text-xs font-bold font-mono',
Expand All @@ -99,7 +101,7 @@ export const GrantCarousel: FC<GrantCarouselProps> = ({
: 'bg-green-50 border border-green-200 text-green-700'
)}
>
{formatCompactUSD(grantData.amount.usd)} pool
{formatCompactUSD(badgeUsd)} pool
</span>
)}
</div>
Expand Down
18 changes: 9 additions & 9 deletions components/Funding/PaymentRequestButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ import { PaymentRequestButtonElement, useStripe } from '@stripe/react-stripe-js'
import type { PaymentRequest, PaymentRequestPaymentMethodEvent } from '@stripe/stripe-js';
import { Button } from '@/components/ui/Button';
import { StripeProvider } from './StripeProvider';
import { PaymentService } from '@/services/payment.service';
import { ID } from '@/types/root';
import { PaymentService, type PaymentIntentTarget } from '@/services/payment.service';

interface PaymentRequestButtonProps {
/** Amount in cents */
amountCents: number;
/** Amount in RSC for creating payment intent */
amountInRsc: number;
/** Fundraise ID for the contribution */
fundraiseId: ID;
/** Fundraise or funding pool target for the contribution */
paymentTarget: PaymentIntentTarget;
/** Label shown in the payment sheet */
label?: string;
/** Button text to show when payment method is not available */
Expand All @@ -34,7 +33,7 @@ interface PaymentRequestButtonProps {
function PaymentRequestButtonInner({
amountCents,
amountInRsc,
fundraiseId,
paymentTarget,
label = 'Fund Research',
unavailableText = 'Not available on this device',
onSuccess,
Expand Down Expand Up @@ -104,9 +103,10 @@ function PaymentRequestButtonInner({

try {
// Create payment intent on our backend
const { clientSecret } = await PaymentService.createPaymentIntent(amountInRsc, {
fundraiseId,
});
const { clientSecret } = await PaymentService.createPaymentIntent(
amountInRsc,
paymentTarget
);

// Confirm the payment with the payment method from Apple Pay/Google Pay
const { error: confirmError, paymentIntent } = await stripe!.confirmCardPayment(
Expand Down Expand Up @@ -149,7 +149,7 @@ function PaymentRequestButtonInner({
return () => {
paymentRequest.off('paymentmethod', handlePaymentMethod);
};
}, [paymentRequest, stripe, amountInRsc, fundraiseId, onSuccess, onError]);
}, [paymentRequest, stripe, amountInRsc, paymentTarget, onSuccess, onError]);

// Still checking availability
if (canMakePayment === null) {
Expand Down
16 changes: 9 additions & 7 deletions components/Funding/PaymentStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
PAYMENT_PROCESSING_FEE,
METHODS_WITH_PROCESSING_FEE,
} from './lib/constants';
import { ID } from '@/types/root';
import AnalyticsService, { LogEvent } from '@/services/analytics.service';
import type { PaymentIntentTarget } from '@/services/payment.service';

interface PaymentStepProps {
/** Amount in RSC (before fees) */
Expand All @@ -39,8 +39,8 @@ interface PaymentStepProps {
rscBalance: number;
/** User's funding credits balance (excludes promotional RSC) */
fundingCreditsBalance?: number;
/** Fundraise ID for payment request button */
fundraiseId: ID;
/** Fundraise or funding pool target for Apple Pay / Google Pay */
paymentTarget: PaymentIntentTarget;
/** Wallet payment method availability from Stripe (resolved at modal level) */
walletAvailability: WalletAvailability;
/** Whether the fundraise has a non-profit org (shows Endaoment option) */
Expand Down Expand Up @@ -71,7 +71,7 @@ export function PaymentStep({
amountDisplay,
rscBalance,
fundingCreditsBalance = 0,
fundraiseId,
paymentTarget,
walletAvailability,
hasNonprofit = false,
isProcessing = false,
Expand Down Expand Up @@ -176,13 +176,15 @@ export function PaymentStep({
// Track payment method selection
if (method) {
AnalyticsService.logEvent(LogEvent.FUNDRAISE_CONTRIBUTION_PAYMENT_METHOD_SELECTED, {
fundraise_id: fundraiseId,
...('fundingPoolId' in paymentTarget
? { funding_pool_id: paymentTarget.fundingPoolId }
: { fundraise_id: paymentTarget.fundraiseId }),
payment_method: method,
amount_usd: amountInUsd,
});
}
},
[fundraiseId, amountInUsd]
[paymentTarget, amountInUsd]
);

// Dummy handlers for PaymentWidget (we handle the action in this component)
Expand Down Expand Up @@ -324,7 +326,7 @@ export function PaymentStep({
<PaymentRequestButton
amountCents={Math.round(totalDueUsd * 100)}
amountInRsc={amountInRsc}
fundraiseId={fundraiseId}
paymentTarget={paymentTarget}
label="Fund Research"
unavailableText={
selectedMethod === 'apple_pay'
Expand Down
5 changes: 4 additions & 1 deletion components/Funding/QuickAmountSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface QuickAmountSelectorProps {
onAmountSelect: (amount: number) => void;
/** Remaining goal amount in USD */
remainingGoalUsd: number;
/** Hide the Remaining button (e.g. unbounded RFP pool contributions) */
showRemaining?: boolean;
/** Optional class name */
className?: string;
}
Expand All @@ -32,6 +34,7 @@ export const QuickAmountSelector: FC<QuickAmountSelectorProps> = ({
selectedAmount,
onAmountSelect,
remainingGoalUsd,
showRemaining = true,
className,
}) => {
const handleAmountClick = useCallback(
Expand Down Expand Up @@ -79,7 +82,7 @@ export const QuickAmountSelector: FC<QuickAmountSelectorProps> = ({
))}

{/* Remaining button */}
{remainingGoalUsd > 0 && (
{showRemaining && remainingGoalUsd > 0 && (
<button
type="button"
onClick={handleFundRemaining}
Expand Down
Loading
Loading