Switch to site built with astro
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Route,
|
||||
} from 'react-router-dom'
|
||||
import Home from './pages/Home/Home'
|
||||
import Auth from './pages/Auth/Auth'
|
||||
import Dashboard from './pages/Dashboard/Dashboard'
|
||||
import { getSession } from './utils'
|
||||
|
||||
export default class App extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// console.log(getSession())
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
<Switch>
|
||||
|
||||
<Route path='/register'>
|
||||
<Auth />
|
||||
</Route>
|
||||
|
||||
<Route path='/login'>
|
||||
<Auth />
|
||||
</Route>
|
||||
|
||||
<PrivateRoute
|
||||
exact
|
||||
path='/'
|
||||
component={Dashboard}
|
||||
/>
|
||||
|
||||
</Switch>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A component to protect routes.
|
||||
* Shows Auth page if the user is not authenticated
|
||||
*/
|
||||
const PrivateRoute = ({ component, ...options }) => {
|
||||
|
||||
const session = getSession()
|
||||
|
||||
const finalComponent = session ? Dashboard : Home
|
||||
return <Route {...options} component={finalComponent} />
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.containerInner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
max-width: 700px;
|
||||
padding: 50px 15px 30px 15px;
|
||||
}
|
||||
|
||||
.containerLoading {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
padding: 160px 0 0 0;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroArtwork {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroArtwork img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.heroTitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroTitle img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.heroDescription {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
margin: 10px 0 32px 0px;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 18px 0;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: #FD5750;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.success {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 32px 0;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-transform: lowercase;
|
||||
color: #78f542;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.containerRegister {}
|
||||
|
||||
.containerSignIn {}
|
||||
|
||||
.socialButtons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
margin: 15px auto 0px auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttonGithub {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto 10px auto;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.buttonGithub:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.buttonGithub img {
|
||||
max-height: 28px;
|
||||
margin-right: 13px;
|
||||
margin-top: 1.5px;
|
||||
}
|
||||
|
||||
.buttonGoogle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.buttonGoogle:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.buttonGoogle img {
|
||||
max-height: 25px;
|
||||
margin-right: 11px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.formType {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0px auto 0px auto;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
font-size: 22px;
|
||||
color: rgba(255,255,255,0.6);
|
||||
text-transform: lowercase;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.formTypeRegister {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
padding: 5px 40px 5px 0;
|
||||
justify-content: flex-end;
|
||||
border-right: 2px solid rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.formTypeRegister:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.formTypeSignIn {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
padding: 5px 0 5px 40px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.formTypeSignIn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.formTypeActive {
|
||||
color: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 380px;
|
||||
width: 100%;
|
||||
margin: 10px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.formField {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
|
||||
.formLabel {
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 10px 0;
|
||||
user-select: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.formInput {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
padding: 10px 0px 22px 0px;
|
||||
border-bottom: 1.5px solid rgba(255,255,255,0.4);
|
||||
border-top: 1.5px solid transparent;
|
||||
border-right: 1.5px solid transparent;
|
||||
border-left: 1.5px solid transparent;
|
||||
transition: all 0.6s ease;
|
||||
background: none;
|
||||
outline: none;
|
||||
color: #ffffff;
|
||||
caret-color: white;
|
||||
}
|
||||
|
||||
.formInput::placeholder {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.formInput:focus {
|
||||
border-bottom: 1.5px solid rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.formError {
|
||||
margin: 10px 0 10px 0;
|
||||
color: #FD5750;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.formButton {
|
||||
margin: 16px auto 0 auto!important;
|
||||
}
|
||||
|
||||
.formButton:hover {
|
||||
cursor: pointer;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.formButton:active {
|
||||
cursor: pointer;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.forgotPassword {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin: 20px auto 5px auto;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
font-size: 19px;
|
||||
color: rgba(255,255,255,0.6);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.forgotPassword:hover {
|
||||
cursor: pointer;
|
||||
color: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.githubLink {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
width: 100%;
|
||||
margin: 26px 0 40px 0;
|
||||
}
|
||||
|
||||
.githubLink a {
|
||||
font-size: 20px;
|
||||
color: #FD5750;
|
||||
opacity: 0.7;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.githubLink:hover a {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
15
site/src/components/ReactCounter.jsx
Normal file
15
site/src/components/ReactCounter.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function ReactCounter() {
|
||||
const [count, setCount] = useState(0);
|
||||
const add = () => setCount((i) => i + 1);
|
||||
const subtract = () => setCount((i) => i - 1);
|
||||
|
||||
return (
|
||||
<div id="react" className="counter">
|
||||
<button onClick={subtract}>-</button>
|
||||
<pre>{count}</pre>
|
||||
<button onClick={add}>+</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
85
site/src/components/Tour.astro
Normal file
85
site/src/components/Tour.astro
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
import { Markdown } from 'astro/components';
|
||||
---
|
||||
<article>
|
||||
<div class="banner">
|
||||
<p><strong>🧑🚀 Seasoned astronaut?</strong> Delete this file. Have fun!</p>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<Markdown>
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```
|
||||
/
|
||||
├── public/
|
||||
│ ├── robots.txt
|
||||
│ └── favicon.ico
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ └── Tour.astro
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory.
|
||||
Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
</Markdown>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>👀 Want to learn more?</h2>
|
||||
<p>Feel free to check <a href="https://github.com/snowpackjs/astro">our documentation</a> or jump into our <a href="https://astro.build/chat">Discord server</a>.</p>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
|
||||
<style>
|
||||
article {
|
||||
padding-top: 2em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
section {
|
||||
margin-top: 2em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
.banner {
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
background: var(--color-light);
|
||||
padding: 1em 1.5em;
|
||||
padding-left: 0.75em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
background: var(--color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1em 1.5em;
|
||||
}
|
||||
|
||||
.tree {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
code:not(.tree) {
|
||||
padding: 0.125em;
|
||||
margin: 0 -0.125em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,32 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import styles from './Loading.module.css'
|
||||
|
||||
export default class Loading extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
async componentDidMount() {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={`${this.props.className}`}>
|
||||
<div className={`${styles.container}`}>
|
||||
|
||||
<img
|
||||
draggable={false}
|
||||
alt={`Loading`}
|
||||
src={
|
||||
'https://s3.amazonaws.com/dashboard.serverless.com/images/icon-serverless-framework.png'
|
||||
}
|
||||
/>
|
||||
|
||||
<p>loading...</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
width: auto;
|
||||
height: 100px;
|
||||
opacity: 0.45;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container img {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
margin: 0 0 20px 0;
|
||||
animation-duration: 0.4s;
|
||||
animation-name: pulse;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
animation-timing-function: ease;
|
||||
filter: none;
|
||||
-webkit-filter: grayscale(100%);
|
||||
-moz-filter: grayscale(100%);
|
||||
-ms-filter: grayscale(100%);
|
||||
-o-filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.container p {
|
||||
font-size: 22px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './Loading'
|
||||
@@ -1,123 +0,0 @@
|
||||
html, body, #root {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
color: #ffffff;
|
||||
margin: 0;
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
font-weight: 500;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
div, h1, h2, h3, h4, h5, h6, p, span, input, button, li {
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
font-size: 24px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links
|
||||
*/
|
||||
|
||||
a, .link {
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
color: rgba(255,255,255,0.6);
|
||||
text-decoration: none;
|
||||
transition: all 0.14s ease;
|
||||
}
|
||||
|
||||
a:hover, .link:hover {
|
||||
cursor: pointer;
|
||||
color: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
a:active, .link:active {
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buttons
|
||||
*/
|
||||
|
||||
.buttonPrimaryLarge {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
height: auto;
|
||||
margin: 20px 0 0 0;
|
||||
padding: 24px 24px 28px 24px;
|
||||
background: #fd5750;
|
||||
color: #fff;
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
border: 0px;
|
||||
transition: all 0.14s ease;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.buttonPrimaryLarge:hover {
|
||||
cursor: pointer;
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
.buttonPrimaryLarge:active {
|
||||
cursor: pointer;
|
||||
transform: scale(1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
|
||||
.animateFadeIn {
|
||||
animation: fadeIn 0.45s;
|
||||
}
|
||||
|
||||
.animateScaleIn {
|
||||
animation: scaleIn 0.35s;
|
||||
}
|
||||
|
||||
.animateFlicker {
|
||||
animation: flicker 2.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flicker {
|
||||
0% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
/**
|
||||
* Render App
|
||||
*/
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
@@ -1,249 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Link,
|
||||
withRouter,
|
||||
} from 'react-router-dom'
|
||||
import Loading from '../../fragments/Loading'
|
||||
import styles from './Auth.module.css'
|
||||
import {
|
||||
userRegister,
|
||||
userLogin,
|
||||
userGet,
|
||||
saveSession,
|
||||
} from '../../utils'
|
||||
|
||||
class Auth extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const pathName = window.location.pathname.replace('/', '')
|
||||
|
||||
this.state = {}
|
||||
this.state.state = pathName
|
||||
this.state.loading = true
|
||||
this.state.error = null
|
||||
this.state.formEmail = ''
|
||||
this.state.formPassword = ''
|
||||
|
||||
// Bindings
|
||||
this.handleFormInput = this.handleFormInput.bind(this)
|
||||
this.handleFormSubmit = this.handleFormSubmit.bind(this)
|
||||
this.handleFormTypeChange = this.handleFormTypeChange.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Component did mount
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
loading: false
|
||||
})
|
||||
|
||||
// Clear query params
|
||||
const url = document.location.href
|
||||
window.history.pushState({}, '', url.split('?')[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a form change
|
||||
*/
|
||||
handleFormTypeChange(type) {
|
||||
this.setState({ state: type },
|
||||
() => {
|
||||
this.props.history.push(`/${type}`)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle text changes within form fields
|
||||
*/
|
||||
handleFormInput(field, value) {
|
||||
value = value.trim()
|
||||
|
||||
const nextState = {}
|
||||
nextState[field] = value
|
||||
|
||||
this.setState(Object.assign(this.state, nextState))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles form submission
|
||||
* @param {object} evt
|
||||
*/
|
||||
async handleFormSubmit(evt) {
|
||||
evt.preventDefault()
|
||||
|
||||
this.setState({ loading: true })
|
||||
|
||||
// Validate email
|
||||
if (!this.state.formEmail) {
|
||||
return this.setState({
|
||||
loading: false,
|
||||
formError: 'email is required'
|
||||
})
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (!this.state.formPassword) {
|
||||
return this.setState({
|
||||
loading: false,
|
||||
formError: 'password is required'
|
||||
})
|
||||
}
|
||||
|
||||
let token
|
||||
try {
|
||||
if (this.state.state === 'register') {
|
||||
token = await userRegister(this.state.formEmail, this.state.formPassword)
|
||||
} else {
|
||||
token = await userLogin(this.state.formEmail, this.state.formPassword)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
if (error.message) {
|
||||
this.setState({
|
||||
formError: error.message,
|
||||
loading: false
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
formError: 'Sorry, something unknown went wrong. Please try again.',
|
||||
loading: false
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch user record and set session in cookie
|
||||
let user = await userGet(token.token)
|
||||
user = user.user
|
||||
saveSession(user.id, user.email, token.token)
|
||||
|
||||
window.location.replace('/')
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className={`${styles.container} animateFadeIn`}>
|
||||
<div className={styles.containerInner}>
|
||||
|
||||
{ /* Logo */}
|
||||
|
||||
<Link to='/' className={`${styles.logo}`}>
|
||||
<img
|
||||
draggable='false'
|
||||
src={'./fullstack-app-title.png'}
|
||||
alt='serverless-fullstack-application'
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{ /* Loading */}
|
||||
|
||||
{this.state.loading && (
|
||||
<div>
|
||||
{< Loading className={styles.containerLoading} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ /* Registration Form */}
|
||||
|
||||
{!this.state.loading && (
|
||||
<div className={styles.formType}>
|
||||
<div
|
||||
className={
|
||||
`${styles.formTypeRegister}
|
||||
${this.state.state === 'register' ? styles.formTypeActive : ''}`}
|
||||
onClick={(e) => { this.handleFormTypeChange('register') }}>
|
||||
Register
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`${styles.formTypeSignIn}
|
||||
${this.state.state === 'login' ? styles.formTypeActive : ''}`}
|
||||
onClick={(e) => { this.handleFormTypeChange('login') }}>
|
||||
Sign-In
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.state.state === 'register' && !this.state.loading && (
|
||||
<div className={styles.containerRegister}>
|
||||
|
||||
<form className={styles.form} onSubmit={this.handleFormSubmit}>
|
||||
<div className={styles.formField}>
|
||||
<label className={styles.formLabel}>email</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='yours@example.com'
|
||||
className={styles.formInput}
|
||||
value={this.state.formEmail}
|
||||
onChange={(e) => { this.handleFormInput('formEmail', e.target.value) }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formField}>
|
||||
<label className={styles.formLabel}>password</label>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='your password'
|
||||
className={styles.formInput}
|
||||
value={this.state.formPassword}
|
||||
onChange={(e) => { this.handleFormInput('formPassword', e.target.value) }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{this.state.formError && (
|
||||
<div className={styles.formError}>{this.state.formError}</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
className={`buttonPrimaryLarge ${styles.formButton}`}
|
||||
type='submit'
|
||||
value='Register'
|
||||
/>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.state.state === 'login' && !this.state.loading && (
|
||||
<div className={styles.containerSignIn}>
|
||||
|
||||
<form className={styles.form} onSubmit={this.handleFormSubmit}>
|
||||
<div className={styles.formField}>
|
||||
<label className={styles.formLabel}>email</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='yours@example.com'
|
||||
className={styles.formInput}
|
||||
value={this.state.formEmail}
|
||||
onChange={(e) => { this.handleFormInput('formEmail', e.target.value) }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formField}>
|
||||
<label className={styles.formLabel}>password</label>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='your password'
|
||||
className={styles.formInput}
|
||||
value={this.state.formPassword}
|
||||
onChange={(e) => { this.handleFormInput('formPassword', e.target.value) }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{this.state.formError && (
|
||||
<div className={styles.formError}>{this.state.formError}</div>
|
||||
)}
|
||||
|
||||
<input className={`buttonPrimaryLarge ${styles.formButton}`} type='submit' value='Sign In' />
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Auth)
|
||||
@@ -1,231 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.containerInner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
max-width: 700px;
|
||||
padding: 50px 15px 30px 15px;
|
||||
}
|
||||
|
||||
.containerLoading {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
padding: 160px 0 0 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 400px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 18px 0;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: #FD5750;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.success {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 32px 0;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-transform: lowercase;
|
||||
color: #78f542;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.containerRegister {}
|
||||
|
||||
.containerSignIn {}
|
||||
|
||||
.formType {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 26px auto 0px auto;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
font-size: 22px;
|
||||
color: rgba(255,255,255,0.6);
|
||||
text-transform: lowercase;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.formTypeRegister {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
padding: 5px 40px 5px 0;
|
||||
justify-content: flex-end;
|
||||
border-right: 2px solid rgba(255,255,255,0.15);
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
.formTypeRegister:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.formTypeSignIn {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
padding: 5px 0 5px 40px;
|
||||
text-align: left;
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
.formTypeSignIn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.formTypeActive {
|
||||
color: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 380px;
|
||||
width: 100%;
|
||||
margin: 10px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.formField {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
|
||||
.formLabel {
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
margin: 0 0 10px 0;
|
||||
user-select: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.formInput {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
padding: 10px 0px 22px 0px;
|
||||
border-bottom: 1.5px solid rgba(255,255,255,0.4);
|
||||
border-top: 1.5px solid transparent;
|
||||
border-right: 1.5px solid transparent;
|
||||
border-left: 1.5px solid transparent;
|
||||
transition: all 0.6s ease;
|
||||
background: none;
|
||||
outline: none;
|
||||
color: #ffffff;
|
||||
caret-color: white;
|
||||
}
|
||||
|
||||
.formInput::placeholder {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.formInput:focus {
|
||||
border-bottom: 1.5px solid rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.formError {
|
||||
margin: 10px 0 10px 0;
|
||||
color: #FD5750;
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.formButton {
|
||||
margin: 16px auto 0 auto!important;
|
||||
}
|
||||
|
||||
.formButton:hover {
|
||||
cursor: pointer;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.formButton:active {
|
||||
cursor: pointer;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.forgotPassword {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin: 20px auto 5px auto;
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
font-size: 19px;
|
||||
color: rgba(255,255,255,0.6);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.forgotPassword:hover {
|
||||
cursor: pointer;
|
||||
color: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.githubLink {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
width: 100%;
|
||||
margin: 26px 0 40px 0;
|
||||
}
|
||||
|
||||
.githubLink a {
|
||||
font-size: 20px;
|
||||
color: #FD5750;
|
||||
opacity: 0.7;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.githubLink:hover a {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
withRouter
|
||||
} from 'react-router-dom'
|
||||
import styles from './Dashboard.module.css'
|
||||
import {
|
||||
getSession,
|
||||
deleteSession
|
||||
} from '../../utils'
|
||||
|
||||
class Dashboard extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
|
||||
// Bindings
|
||||
this.logout = this.logout.bind(this)
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
|
||||
const userSession = getSession()
|
||||
|
||||
this.setState({
|
||||
session: userSession,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user out by clearing cookie and redirecting
|
||||
*/
|
||||
logout() {
|
||||
deleteSession()
|
||||
this.props.history.push(`/`)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className={`${styles.container} animateFadeIn`}>
|
||||
<div className={styles.containerInner}>
|
||||
|
||||
{ /* Navigation */ }
|
||||
|
||||
<div className={styles.navigationContainer}>
|
||||
<div
|
||||
className={`link`}>
|
||||
{ this.state.session ? this.state.session.userEmail : '' }
|
||||
</div>
|
||||
<div
|
||||
className={`link`}
|
||||
onClick={this.logout}>
|
||||
logout
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ /* Content */ }
|
||||
|
||||
<div className={`${styles.contentContainer}`}>
|
||||
|
||||
<div className={`${styles.artwork} animateFlicker`}>
|
||||
<img
|
||||
draggable='false'
|
||||
src={'./fullstack-app-artwork.png'}
|
||||
alt='serverless-fullstack-application'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.welcomeMessage}`}>
|
||||
Welcome to your serverless fullstack dashboard...
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Dashboard)
|
||||
@@ -1,92 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.containerInner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 0 30px 0;
|
||||
}
|
||||
|
||||
.navigationContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-content: center;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
padding: 25px 45px 0 45px;
|
||||
}
|
||||
|
||||
.navigationContainer div {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 7% 15px 45px 15px;
|
||||
}
|
||||
|
||||
.welcomeMessage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.artwork {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.artwork img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special
|
||||
*/
|
||||
|
||||
::-moz-selection {
|
||||
background: #fd5750;
|
||||
color: #ffffff;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #fd5750;
|
||||
color: #ffffff;
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Link,
|
||||
withRouter
|
||||
} from 'react-router-dom'
|
||||
import styles from './Home.module.css'
|
||||
|
||||
class Home extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
async componentDidMount() { }
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className={`${styles.container} animateFadeIn`}>
|
||||
<div className={styles.containerInner}>
|
||||
|
||||
{ /* Hero Artwork */}
|
||||
|
||||
<div className={`${styles.heroArtwork} animateFlicker`}>
|
||||
<img
|
||||
draggable='false'
|
||||
src={'./fullstack-app-artwork.png'}
|
||||
alt='serverless-fullstack-application'
|
||||
/>
|
||||
</div>
|
||||
<div className={`${styles.heroTitle}`}>
|
||||
<img
|
||||
draggable='false'
|
||||
src={'./fullstack-app-title.png'}
|
||||
alt='serverless-fullstack-application'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{ /* Hero Description */}
|
||||
|
||||
<div className={`${styles.heroDescription}`}>
|
||||
A serverless full-stack application built with AWS Lambda, AWS HTTP API, Express.js, React & AWS DynamoDB.
|
||||
</div>
|
||||
|
||||
{ /* Call To Action */}
|
||||
|
||||
<div className={`${styles.containerCta}`}>
|
||||
|
||||
<Link to='/register'>
|
||||
<button className={`buttonPrimaryLarge`}>
|
||||
Register
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
<Link to='/login' className={`${styles.linkSignIn}`}>sign-in</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Home)
|
||||
@@ -1,105 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.containerInner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
max-width: 700px;
|
||||
padding: 50px 15px 30px 15px;
|
||||
}
|
||||
|
||||
.heroArtwork {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroArtwork img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.heroTitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroTitle img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.heroDescription {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
line-height: 38px;
|
||||
margin: 32px 0px 52px 0;
|
||||
}
|
||||
|
||||
.containerCta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.containerCta button {
|
||||
margin: 0px auto 20px auto;
|
||||
}
|
||||
|
||||
.containerCta .linkSignIn {
|
||||
padding: 10px;
|
||||
color: #fd5750;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.containerCta .linkSignIn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special
|
||||
*/
|
||||
|
||||
::-moz-selection {
|
||||
background: #fd5750;
|
||||
color: #ffffff;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #fd5750;
|
||||
color: #ffffff;
|
||||
opacity: 1;
|
||||
}
|
||||
58
site/src/pages/index.astro
Normal file
58
site/src/pages/index.astro
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
// Component Imports
|
||||
import Tour from '../components/Tour.astro';
|
||||
// You can import components from any supported Framework here!
|
||||
import ReactCounter from '../components/ReactCounter.jsx';
|
||||
|
||||
// Component Script:
|
||||
// You can write any JavaScript/TypeScript that you'd like here.
|
||||
// It will run during the build, but never in the browser.
|
||||
// All variables are available to use in the HTML template below.
|
||||
let title = 'My Astro Site';
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
// https://docs.astro.build/core-concepts/astro-components/
|
||||
---
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>{title}</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="/style/global.css">
|
||||
<link rel="stylesheet" href="/style/home.css">
|
||||
|
||||
<style>
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
max-width: min(100%, 68ch);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<div>
|
||||
<img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
|
||||
<h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<Tour />
|
||||
|
||||
<!--
|
||||
- You can also use imported framework components directly in your markup!
|
||||
-
|
||||
- Note: by default, these components are NOT interactive on the client.
|
||||
- The `:visible` directive tells Astro to make it interactive.
|
||||
-
|
||||
- See https://docs.astro.build/core-concepts/component-hydration/
|
||||
-->
|
||||
<ReactCounter client:visible />
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,76 +0,0 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
/**
|
||||
* Format Org and Username correctly for the Serverless Platform backend
|
||||
*/
|
||||
export const formatOrgAndUsername = (name = '') => {
|
||||
name = name.toString().toLowerCase().replace(/[^a-z\d-]+/gi, '-')
|
||||
// Remove multiple instances of hyphens
|
||||
name = name.replace(/-{2,}/g, '-')
|
||||
if (name.length > 40) {
|
||||
name = name.substring(0, 40)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse query parameters in a URL
|
||||
* @param {*} searchString
|
||||
*/
|
||||
export const parseQueryParams = (searchString = null) => {
|
||||
if (!searchString) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Clone string
|
||||
let clonedParams = (' ' + searchString).slice(1)
|
||||
|
||||
return clonedParams
|
||||
.substr(1)
|
||||
.split('&')
|
||||
.filter((el) => el.length)
|
||||
.map((el) => el.split('='))
|
||||
.reduce(
|
||||
(accumulator, currentValue) =>
|
||||
Object.assign(accumulator, {
|
||||
[decodeURIComponent(currentValue.shift())]: decodeURIComponent(currentValue.pop())
|
||||
}),
|
||||
{}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse hash fragment parameters in a URL
|
||||
*/
|
||||
export const parseHashFragment = (hashString) => {
|
||||
const hashData = {}
|
||||
let hash = decodeURI(hashString)
|
||||
hash = hash.split('&')
|
||||
hash.forEach((val) => {
|
||||
val = val.replace('#', '')
|
||||
hashData[val.split('=')[0]] = val.split('=')[1]
|
||||
})
|
||||
return hashData
|
||||
}
|
||||
|
||||
/**
|
||||
* Save session in browser cookie
|
||||
*/
|
||||
export const saveSession = (userId, userEmail, userToken) => {
|
||||
Cookies.set('serverless', { userId, userEmail, userToken })
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session in browser cookie
|
||||
*/
|
||||
export const getSession = () => {
|
||||
const data = Cookies.get('serverless')
|
||||
return data ? JSON.parse(data) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete session in browser cookie
|
||||
*/
|
||||
export const deleteSession = () => {
|
||||
Cookies.remove('serverless')
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
export * from './helpers'
|
||||
export * from './api'
|
||||
Reference in New Issue
Block a user