feat: add ui to handle code creation and stake requirements
This commit is contained in:
@@ -16,8 +16,8 @@ export const ApplyCodeForm = () => {
|
||||
setValue,
|
||||
setError,
|
||||
} = useForm();
|
||||
|
||||
const [params] = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
const code = params.get('code');
|
||||
if (code) setValue('code', code);
|
||||
@@ -46,7 +46,7 @@ export const ApplyCodeForm = () => {
|
||||
|
||||
return (
|
||||
<div className="w-1/2 mx-auto">
|
||||
<h3 className="text-xl mb-5 text-center uppercase calt">
|
||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
||||
Apply a referral code
|
||||
</h3>
|
||||
<p className="mb-6 text-center">Enter a referral code</p>
|
||||
@@ -57,7 +57,7 @@ export const ApplyCodeForm = () => {
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<label className="flex-grow">
|
||||
<span className="block text-vega-clight-100 dark:text-vega-cdark-100 mb-1 text-sm">
|
||||
<span className="block mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
Your referral code
|
||||
</span>
|
||||
<Input
|
||||
|
||||
@@ -5,25 +5,128 @@ import {
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { RainbowButton } from './buttons';
|
||||
import { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Button,
|
||||
CopyWithTooltip,
|
||||
Dialog,
|
||||
ExternalLink,
|
||||
InputError,
|
||||
Intent,
|
||||
TradingAnchorButton,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
|
||||
|
||||
export const CreateCodeForm = () => {
|
||||
const CREATE_CODE_QUERY = gql`
|
||||
query CreateCode($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
}
|
||||
}
|
||||
networkParameter(key: "referralProgram.minStakedVegaTokens") {
|
||||
value
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CreateCodeContainer = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { data, loading } = useQuery(CREATE_CODE_QUERY, {
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
// TOOD: remove when network params available
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
const requiredStake = data?.networkParameter?.value || '0';
|
||||
const currentStakeAvailable =
|
||||
data?.party?.stakingSummary.currentStakeAvailable || '0';
|
||||
|
||||
return (
|
||||
<CreateCodeForm
|
||||
currentStakeAvailable={currentStakeAvailable}
|
||||
requiredStake={requiredStake}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const CreateCodeForm = ({
|
||||
currentStakeAvailable,
|
||||
requiredStake,
|
||||
}: {
|
||||
currentStakeAvailable: string;
|
||||
requiredStake: string;
|
||||
}) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
return (
|
||||
<div className="w-1/2 mx-auto">
|
||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
||||
Create a referral code
|
||||
</h3>
|
||||
<p className="mb-6 text-center">
|
||||
Generate a referral code to share with your friends and start earning
|
||||
commission.
|
||||
</p>
|
||||
<div className="mb-5">
|
||||
<div className="text-center">
|
||||
<RainbowButton
|
||||
variant="border"
|
||||
onClick={() => {
|
||||
if (pubKey) {
|
||||
setDialogOpen(true);
|
||||
} else {
|
||||
openWalletDialog();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{pubKey ? 'Create a referral code' : 'Connect wallet'}
|
||||
</RainbowButton>
|
||||
</div>
|
||||
</div>
|
||||
<Dialog
|
||||
title="Create a referral code"
|
||||
open={dialogOpen}
|
||||
onChange={() => setDialogOpen(false)}
|
||||
size="small"
|
||||
>
|
||||
<CreateCodeDialog
|
||||
currentStakeAvailable={currentStakeAvailable}
|
||||
setDialogOpen={setDialogOpen}
|
||||
requiredStake={requiredStake}
|
||||
/>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateCodeDialog = ({
|
||||
setDialogOpen,
|
||||
currentStakeAvailable,
|
||||
requiredStake,
|
||||
}: {
|
||||
setDialogOpen: (open: boolean) => void;
|
||||
currentStakeAvailable: string;
|
||||
requiredStake: string;
|
||||
}) => {
|
||||
const createLink = useLinks(DApp.Governance);
|
||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [code, setCode] = useState<string | null>(null);
|
||||
const [status, setStatus] = useState<
|
||||
'idle' | 'loading' | 'success' | 'error'
|
||||
>('idle');
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||
|
||||
const onSubmit = () => {
|
||||
if (isReadOnly || !pubKey) {
|
||||
setErr('Not connected');
|
||||
@@ -56,44 +159,69 @@ export const CreateCodeForm = () => {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-1/2 mx-auto">
|
||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
||||
Create a referral code
|
||||
</h3>
|
||||
<p className="mb-6 text-center">
|
||||
Generate a referral code to share with your friends and start earning
|
||||
commission.
|
||||
</p>
|
||||
<div className="mb-5">
|
||||
{pubKey ? (
|
||||
<div className="text-center">
|
||||
<RainbowButton
|
||||
className={classNames({
|
||||
'animate-shake': err,
|
||||
})}
|
||||
variant="border"
|
||||
disabled={status === 'loading'}
|
||||
onClick={() => onSubmit()}
|
||||
>
|
||||
{status === 'loading'
|
||||
? 'Complete in wallet'
|
||||
: 'Create a referral code'}
|
||||
</RainbowButton>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center">
|
||||
<RainbowButton variant="border" onClick={() => openWalletDialog()}>
|
||||
Connect wallet
|
||||
</RainbowButton>
|
||||
</div>
|
||||
)}
|
||||
const getButtonProps = () => {
|
||||
if (status === 'idle' || status === 'error') {
|
||||
return {
|
||||
children: 'Generate code',
|
||||
onClick: () => onSubmit(),
|
||||
};
|
||||
}
|
||||
|
||||
if (status === 'loading') {
|
||||
return {
|
||||
children: 'Confirm in wallet...',
|
||||
disabled: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (status === 'success') {
|
||||
return {
|
||||
children: 'Close',
|
||||
intent: Intent.Success,
|
||||
onClick: () => setDialogOpen(false),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Add when network parameters are updated
|
||||
|
||||
if (
|
||||
currentStakeAvailable === '0' ||
|
||||
BigInt(currentStakeAvailable) < BigInt(requiredStake)
|
||||
) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<p>
|
||||
You need at least {addDecimalsFormatNumber(requiredStake, 18)} VEGA
|
||||
staked to generate a referral code and participate in the referral
|
||||
program.
|
||||
</p>
|
||||
<TradingAnchorButton
|
||||
href={createLink(TokenStaticLinks.ASSOCIATE)}
|
||||
intent={Intent.Primary}
|
||||
target="_blank"
|
||||
>
|
||||
Stake some $VEGA now
|
||||
</TradingAnchorButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{(status === 'idle' || status === 'loading' || status === 'error') && (
|
||||
<p>
|
||||
Generate a referral code to share with your friends aand start enaring
|
||||
commission.
|
||||
</p>
|
||||
)}
|
||||
{status === 'success' && code && (
|
||||
<div className="flex gap-2">
|
||||
<p className="flex items-center px-2 text-sm rounded bg-vega-clight-700 dark:bg-vega-cdark-700">
|
||||
<span>{code}</span>
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 min-w-0 p-2 text-sm rounded bg-vega-clight-700 dark:bg-vega-cdark-700">
|
||||
<p className="overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{code}
|
||||
</p>
|
||||
</div>
|
||||
<CopyWithTooltip text={code}>
|
||||
<TradingButton
|
||||
className="text-sm no-underline"
|
||||
@@ -104,6 +232,17 @@ export const CreateCodeForm = () => {
|
||||
</CopyWithTooltip>
|
||||
</div>
|
||||
)}
|
||||
<TradingButton
|
||||
fill={true}
|
||||
intent={Intent.Primary}
|
||||
{...getButtonProps()}
|
||||
/>
|
||||
{err && <InputError>{err}</InputError>}
|
||||
{/* TODO: Add links */}
|
||||
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
|
||||
<ExternalLink>About the referral program</ExternalLink>
|
||||
<ExternalLink>Disclaimer</ExternalLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { gql } from '@apollo/client';
|
||||
import { getNumberFormat } from '@vegaprotocol/utils';
|
||||
import { addDays } from 'date-fns';
|
||||
|
||||
// TODO: Generate query
|
||||
// eslint-disable-next-line
|
||||
const REFERRAL_PROGRAM_QUERY = gql`
|
||||
query ReferralProgram {
|
||||
currentReferralProgram {
|
||||
@@ -27,7 +28,9 @@ const REFERRAL_PROGRAM_QUERY = gql`
|
||||
|
||||
export const useReferralProgram = () => {
|
||||
// TODO: get real data
|
||||
// const { data, loading, error } = useQuery(REFERRAL_PROGRAM_QUERY);
|
||||
// const { data, loading, error } = useQuery(REFERRAL_PROGRAM_QUERY, {
|
||||
// fetchPolicy: 'cache-and-network',
|
||||
// });
|
||||
|
||||
const dummyData = {
|
||||
currentReferralProgram: {
|
||||
|
||||
@@ -59,6 +59,7 @@ export const Referrals = () => {
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/1x.png"
|
||||
alt="1x multiplier"
|
||||
@@ -84,6 +85,7 @@ export const Referrals = () => {
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/2x.png"
|
||||
alt="2x multiplier"
|
||||
@@ -108,6 +110,7 @@ export const Referrals = () => {
|
||||
)}
|
||||
>
|
||||
<div aria-hidden>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/3x.png"
|
||||
alt="3x multiplier"
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LayoutWithSidebar } from '../components/layouts';
|
||||
import { LayoutWithSky } from '../client-pages/referrals/layout';
|
||||
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
|
||||
import { ApplyCodeForm } from '../client-pages/referrals/apply-code-form';
|
||||
import { CreateCodeForm } from '../client-pages/referrals/create-code-form';
|
||||
import { CreateCodeContainer } from '../client-pages/referrals/create-code-form';
|
||||
|
||||
const LazyHome = dynamic(() => import('../client-pages/home'), {
|
||||
ssr: false,
|
||||
@@ -146,7 +146,7 @@ export const routerConfig: RouteObject[] = [
|
||||
},
|
||||
{
|
||||
path: Routes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeForm />,
|
||||
element: <CreateCodeContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -167,7 +167,7 @@ export const ClientRouter = () => {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<div className="flex items-center justify-center w-full h-full">
|
||||
<Loader />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -175,4 +175,5 @@ export const ExternalLinks = {
|
||||
export const TokenStaticLinks = {
|
||||
PROPOSAL_PAGE: ':tokenUrl/proposals/:proposalId',
|
||||
UPDATE_PROPOSAL_PAGE: ':tokenUrl/proposals/propose/update-market',
|
||||
ASSOCIATE: 'token/associate',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user