add exchange order screen
This commit is contained in:
51
app/javascript/__generated__/schema.graphql
generated
51
app/javascript/__generated__/schema.graphql
generated
@@ -39,6 +39,36 @@ type Currency implements Node {
|
||||
name: String!
|
||||
}
|
||||
|
||||
"""
|
||||
The connection type for Currency.
|
||||
"""
|
||||
type CurrencyConnection {
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
edges: [CurrencyEdge!]!
|
||||
|
||||
"""
|
||||
Information to aid in pagination.
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
type CurrencyEdge {
|
||||
"""
|
||||
A cursor for use in pagination.
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
The item at the end of the edge.
|
||||
"""
|
||||
node: Currency!
|
||||
}
|
||||
|
||||
type FiatBalance implements Node {
|
||||
amountCents: Int!
|
||||
amountCurrency: String!
|
||||
@@ -139,6 +169,27 @@ type Query {
|
||||
"""
|
||||
last: Int
|
||||
): BalanceConnection!
|
||||
currencies(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): CurrencyConnection!
|
||||
currentUser: User
|
||||
fiatBalances(
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { FC } from "react";
|
||||
import React from "react";
|
||||
import { Switch, Route } from "react-router-dom";
|
||||
|
||||
import { Home, Wallet } from "./pages";
|
||||
import { Home, Orders, Wallet } from "./pages";
|
||||
|
||||
export const Routes: FC = () => {
|
||||
return (
|
||||
@@ -13,6 +13,9 @@ export const Routes: FC = () => {
|
||||
<Route exact path="/wallet">
|
||||
<Wallet />
|
||||
</Route>
|
||||
<Route exact path="/orders/exchange">
|
||||
<Orders.Exchange />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,10 @@ const MenuItems: MenuItem[] = [
|
||||
label: "Carteira",
|
||||
path: "/wallet",
|
||||
},
|
||||
{
|
||||
label: "Ordem de Troca",
|
||||
path: "/orders/exchange",
|
||||
},
|
||||
];
|
||||
|
||||
export const SideNav = () => {
|
||||
|
||||
182
app/javascript/src/pages/Orders/Exchange/Exchange.tsx
Normal file
182
app/javascript/src/pages/Orders/Exchange/Exchange.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import React, { useState } from "react";
|
||||
import type { FC } from "react";
|
||||
import { graphql } from "babel-plugin-relay/macro";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
import { BigNumber } from "bignumber.js";
|
||||
import cx from "classnames";
|
||||
|
||||
import { useCurrentUser } from "../../../contexts/UserProvider";
|
||||
import { Unauthenticated } from "../../../messages/Unauthenticated";
|
||||
import type { ExchangeQuery } from "./__generated__/ExchangeQuery.graphql";
|
||||
|
||||
const tabBaseStyles =
|
||||
"w-full text-base font-bold text-black px-4 py-2 focus:ring-blue-500";
|
||||
|
||||
const selectedTabStyles =
|
||||
"bg-blue-600 hover:bg-blue-700 rounded-l-frounded-full text-white";
|
||||
|
||||
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 Exchange: FC = () => {
|
||||
const { isAuthenticated } = useCurrentUser();
|
||||
const [exchangeOption, setExchangeOption] = useState<"BUY" | "SELL">("BUY");
|
||||
const [cryptoDock, setCryptoDock] = useState<string>("0");
|
||||
const [fiatDock, setFiatDock] = useState<string>("0.00");
|
||||
|
||||
const { balances, fiatBalances } = useLazyLoadQuery<ExchangeQuery>(
|
||||
graphql`
|
||||
query ExchangeQuery {
|
||||
fiatBalances {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
amountCents
|
||||
amountCurrency
|
||||
}
|
||||
}
|
||||
}
|
||||
balances {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
amount
|
||||
currency {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
||||
if (!isAuthenticated) return <Unauthenticated />;
|
||||
const [crypto] = balances.edges;
|
||||
const [fiat] = fiatBalances.edges;
|
||||
|
||||
const avaliableCrypto = new BigNumber(crypto.node.amount);
|
||||
const avaliableFiat = (
|
||||
fiat.node.amountCents ? fiat.node.amountCents / 100 : 0
|
||||
).toFixed(2);
|
||||
|
||||
const handleSellTabClick = () => {
|
||||
setExchangeOption("SELL");
|
||||
setCryptoDock("0");
|
||||
};
|
||||
|
||||
const handleBuyTabClick = () => {
|
||||
setExchangeOption("BUY");
|
||||
setCryptoDock("0");
|
||||
};
|
||||
|
||||
const handleCryptoAmountChange = ({
|
||||
currentTarget: { value },
|
||||
}: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newCryptoAmount = new BigNumber(value);
|
||||
|
||||
if (newCryptoAmount.isLessThanOrEqualTo(avaliableCrypto)) {
|
||||
setCryptoDock(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFiatAmountChange = ({
|
||||
currentTarget: { value },
|
||||
}: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newFiatAmount = Number(value);
|
||||
|
||||
if (Number(avaliableFiat) >= newFiatAmount) {
|
||||
setFiatDock(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMaxFiatDockButton = () => {
|
||||
setFiatDock(avaliableFiat);
|
||||
};
|
||||
|
||||
const handleMaxCryptoButton = () => {
|
||||
setCryptoDock(avaliableCrypto.toString());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid place-items-center w-full">
|
||||
<div className="max-w-lg">
|
||||
<div className="flex items-center bg-white rounded-full border border-gray-200 mb-3">
|
||||
<button
|
||||
type="button"
|
||||
className={cx(
|
||||
tabBaseStyles,
|
||||
"rounded-full",
|
||||
exchangeOption === "BUY" && selectedTabStyles
|
||||
)}
|
||||
onClick={handleBuyTabClick}
|
||||
>
|
||||
Comprar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={cx(
|
||||
tabBaseStyles,
|
||||
"rounded-full",
|
||||
exchangeOption === "SELL" && selectedTabStyles
|
||||
)}
|
||||
onClick={handleSellTabClick}
|
||||
>
|
||||
Vender
|
||||
</button>
|
||||
</div>
|
||||
<form className="bg-white p-4 rounded-2xl border border-gray-200">
|
||||
<span className="mb-2">
|
||||
{exchangeOption === "SELL" ? "CAKE" : "BRL"} disponível:{" "}
|
||||
{exchangeOption === "SELL" ? crypto.node.amount : avaliableFiat}
|
||||
</span>
|
||||
<div className="flex flex-row">
|
||||
{exchangeOption === "BUY" ? (
|
||||
<>
|
||||
<input
|
||||
className={cx(inputBaseStyles)}
|
||||
type="number"
|
||||
value={fiatDock}
|
||||
onChange={handleFiatAmountChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
disabled={fiatDock === avaliableFiat}
|
||||
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
||||
onClick={handleMaxFiatDockButton}
|
||||
>
|
||||
Max
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<input
|
||||
className={cx(inputBaseStyles)}
|
||||
type="number"
|
||||
value={cryptoDock}
|
||||
onChange={handleCryptoAmountChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
disabled={avaliableCrypto.isEqualTo(cryptoDock)}
|
||||
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
||||
onClick={handleMaxCryptoButton}
|
||||
>
|
||||
Max
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="cursor-pointer py-2 px-4 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"
|
||||
type="submit"
|
||||
>
|
||||
{exchangeOption === "BUY" ? "Comprar" : "Vender"} CAKE
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
259
app/javascript/src/pages/Orders/Exchange/__generated__/ExchangeQuery.graphql.ts
generated
Normal file
259
app/javascript/src/pages/Orders/Exchange/__generated__/ExchangeQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,259 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from "relay-runtime";
|
||||
export type ExchangeQueryVariables = {};
|
||||
export type ExchangeQueryResponse = {
|
||||
readonly fiatBalances: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly amountCents: number;
|
||||
readonly amountCurrency: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly balances: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly amount: string;
|
||||
readonly currency: {
|
||||
readonly name: string;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
export type ExchangeQuery = {
|
||||
readonly response: ExchangeQueryResponse;
|
||||
readonly variables: ExchangeQueryVariables;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
query ExchangeQuery {
|
||||
fiatBalances {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
amountCents
|
||||
amountCurrency
|
||||
}
|
||||
}
|
||||
}
|
||||
balances {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
amount
|
||||
currency {
|
||||
name
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "FiatBalanceConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "fiatBalances",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "FiatBalanceEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "FiatBalance",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "amountCents",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "amountCurrency",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "amount",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "ExchangeQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"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": [
|
||||
(v0/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Currency",
|
||||
"kind": "LinkedField",
|
||||
"name": "currency",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "ExchangeQuery",
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"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": [
|
||||
(v0/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Currency",
|
||||
"kind": "LinkedField",
|
||||
"name": "currency",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "7a42b2fc93f97505aaaf21867de36321",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "ExchangeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query ExchangeQuery {\n fiatBalances {\n edges {\n node {\n id\n amountCents\n amountCurrency\n }\n }\n }\n balances {\n edges {\n node {\n id\n amount\n currency {\n name\n id\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
(node as any).hash = '80cac44f2e6288dfb573d0037b7d4148';
|
||||
export default node;
|
||||
1
app/javascript/src/pages/Orders/Exchange/index.ts
Normal file
1
app/javascript/src/pages/Orders/Exchange/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./Exchange";
|
||||
5
app/javascript/src/pages/Orders/index.ts
Normal file
5
app/javascript/src/pages/Orders/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Exchange } from "./Exchange";
|
||||
|
||||
export const Orders = {
|
||||
Exchange,
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./Home";
|
||||
export * from "./Wallet";
|
||||
export * from "./Orders";
|
||||
|
||||
@@ -25,7 +25,7 @@ class User < ApplicationRecord
|
||||
:recoverable, :rememberable, :validatable
|
||||
|
||||
has_many :documents, class_name: "UserDocument", dependent: :destroy
|
||||
has_many :balances, dependent: :restrict_with_error
|
||||
has_one :balance, dependent: :restrict_with_error
|
||||
has_one :fiat_balance, dependent: :restrict_with_error
|
||||
|
||||
validates :first_name, :last_name, :email, presence: true
|
||||
|
||||
Reference in New Issue
Block a user