feat: build donations components

- Donate Card
- Stat Card
- Donations Stats
- Header
This commit is contained in:
MTG2000
2022-05-24 19:01:59 +03:00
parent 8b94788b13
commit c61ef7e502
13 changed files with 300 additions and 2 deletions

View File

@@ -0,0 +1,13 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import DonateCard from './DonateCard';
export default {
title: 'Donations/Componets/Donate Card',
component: DonateCard,
} as ComponentMeta<typeof DonateCard>;
const Template: ComponentStory<typeof DonateCard> = (args) => <div className="max-w-[326px]"><DonateCard {...args as any} /></div>;
export const Default = Template.bind({});

View File

@@ -0,0 +1,103 @@
import React, { FormEvent, useState } from 'react';
import { PaymentStatus, useVote } from 'src/utils/hooks';
import Confetti from "react-confetti";
import { useWindowSize } from '@react-hookz/web';
import { Vote_Item_Type } from 'src/graphql';
const defaultOptions = [
{ text: '500', value: 500 },
{ text: '1,000', value: 1000 },
{ text: '5,000', value: 5000 },
{ text: '25,000', value: 25000 },
]
export default function DonateCard() {
const { width, height } = useWindowSize()
const [selectedOption, setSelectedOption] = useState(-1);
const [donationAmount, setDonationAmount] = useState<number>();
const { vote, paymentStatus } = useVote({
itemId: 123,
itemType: Vote_Item_Type.Project
})
const onChangeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedOption(-1);
setDonationAmount(Number(event.target.value));
};
const onSelectOption = (idx: number) => {
setSelectedOption(idx);
setDonationAmount(defaultOptions[idx].value);
}
const requestPayment = (e: FormEvent) => {
e.preventDefault();
if (donationAmount)
vote(donationAmount, {
onSuccess: () => {
setTimeout(() => {
setDonationAmount(undefined);
setSelectedOption(-1);
}, 4000);
},
onError: () => {
setTimeout(() => {
setDonationAmount(undefined);
setSelectedOption(-1);
}, 4000);
}
});
}
return (
<div
className="bg-gray-50 border w-full shadow-2xl p-24 rounded-xl relative"
>
<h2 className='text-h5 font-bold'>Donate to BOLT🔩FUN</h2>
<form onSubmit={requestPayment} className="mt-32 ">
<div className="input-wrapper">
<input
className={` input-text input-removed-arrows`}
value={donationAmount} onChange={onChangeInput}
type="number"
placeholder="1,000"
autoFocus
/>
<p className='px-16 shrink-0 self-center text-primary-400'>
Sats
</p>
</div>
<div className="flex mt-16 justify-between">
{defaultOptions.map((option, idx) =>
<button
type='button'
key={idx}
className={`btn border px-12 rounded-md py-8 text-body5 bg-primary-100 hover:bg-primary-50 text-primary-600 ${idx === selectedOption && "border-primary-500 bg-primary-100 hover:bg-primary-100 text-primary-600"}`}
onClick={() => onSelectOption(idx)}
>
{option.text}
</button>
)}
</div>
{paymentStatus === PaymentStatus.FETCHING_PAYMENT_DETAILS && <p className="text-body6 mt-12 text-yellow-500">Please wait while we the fetch payment details.</p>}
{paymentStatus === PaymentStatus.NOT_PAID && <p className="text-body6 mt-12 text-red-500">You did not confirm the payment. Please try again.</p>}
{paymentStatus === PaymentStatus.PAID && <p className="text-body6 mt-12 text-green-500">The invoice was paid! Please wait while we confirm it.</p>}
{paymentStatus === PaymentStatus.AWAITING_PAYMENT && <p className="text-body6 mt-12 text-yellow-500">Waiting for your payment...</p>}
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <p className="text-body6 mt-12 text-green-500">Thanks for your vote</p>}
<button
type='submit'
className="btn btn-primary w-full mt-32"
disabled={paymentStatus !== PaymentStatus.DEFAULT && paymentStatus !== PaymentStatus.NOT_PAID}
>
{paymentStatus === PaymentStatus.DEFAULT || paymentStatus === PaymentStatus.NOT_PAID ? "Make a donation" : "Donating..."}
</button>
</form>
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <Confetti width={width} height={height} />}
</div>
)
}

View File

@@ -1 +1 @@
export * from './pages/DonationsPage'
export * from './pages/DonatePage/DonatePage'

