Files
ncd-fe/packages/pl-fe/src/features/patron/components/funding-panel.tsx
2026-02-14 15:46:38 +00:00

98 lines
3.0 KiB
TypeScript

import React from 'react';
import { FormattedMessage } from 'react-intl';
import Button from '@/components/ui/button';
import HStack from '@/components/ui/hstack';
import Icon from '@/components/ui/icon';
import ProgressBar from '@/components/ui/progress-bar';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Widget from '@/components/ui/widget';
import { usePatronInstance } from '@/queries/patron/use-patron-instance';
/** Formats integer to USD string. */
const moneyFormat = (amount: number): string => (
new Intl
.NumberFormat('en-US', {
style: 'currency',
currency: 'usd',
notation: 'compact',
})
.format(amount / 100)
);
interface IFundingPanel {
/** Renders a compact version without the Widget wrapper, for use in tight spaces like mobile nav. */
compact?: boolean;
}
const FundingPanel: React.FC<IFundingPanel> = ({ compact }) => {
const { data: patron } = usePatronInstance();
if (!patron || !patron.goals.length) return null;
const amount = patron.funding.amount;
const goal = patron.goals[0].amount;
const goalText = patron.goals[0].text;
const goalReached = amount >= goal;
let ratioText;
if (goalReached) {
ratioText = <><strong>{moneyFormat(goal)}</strong> per month <span>&mdash; reached!</span></>;
} else {
ratioText = <><strong>{moneyFormat(amount)} out of {moneyFormat(goal)}</strong> per month</>;
}
const handleDonateClick = () => {
window.open(patron.url, '_blank');
};
if (compact) {
return (
<button
type='button'
className='flex w-full flex-col gap-2 text-left'
onClick={handleDonateClick}
>
<Text size='sm' weight='medium'>
<FormattedMessage id='patron.title' defaultMessage='Funding Goal' />
</Text>
<ProgressBar progress={amount / goal} />
<HStack alignItems='center' justifyContent='between'>
<Text size='xs' theme='muted'>{ratioText}</Text>
<HStack alignItems='center' space={1} className='text-primary-600 dark:text-primary-400'>
<Text size='xs' weight='medium' theme='inherit'>
<FormattedMessage id='patron.donate' defaultMessage='Donate' />
</Text>
<Icon src={require('@phosphor-icons/core/regular/arrow-square-out.svg')} className='size-3.5' />
</HStack>
</HStack>
</button>
);
}
return (
<Widget
title={<FormattedMessage id='patron.title' defaultMessage='Funding Goal' />}
onActionClick={handleDonateClick}
>
<Stack space={4}>
<Stack space={2}>
<Text>{ratioText}</Text>
<ProgressBar progress={amount / goal} />
</Stack>
<Stack space={2}>
{goalText && <Text theme='muted'>{goalText}</Text>}
<Button block theme='primary' onClick={handleDonateClick}>
<FormattedMessage id='patron.donate' defaultMessage='Donate' />
</Button>
</Stack>
</Stack>
</Widget>
);
};
export { FundingPanel as default };