add stake modal
This commit is contained in:
73
app/javascript/src/components/Modal/Modal.tsx
Normal file
73
app/javascript/src/components/Modal/Modal.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import type { FC } from "react";
|
||||||
|
import React, { Fragment } from "react";
|
||||||
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
setIsOpen: (state: boolean) => void;
|
||||||
|
title: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Modal: FC<Props> = ({
|
||||||
|
isOpen,
|
||||||
|
setIsOpen,
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
className = "",
|
||||||
|
}) => {
|
||||||
|
const closeModal = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
as="div"
|
||||||
|
className={`fixed inset-0 z-10 overflow-y-auto ${className}`}
|
||||||
|
onClose={closeModal}
|
||||||
|
>
|
||||||
|
<div className="min-h-screen px-4 text-center">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<Dialog.Overlay className="fixed inset-0 bg-gray-900 bg-opacity-50" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="inline-block h-screen align-middle"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
​
|
||||||
|
</span>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 scale-95"
|
||||||
|
enterTo="opacity-100 scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 scale-100"
|
||||||
|
leaveTo="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded">
|
||||||
|
<Dialog.Title
|
||||||
|
as="h3"
|
||||||
|
className="text-lg font-medium leading-6 text-gray-900 mb-4"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Dialog.Title>
|
||||||
|
<div className="mt-2">{children}</div>
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
};
|
||||||
1
app/javascript/src/components/Modal/index.ts
Normal file
1
app/javascript/src/components/Modal/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./Modal";
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
import { pools } from "../constants/Pools";
|
|
||||||
import { Pool } from "./Pool";
|
|
||||||
|
|
||||||
export const PoolListing = () => {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 place-items-center w-full gap-8 py-4 -mt-16 overflow-x-hidden">
|
|
||||||
{pools
|
|
||||||
.filter((pool) => !pool.isFinished)
|
|
||||||
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
|
|
||||||
.map((pool) => (
|
|
||||||
<Pool key={pool.sousId} pool={pool} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,7 @@ import * as React from "react";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Transition } from "@headlessui/react";
|
import { Transition } from "@headlessui/react";
|
||||||
|
|
||||||
import { useApp } from "../contexts/AppProvider";
|
import { useApp } from "../../contexts/AppProvider";
|
||||||
|
|
||||||
type MenuItem = {
|
type MenuItem = {
|
||||||
label: string;
|
label: string;
|
||||||
1
app/javascript/src/components/SideNav/index.ts
Normal file
1
app/javascript/src/components/SideNav/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./SideNav";
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./Navbar";
|
export * from "./Navbar";
|
||||||
export * from "./SideNav";
|
export * from "./SideNav";
|
||||||
|
export * from "./Modal";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Container } from "../../components/Container";
|
import { Container } from "./Container";
|
||||||
import { Header } from "../../components/Header";
|
import { Header } from "./Header";
|
||||||
import { PoolListing } from "../../components/PoolListing";
|
import { PoolListing } from "./PoolListing";
|
||||||
|
|
||||||
export const Home: FC = () => {
|
export const Home: FC = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
|
import type { FC } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import type { PoolConfig } from "../types";
|
import type { PoolConfig } from "../../types";
|
||||||
import { useBsc } from "../contexts/BscProvider";
|
import { useBsc } from "../../contexts/BscProvider";
|
||||||
import { getPriceInBusd } from "../utils/getPrice";
|
import { getApr } from "../../utils/apr";
|
||||||
import { getApr } from "../utils/apr";
|
import { getPriceInBusd } from "../../utils/getPrice";
|
||||||
import { getTotalStaked } from "../utils/getTotalStaked";
|
import { getTotalStaked } from "../../utils/getTotalStaked";
|
||||||
|
import { StakeOrderModal } from "./StakeOrderModal";
|
||||||
|
|
||||||
type PoolProps = {
|
type PoolProps = {
|
||||||
pool: PoolConfig;
|
pool: PoolConfig;
|
||||||
|
cakeBalance: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Pool = ({ pool }: PoolProps) => {
|
export const Pool: FC<PoolProps> = ({ pool, cakeBalance }) => {
|
||||||
const {
|
const {
|
||||||
provider,
|
provider,
|
||||||
pancake: { router },
|
pancake: { router },
|
||||||
@@ -77,12 +80,7 @@ export const Pool = ({ pool }: PoolProps) => {
|
|||||||
/>
|
/>
|
||||||
<div className="mt-4 p-2">
|
<div className="mt-4 p-2">
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Investir:</span>{" "}
|
<span className="font-medium">Pool:</span> {pool.earningToken.symbol}
|
||||||
{pool.stakingToken.symbol}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="font-medium">Receber:</span>{" "}
|
|
||||||
{pool.earningToken.symbol}
|
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="font-medium mr-1">Rendimento:</span>
|
<span className="font-medium mr-1">Rendimento:</span>
|
||||||
@@ -92,6 +90,10 @@ export const Pool = ({ pool }: PoolProps) => {
|
|||||||
`${apr.value}%`
|
`${apr.value}%`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<StakeOrderModal
|
||||||
|
poolName={pool.earningToken.symbol}
|
||||||
|
cakeBalance={cakeBalance}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
42
app/javascript/src/pages/Home/PoolListing.tsx
Normal file
42
app/javascript/src/pages/Home/PoolListing.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import React from "react";
|
||||||
|
import { useLazyLoadQuery } from "react-relay";
|
||||||
|
|
||||||
|
import { pools } from "../../constants/Pools";
|
||||||
|
import { Pool } from "./Pool";
|
||||||
|
import type { PoolListingQuery } from "./__generated__/PoolListingQuery.graphql";
|
||||||
|
|
||||||
|
export const PoolListing = () => {
|
||||||
|
const { balances } = useLazyLoadQuery<PoolListingQuery>(
|
||||||
|
graphql`
|
||||||
|
query PoolListingQuery {
|
||||||
|
balances {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const cakeBalance =
|
||||||
|
balances.edges.find((edge) => edge.node.currency.name === "CAKE")?.node
|
||||||
|
.amount ?? "0";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 place-items-center w-full gap-8 py-4 -mt-16 overflow-x-hidden">
|
||||||
|
{pools
|
||||||
|
.filter((pool) => !pool.isFinished)
|
||||||
|
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
|
||||||
|
.map((pool) => (
|
||||||
|
<Pool key={pool.sousId} pool={pool} cakeBalance={cakeBalance} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
92
app/javascript/src/pages/Home/StakeOrderModal.tsx
Normal file
92
app/javascript/src/pages/Home/StakeOrderModal.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import type { ChangeEvent, FC } from "react";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import cx from "classnames";
|
||||||
|
import { BigNumber } from "bignumber.js";
|
||||||
|
|
||||||
|
import { Modal } from "../../components";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
poolName: string;
|
||||||
|
cakeBalance: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const inputBaseStyles =
|
||||||
|
"rounded-lg border-transparent flex-1 appearance-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent mb-3";
|
||||||
|
|
||||||
|
export const StakeOrderModal: FC<Props> = ({ poolName, cakeBalance }) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [investAmountInput, setInvestAmountInput] = useState("0");
|
||||||
|
|
||||||
|
const avaliableCake = new BigNumber(cakeBalance);
|
||||||
|
const investAmount = new BigNumber(investAmountInput);
|
||||||
|
|
||||||
|
const handleButtonClick = () => {
|
||||||
|
setIsOpen((prevState) => !prevState);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = () => {};
|
||||||
|
|
||||||
|
const handleInvestInput = ({
|
||||||
|
currentTarget: { value },
|
||||||
|
}: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newInvestAmount = new BigNumber(value);
|
||||||
|
|
||||||
|
if (
|
||||||
|
newInvestAmount.isLessThanOrEqualTo(avaliableCake) &&
|
||||||
|
newInvestAmount.isGreaterThanOrEqualTo(0)
|
||||||
|
) {
|
||||||
|
setInvestAmountInput(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stakeAvaliable =
|
||||||
|
avaliableCake.isGreaterThan(0) &&
|
||||||
|
avaliableCake.isLessThanOrEqualTo(investAmount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
type="button"
|
||||||
|
className="py-2 px-4 text-blue-600 border-2 border-blue-600 hover:bg-blue-100 w-full transition ease-in duration-200 text-center text-base font-semibold shadow-md rounded-lg "
|
||||||
|
>
|
||||||
|
Stake
|
||||||
|
</button>
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
setIsOpen={setIsOpen}
|
||||||
|
title={`Invista em ${poolName}`}
|
||||||
|
>
|
||||||
|
<span className="mb-2">CAKE disponível: {cakeBalance}</span>
|
||||||
|
<form onSubmit={onSubmit} className="bg-white py-2">
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<input
|
||||||
|
className={cx(inputBaseStyles)}
|
||||||
|
type="number"
|
||||||
|
value={investAmountInput}
|
||||||
|
onChange={handleInvestInput}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={investAmountInput === cakeBalance}
|
||||||
|
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Max
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{avaliableCake.isEqualTo(0) && (
|
||||||
|
<span className="text-red-500 mb-1">Você não possuí saldo.</span>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="cursor-pointer py-2 px-4 disabled:opacity-50 disabled:cursor-default bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white w-full transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg"
|
||||||
|
disabled={!stakeAvaliable}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Fazer Stake
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
189
app/javascript/src/pages/Home/__generated__/PoolListingQuery.graphql.ts
generated
Normal file
189
app/javascript/src/pages/Home/__generated__/PoolListingQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ConcreteRequest } from "relay-runtime";
|
||||||
|
export type PoolListingQueryVariables = {};
|
||||||
|
export type PoolListingQueryResponse = {
|
||||||
|
readonly balances: {
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly currency: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
|
readonly amount: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type PoolListingQuery = {
|
||||||
|
readonly response: PoolListingQueryResponse;
|
||||||
|
readonly variables: PoolListingQueryVariables;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
query PoolListingQuery {
|
||||||
|
balances {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
}
|
||||||
|
amount
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const node: ConcreteRequest = (function(){
|
||||||
|
var v0 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v1 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v2 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"fragment": {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "PoolListingQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BalanceConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "balances",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BalanceEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Balance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v0/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Query",
|
||||||
|
"abstractKey": null
|
||||||
|
},
|
||||||
|
"kind": "Request",
|
||||||
|
"operation": {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Operation",
|
||||||
|
"name": "PoolListingQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BalanceConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "balances",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BalanceEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Balance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v2/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v2/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"cacheID": "6abf5e963429e49993af50df156f8e1c",
|
||||||
|
"id": null,
|
||||||
|
"metadata": {},
|
||||||
|
"name": "PoolListingQuery",
|
||||||
|
"operationKind": "query",
|
||||||
|
"text": "query PoolListingQuery {\n balances {\n edges {\n node {\n currency {\n name\n id\n }\n amount\n id\n }\n }\n }\n}\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
(node as any).hash = '4fefb238e24b79198799686599255e6c';
|
||||||
|
export default node;
|
||||||
Reference in New Issue
Block a user