add frontend files
This commit is contained in:
8
app/javascript/modules.d.ts
vendored
Normal file
8
app/javascript/modules.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
declare module "@rails/ujs"
|
||||
declare module "turbolinks"
|
||||
declare module "@rails/activestorage"
|
||||
|
||||
declare module '*.png' {
|
||||
const png: string
|
||||
export default png
|
||||
}
|
||||
@@ -13,4 +13,3 @@
|
||||
// ActiveStorage.start()
|
||||
|
||||
import "stylesheets/application"
|
||||
import './src/index'
|
||||
1
app/javascript/packs/react.ts
Normal file
1
app/javascript/packs/react.ts
Normal file
@@ -0,0 +1 @@
|
||||
import '../src/index'
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from "react"
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Route,
|
||||
} from "react-router-dom";
|
||||
|
||||
export const App = () => (
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route path="/about">
|
||||
<div>About!</div>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<div className="bg-gray-600">Hello World!</div>
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
)
|
||||
30
app/javascript/src/App.tsx
Normal file
30
app/javascript/src/App.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from "react"
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Route,
|
||||
} from "react-router-dom";
|
||||
import { AppContext } from "./contexts/AppContext";
|
||||
import { AuthProvider } from "./contexts/AuthProvider";
|
||||
import { Home } from "./pages";
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<AppContext>
|
||||
<main className="min-h-screen w-full bg-gray-50 flex flex-col">
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route path="/hello_about">
|
||||
<div className="bg-gray-600">Hello World!</div>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Home />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
</main>
|
||||
</AppContext>
|
||||
</AuthProvider>
|
||||
)
|
||||
}
|
||||
17
app/javascript/src/components/Container.tsx
Normal file
17
app/javascript/src/components/Container.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as React from "react";
|
||||
import cx from "classnames";
|
||||
|
||||
export type ContainerProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const Container = ({
|
||||
children,
|
||||
className,
|
||||
}: React.PropsWithChildren<ContainerProps>) => {
|
||||
return (
|
||||
<div className="w-full flex items-center justify-center px-8 py-2 2xl:p-0">
|
||||
<div className={cx("max-w-5xl w-full flex", className)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
11
app/javascript/src/components/Header.tsx
Normal file
11
app/javascript/src/components/Header.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import * as React from "react";
|
||||
|
||||
export type HeaderProps = {};
|
||||
|
||||
export const Header = ({ children }: React.PropsWithChildren<HeaderProps>) => {
|
||||
return (
|
||||
<div className="w-full h-64 bg-gradient-to-br from-green-300 to-green-400 grid place-items-center">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
62
app/javascript/src/components/Navbar.tsx
Normal file
62
app/javascript/src/components/Navbar.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import * as React from "react";
|
||||
import { useAuth0 } from "@auth0/auth0-react";
|
||||
import XStakeLogo from '../images/logo.png'
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
|
||||
export const Navbar = () => {
|
||||
const { setSideNavExpanded } = useAppContext()
|
||||
|
||||
const handleExpandSideNav = () => {
|
||||
setSideNavExpanded((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const { loginWithRedirect, logout, isAuthenticated } = useAuth0();
|
||||
|
||||
return (
|
||||
<nav className="fixed w-full h-16 flex bg-white shadow items-center px-4 space-x-2 z-50">
|
||||
<button
|
||||
className="w-10 h-10 xl:hidden"
|
||||
onClick={() => handleExpandSideNav()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-full w-full"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<img
|
||||
src={XStakeLogo}
|
||||
alt={"XStake Logo"}
|
||||
width={64}
|
||||
placeholder={"blurred"}
|
||||
/>
|
||||
<h1 className="text-2xl font-bold">XStake</h1>
|
||||
<div className="w-full h-full flex items-center justify-end">
|
||||
{isAuthenticated ? (
|
||||
<button
|
||||
className="cursor-pointer hover:bg-gray-100 h-full px-4 font-bold"
|
||||
onClick={() => logout({ returnTo: window.location.origin })}
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="cursor-pointer hover:bg-gray-100 h-full px-4 font-bold"
|
||||
onClick={loginWithRedirect}
|
||||
>
|
||||
Entrar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
50
app/javascript/src/components/PoolListing.tsx
Normal file
50
app/javascript/src/components/PoolListing.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
|
||||
import { pools } from "../constants/Pools";
|
||||
|
||||
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) => (
|
||||
<div
|
||||
key={pool.sousId}
|
||||
id={pool.sousId.toString()}
|
||||
className="flex items-center w-full h-auto bg-white px-16 p-4 rounded-xl shadow flex-col relative z-0 overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer"
|
||||
>
|
||||
<div
|
||||
className="box-border h-full w-full absolute left-0 top-0 rounded-xl opacity-20 filter blur-2xl bg-cover"
|
||||
style={{
|
||||
backgroundImage: `url('https://pancakeswap.finance/images/tokens/${pool.earningToken.address["56"]}.svg')`,
|
||||
backgroundPositionX: "50%",
|
||||
backgroundPositionY: "50%",
|
||||
backgroundSize: "125%",
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
className="shadow-xl rounded-full w-24"
|
||||
src={`https://pancakeswap.finance/images/tokens/${pool.earningToken.address["56"]}.svg`}
|
||||
alt={`${pool.earningToken.symbol} icon`}
|
||||
/>
|
||||
<div className="mt-4 p-2">
|
||||
<p>
|
||||
<span className="font-medium">Investir:</span>{" "}
|
||||
{pool.stakingToken.symbol}
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium">Receber:</span>{" "}
|
||||
{pool.earningToken.symbol}
|
||||
</p>
|
||||
<div className="flex items-center">
|
||||
<span className="font-medium mr-1">Rendimento:</span>
|
||||
<div className="w-10 h-5 inline-block animate-pulse bg-gray-300 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
57
app/javascript/src/components/SideNav.tsx
Normal file
57
app/javascript/src/components/SideNav.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import * as React from "react";
|
||||
import cx from "classnames";
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
|
||||
type MenuItem = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
const MenuItems: MenuItem[] = [
|
||||
{
|
||||
label: "Início",
|
||||
},
|
||||
{
|
||||
label: "Stake",
|
||||
},
|
||||
{
|
||||
label: "Carteira",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const SideNav = () => {
|
||||
const { sideNavExpanded, setSideNavExpanded } = useAppContext()
|
||||
|
||||
const handleCloseSideNav = () => {
|
||||
setSideNavExpanded(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed left-0 right-0 bottom-0 mt-16 top-0 z-40 xl:static xl:w-72">
|
||||
<div
|
||||
role="row"
|
||||
onClick={() => handleCloseSideNav()}
|
||||
className={cx(
|
||||
"xl:hidden absolute w-full h-full bg-black bg-opacity-60 backdrop-filter backdrop-blur-sm z-30 transition-all duration-500",
|
||||
!sideNavExpanded && "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<aside
|
||||
className={`bg-white w-5/6 md:w-2/6 overflow-hidden absolute h-full drop-shadow-xl drop border-r border-gray-200 z-40 transition-all duration-500 xl:transition-none xl:mx-0 xl:static xl:w-full ${
|
||||
sideNavExpanded ? "mx-0" : "-mx-full"
|
||||
}`}
|
||||
>
|
||||
<ul>
|
||||
{MenuItems.map((item) => (
|
||||
<li
|
||||
key={item.label}
|
||||
className="text-xl p-4 px-8 hover:bg-gray-100 cursor-pointer"
|
||||
>
|
||||
<a href="#">{item.label}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
2637
app/javascript/src/constants/Pools.ts
Normal file
2637
app/javascript/src/constants/Pools.ts
Normal file
File diff suppressed because it is too large
Load Diff
1714
app/javascript/src/constants/pancake/Tokens.ts
Normal file
1714
app/javascript/src/constants/pancake/Tokens.ts
Normal file
File diff suppressed because it is too large
Load Diff
28
app/javascript/src/contexts/AppContext.tsx
Normal file
28
app/javascript/src/contexts/AppContext.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React, { createContext, Dispatch, FC, SetStateAction, useContext, useState } from 'react'
|
||||
|
||||
export type AppContext = {
|
||||
setSideNavExpanded: Dispatch<SetStateAction<boolean>>
|
||||
sideNavExpanded: boolean
|
||||
}
|
||||
|
||||
const Context = createContext<AppContext | null>(null)
|
||||
|
||||
export const useAppContext = (): AppContext => {
|
||||
const context = useContext(Context);
|
||||
|
||||
if (context === null) {
|
||||
throw new Error("You probably forgot to put <AppContext>.");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export const AppContext: FC = ({ children }) => {
|
||||
const [sideNavExpanded, setSideNavExpanded] = useState(false)
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ sideNavExpanded, setSideNavExpanded }}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
22
app/javascript/src/contexts/AuthProvider.tsx
Normal file
22
app/javascript/src/contexts/AuthProvider.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Auth0Provider } from '@auth0/auth0-react'
|
||||
import React, { FC } from 'react'
|
||||
|
||||
export const AuthProvider: FC = ({children}) => {
|
||||
// @ts-ignore
|
||||
const domain = window.AUTH_DOMAIN
|
||||
// @ts-ignore
|
||||
const clientId = window.AUTH_CLIENT_ID
|
||||
// @ts-ignore
|
||||
const audience = window.AUTH_AUDIENCE
|
||||
|
||||
return (
|
||||
<Auth0Provider
|
||||
domain={domain}
|
||||
clientId={clientId}
|
||||
audience={audience}
|
||||
redirectUri={window.location.origin}
|
||||
>
|
||||
{children}
|
||||
</Auth0Provider>
|
||||
)
|
||||
}
|
||||
BIN
app/javascript/src/images/logo.png
Normal file
BIN
app/javascript/src/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 202 KiB |
@@ -6,8 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
,
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'),
|
||||
)
|
||||
})
|
||||
30
app/javascript/src/pages/Home/Home.tsx
Normal file
30
app/javascript/src/pages/Home/Home.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React, { FC } from 'react'
|
||||
import { Container } from '../../components/Container'
|
||||
import { Header } from '../../components/Header'
|
||||
import { Navbar } from '../../components/Navbar'
|
||||
import { PoolListing } from '../../components/PoolListing'
|
||||
import { SideNav } from '../../components/SideNav'
|
||||
|
||||
export const Home: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex flex-grow">
|
||||
<SideNav />
|
||||
<div className="flex flex-col h-full w-full overflow-x-hidden mt-16">
|
||||
<Header>
|
||||
<Container className="flex-col">
|
||||
<h1 className="text-5xl text-white font-medium">XStake</h1>
|
||||
<h2 className="text-3xl text-gray-50 font-light">
|
||||
Investir em crypto não precisa ser difícil.
|
||||
</h2>
|
||||
</Container>
|
||||
</Header>
|
||||
<Container>
|
||||
<PoolListing />
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
app/javascript/src/pages/Home/index.ts
Normal file
1
app/javascript/src/pages/Home/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Home'
|
||||
1
app/javascript/src/pages/index.ts
Normal file
1
app/javascript/src/pages/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Home'
|
||||
1
app/javascript/src/pages/index.tsx
Normal file
1
app/javascript/src/pages/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Home'
|
||||
15
app/javascript/src/types/PoolConfig.ts
Normal file
15
app/javascript/src/types/PoolConfig.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { Address, Token } from "../constants/pancake/Tokens";
|
||||
import type { PoolCategory } from "../constants/Pools";
|
||||
|
||||
export type PoolConfig = {
|
||||
sousId: number;
|
||||
earningToken: Token;
|
||||
stakingToken: Token;
|
||||
contractAddress: Address;
|
||||
poolCategory: PoolCategory;
|
||||
tokenPerBlock: string;
|
||||
sortOrder?: number;
|
||||
harvest?: boolean;
|
||||
isFinished?: boolean;
|
||||
enableEmergencyWithdraw?: boolean;
|
||||
};
|
||||
5
app/javascript/src/types/Token.ts
Normal file
5
app/javascript/src/types/Token.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type Token = {
|
||||
symbol: string;
|
||||
address: string;
|
||||
decimals: number;
|
||||
};
|
||||
2
app/javascript/src/types/index.ts
Normal file
2
app/javascript/src/types/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./Token";
|
||||
export * from "./PoolConfig";
|
||||
@@ -1,11 +1,18 @@
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
|
||||
module.exports = {
|
||||
purge: [],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {
|
||||
spacing: {
|
||||
...defaultTheme.spacing,
|
||||
'full': '100%',
|
||||
}
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,7 +7,15 @@
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||
|
||||
<script>
|
||||
window.AUTH_DOMAIN = "<%= ENV['AUTH_DOMAIN'] %>"
|
||||
window.AUTH_CLIENT_ID = "<%= ENV['AUTH_CLIENT_ID'] %>"
|
||||
window.AUTH_AUDIENCE = "<%= ENV['AUTH_AUDIENCE'] %>"
|
||||
</script>
|
||||
|
||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||
<%= javascript_pack_tag 'react', 'data-turbolinks-track': 'reload' %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user