View File

@@ -0,0 +1,20 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import DonatePage from './DonatePage';
export default {
title: 'Donations/Donate Page/Page',
component: DonatePage,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof DonatePage>;
const Template: ComponentStory<typeof DonatePage> = (args) => <DonatePage {...args as any} ></DonatePage>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -1,6 +1,6 @@
export default function HackathonsPage() {
export default function DonatePage() {
return (
<div

View File

@@ -0,0 +1,32 @@
import { BiCoinStack } from "react-icons/bi";
import { FiGrid } from "react-icons/fi";
import { IoMedalOutline, IoRocketOutline } from "react-icons/io5";
import StatCard from "../StatCard/StatCard";
export default function DonationStats() {
return (
<div className="grid sm:grid-cols-2 md:grid-cols-4 gap-16">
<StatCard
color="#8B5CF6"
label={<><BiCoinStack className='scale-125 mr-8' /> <span className="align-middle">Donations</span></>}
value='$2.6k'
/>
<StatCard
color="#F59E0B"
label={<><IoRocketOutline className='scale-125 mr-8' /> <span className="align-middle">Tournaments</span></>}
value='1'
/>
<StatCard
color="#22C55E"
label={<><IoMedalOutline className='scale-125 mr-8' /> <span className="align-middle">Prizes</span></>}
value='2.5k'
/>
<StatCard
color="#3B82F6"
label={<><FiGrid className='scale-125 mr-8' /> <span className="align-middle">Applications</span></>}
value='36'
/>
</div>
)
}

View File

@@ -0,0 +1,20 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { FiGrid } from 'react-icons/fi'
import DonationStats from './DonationStats';
export default {
title: 'Donations/Donate Page/DonationStats',
component: DonationStats,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof DonationStats>;
const Template: ComponentStory<typeof DonationStats> = (args) => <div className="max-w-[910px] mx-auto"><DonationStats {...args as any} ></DonationStats></div>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -0,0 +1,20 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import Header from './Header';
export default {
title: 'Donations/Donate Page/Header',
component: Header,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof Header>;
const Template: ComponentStory<typeof Header> = (args) => <Header {...args as any} ></Header>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -0,0 +1,26 @@
import DonateCard from 'src/features/Donations/components/DonateCard/DonateCard'
import DonationStats from '../DonationStats/DonationStats'
import styles from './styles.module.scss'
export default function Header() {
return (
<div className={`${styles.header}`}>
<div className="flex items-center gap-24 flex-col md:flex-row">
<div>
<h1 className="text-[54px] font-bolder">
Donate
</h1>
<p className='text-h3 font-bolder mt-24'>
Help fund <span className="text-primary-600">BOLT🔩FUN</span>, as well as other <span className="text-primary-600">Makers</span> working on lightning apps through tournaments and prize pools
</p>
</div>
<div className="max-w-[326px]">
<DonateCard />
</div>
</div>
<div className="mt-52 md:mt-80">
<DonationStats />
</div>
</div>
)
}

View File

@@ -0,0 +1,17 @@
@import "/src/styles/mixins";
.header {
padding: 56px 24px;
background: #ffecf9;
background: linear-gradient(40deg, white -5%, #ffb7d963 74%, #e3faff61 100%);
& > div {
max-width: calc(min(100%, 910px));
margin-left: auto;
margin-right: auto;
}
@include gt-md {
padding: 156px 0;
}
}

View File

@@ -0,0 +1,23 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { FiGrid } from 'react-icons/fi'
import StatCard from './StatCard';
export default {
title: 'Donations/Donate Page/StatCard',
component: StatCard,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof StatCard>;
const Template: ComponentStory<typeof StatCard> = (args) => <div className="max-w-[220px]"><StatCard {...args} ></StatCard></div>
export const Default = Template.bind({});
Default.args = {
color: "#3B82F6",
label: <><FiGrid className='scale-125 mr-8' /> Applications</>,
value: '36'
}

View File

@@ -0,0 +1,24 @@
import { ReactNode } from 'react'
interface Props {
label: ReactNode,
value: ReactNode,
color: string
}
export default function StatCard(props: Props) {
return (
<div className="bg-white p-24 rounded-16 text-center"
style={{
color: props.color,
}}
>
<p className="text-body4">
{props.label}
</p>
<p className="text-h2 mt-8 font-bolder">
{props.value}
</p>
</div>
)
}