Switch to site built with astro
18
site/.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# build output
|
||||||
|
dist
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
.snowpack/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
2
site/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
## force pnpm to hoist
|
||||||
|
shamefully-hoist = true
|
||||||
6
site/.stackblitzrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"startCommand": "npm start",
|
||||||
|
"env": {
|
||||||
|
"ENABLE_CJS_IMPORTS": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,68 +1,42 @@
|
|||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
# Welcome to [Astro](https://astro.build)
|
||||||
|
|
||||||
## Available Scripts
|
[](https://stackblitz.com/github/snowpackjs/astro/tree/latest/examples/starter)
|
||||||
|
|
||||||
In the project directory, you can run:
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
### `yarn start`
|
## 🚀 Project Structure
|
||||||
|
|
||||||
Runs the app in the development mode.<br />
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.<br />
|
```
|
||||||
You will also see any lint errors in the console.
|
/
|
||||||
|
├── public/
|
||||||
|
│ ├── robots.txt
|
||||||
|
│ └── favicon.ico
|
||||||
|
├── src/
|
||||||
|
│ ├── components/
|
||||||
|
│ │ └── Tour.astro
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
### `yarn test`
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.<br />
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `yarn build`
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.<br />
|
## 🧞 Commands
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.<br />
|
All commands are run from the root of the project, from a terminal:
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
| Command | Action |
|
||||||
|
|:----------------|:--------------------------------------------|
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
|
||||||
### `yarn eject`
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
Feel free to check [our documentation](https://github.com/snowpackjs/astro) or jump into our [Discord server](https://astro.build/chat).
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
||||||
|
|
||||||
### Code Splitting
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
|
|
||||||
|
|
||||||
### Analyzing the Bundle Size
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
|
|
||||||
|
|
||||||
### Making a Progressive Web App
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
|
|
||||||
|
|
||||||
### Advanced Configuration
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
|
|
||||||
|
|
||||||
### Deployment
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
|
|
||||||
|
|
||||||
### `yarn build` fails to minify
|
|
||||||
|
|
||||||
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
|
|
||||||
|
|||||||
18
site/astro.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export default {
|
||||||
|
// projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project.
|
||||||
|
// pages: './src/pages', // Path to Astro components, pages, and data
|
||||||
|
// dist: './dist', // When running `astro build`, path to final static output
|
||||||
|
// public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don’t need processing.
|
||||||
|
buildOptions: {
|
||||||
|
// site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs.
|
||||||
|
sitemap: true, // Generate sitemap (set to "false" to disable)
|
||||||
|
},
|
||||||
|
devOptions: {
|
||||||
|
// hostname: 'localhost', // The hostname to run the dev server on.
|
||||||
|
// port: 3000, // The port to run the dev server on.
|
||||||
|
// tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js'
|
||||||
|
},
|
||||||
|
renderers: [
|
||||||
|
"@astrojs/renderer-react"
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -1,37 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "serverless-fullstack-app-website",
|
"name": "@example/starter",
|
||||||
"version": "0.1.0",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
|
||||||
"@testing-library/react": "^9.3.2",
|
|
||||||
"@testing-library/user-event": "^7.1.2",
|
|
||||||
"react": "^16.13.1",
|
|
||||||
"react-dom": "^16.13.1",
|
|
||||||
"react-scripts": "3.4.3",
|
|
||||||
"js-cookie": "^2.2.1",
|
|
||||||
"moment": "^2.24.0",
|
|
||||||
"react-router-dom": "^5.1.2"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"dev": "astro dev",
|
||||||
"build": "react-scripts build",
|
"start": "astro dev",
|
||||||
"test": "react-scripts test",
|
"build": "astro build",
|
||||||
"eject": "react-scripts eject"
|
"preview": "astro preview"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"devDependencies": {
|
||||||
"extends": "react-app"
|
"astro": "^0.20.7",
|
||||||
},
|
"@astrojs/renderer-react": "^0.2.1"
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 11 KiB |
12
site/public/assets/logo.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg width="193" height="256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style>
|
||||||
|
#flame { fill: #FF5D01; }
|
||||||
|
#a { fill: #000014; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
#a { fill: #fff; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<path id="a" fill-rule="evenodd" clip-rule="evenodd" d="M131.496 18.929c1.943 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53L99.746 60.56a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.224 180.224 0 00-52.01 17.557l43.52-142.281c1.989-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.085 1.157a16 16 0 016.488 4.806z" fill="url(#paint0_linear)"/>
|
||||||
|
<path id="flame" fill-rule="evenodd" clip-rule="evenodd" d="M136.678 180.151c-7.14 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.962 10.367-1.962 13.902 0 0-1.055 17.355 11.016 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.973-19.87 5.977-3.79 12.616-8.001 17.192-16.449a31.013 31.013 0 003.744-14.82c0-3.299-.513-6.479-1.463-9.463z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 84 B |
@@ -1,22 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"
|
|
||||||
/>
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta name="description" content="A site that does bookmarks the right way"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<title>Forget Me Not</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Forget Me Not",
|
|
||||||
"name": "Forget Me Not",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "android-chrome-192x192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "android-chrome-512x512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow:
|
Disallow: /
|
||||||
|
|||||||
28
site/public/style/global.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||||
|
font-size: 1rem;
|
||||||
|
--user-font-scale: 1rem - 16px;
|
||||||
|
font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background: #111827;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
site/public/style/home.css
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
:root {
|
||||||
|
--font-mono: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono',
|
||||||
|
'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
|
||||||
|
--color-light: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--color-light: #1f2937;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div {
|
||||||
|
font-size: clamp(2rem, -0.4742rem + 6.1856vw, 2.75rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
header img {
|
||||||
|
width: 2em;
|
||||||
|
height: 2.667em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: clamp(1.5rem, 1rem + 1.25vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
gap: 1em;
|
||||||
|
font-size: 2rem;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter > pre {
|
||||||
|
text-align: center;
|
||||||
|
min-width: 3ch;
|
||||||
|
}
|
||||||
11
site/sandbox.config.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"infiniteLoopProtection": true,
|
||||||
|
"hardReloadOnChange": false,
|
||||||
|
"view": "browser",
|
||||||
|
"template": "node",
|
||||||
|
"container": {
|
||||||
|
"port": 3000,
|
||||||
|
"startScript": "start",
|
||||||
|
"node": "14"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,6 @@ inputs:
|
|||||||
src:
|
src:
|
||||||
src: ./
|
src: ./
|
||||||
hook: npm run build
|
hook: npm run build
|
||||||
dist: build
|
dist: dist
|
||||||
# Enable this when you want to set a custom domain.
|
# Enable this when you want to set a custom domain.
|
||||||
# domain: ${env:domain}
|
# domain: ${env:domain}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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'
|
export * from './api'
|
||||||
3
site/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"moduleResolution": "node"
|
||||||
|
}
|
||||||