98 lines
3.0 KiB
TypeScript
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>— 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 };
|