A start on a frontend application in React.
This commit is contained in:
parent
d80f705b5d
commit
56d0889963
99
backend/Cargo.lock
generated
99
backend/Cargo.lock
generated
@ -213,6 +213,12 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-sha1"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -551,9 +557,11 @@ dependencies = [
|
|||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
|
"local-ip-address",
|
||||||
"password-hash",
|
"password-hash",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"rocket",
|
"rocket",
|
||||||
|
"rocket_cors",
|
||||||
"rocket_dyn_templates",
|
"rocket_dyn_templates",
|
||||||
"rocket_sync_db_pools",
|
"rocket_sync_db_pools",
|
||||||
"serde",
|
"serde",
|
||||||
@ -861,6 +869,19 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "local-ip-address"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b143c6ef86e36328caa40a7578e95d1544aca8a1740235fd2b416a69441a5c7"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"memalloc",
|
||||||
|
"neli",
|
||||||
|
"thiserror",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -915,6 +936,12 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memalloc"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@ -1034,6 +1061,16 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "neli"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "net2"
|
name = "net2"
|
||||||
version = "0.2.37"
|
version = "0.2.37"
|
||||||
@ -1534,6 +1571,22 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_cors"
|
||||||
|
version = "0.6.0-alpha1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f5aa2c9cdb5dabbbf38bd4fb0038844cc47598399fed70fc938185679154793"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"rocket",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"unicase",
|
||||||
|
"unicase_serde",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_dyn_templates"
|
name = "rocket_dyn_templates"
|
||||||
version = "0.1.0-rc.1"
|
version = "0.1.0-rc.1"
|
||||||
@ -2177,6 +2230,25 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase_serde"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@ -2417,6 +2489,33 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68088239696c06152844eadc03d262f088932cce50c67e4ace86e19d95e976fe"
|
||||||
|
dependencies = [
|
||||||
|
"const-sha1",
|
||||||
|
"windows_gen",
|
||||||
|
"windows_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_gen"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf583322dc423ee021035b358e535015f7fd163058a31e2d37b99a939141121d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_macros"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58acfb8832e9f707f8997bd161e537a1c1f603e60a5bd9c3cf53484fdcc998f3"
|
||||||
|
dependencies = [
|
||||||
|
"syn",
|
||||||
|
"windows_gen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "ws2_32-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -21,4 +21,5 @@ rand_core = { version = "0.6", features = ["std"] }
|
|||||||
diesel-derive-enum = { version = "1.1", features = ["sqlite"] }
|
diesel-derive-enum = { version = "1.1", features = ["sqlite"] }
|
||||||
jsonwebtoken = "8.1"
|
jsonwebtoken = "8.1"
|
||||||
validator = { version = "0.14", features = ["derive"] }
|
validator = { version = "0.14", features = ["derive"] }
|
||||||
|
rocket_cors = "0.6.0-alpha1"
|
||||||
|
local-ip-address = "0.4"
|
||||||
|
@ -45,13 +45,12 @@ fn rocket() -> _ {
|
|||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
.attach(AdHoc::on_ignite("Run Migrations", schema::run_migrations))
|
.attach(AdHoc::on_ignite("Run Migrations", schema::run_migrations))
|
||||||
.attach(AdHoc::config::<AppConfig>())
|
.attach(AdHoc::config::<AppConfig>())
|
||||||
|
.attach(site::make_cors())
|
||||||
.mount(
|
.mount(
|
||||||
"/",
|
"/",
|
||||||
routes![
|
routes![
|
||||||
site::index,
|
site::index,
|
||||||
site::gamenights,
|
site::files
|
||||||
site::add_game_night,
|
|
||||||
site::register
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.mount(
|
.mount(
|
||||||
|
@ -1,90 +1,43 @@
|
|||||||
use crate::schema;
|
use rocket::fs::NamedFile;
|
||||||
use rocket::request::FlashMessage;
|
use rocket::http::Method;
|
||||||
use rocket::response::Redirect;
|
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors, CorsOptions};
|
||||||
use rocket_dyn_templates::Template;
|
use std::io;
|
||||||
use serde::{Deserialize, Serialize};
|
use std::path::{Path, PathBuf};
|
||||||
use std::borrow::Cow;
|
use local_ip_address::local_ip;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct FlashData {
|
pub fn make_cors() -> Cors {
|
||||||
has_data: bool,
|
let allowed_origins = AllowedOrigins::some_exact(&[
|
||||||
kind: Cow<'static, str>,
|
"http://localhost:3000",
|
||||||
message: Cow<'static, str>,
|
"http://127.0.0.1:3000",
|
||||||
|
&format!("http://{}:8000",local_ip().unwrap())[..],
|
||||||
|
"http://localhost:8000",
|
||||||
|
"http://0.0.0.0:8000",
|
||||||
|
]);
|
||||||
|
|
||||||
|
CorsOptions {
|
||||||
|
allowed_origins,
|
||||||
|
allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(), // 1.
|
||||||
|
allowed_headers: AllowedHeaders::some(&[
|
||||||
|
"Authorization",
|
||||||
|
"Accept",
|
||||||
|
"Access-Control-Allow-Origin",
|
||||||
|
]),
|
||||||
|
allow_credentials: true,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.to_cors()
|
||||||
|
.expect("error while building CORS")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlashData {
|
#[get("/<file..>", rank = 10)]
|
||||||
const EMPTY: Self = Self {
|
pub async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||||
has_data: false,
|
NamedFile::open(Path::new("../frontend/build/").join(file))
|
||||||
message: Cow::Borrowed(""),
|
.await
|
||||||
kind: Cow::Borrowed(""),
|
.ok()
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct GameNightsData {
|
|
||||||
gamenights: Vec<schema::GameNight>,
|
|
||||||
flash: FlashData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/gamenights")]
|
|
||||||
pub async fn gamenights(conn: schema::DbConn) -> Template {
|
|
||||||
let gamenights = schema::get_all_gamenights(conn).await;
|
|
||||||
|
|
||||||
let data = GameNightsData {
|
|
||||||
gamenights: gamenights,
|
|
||||||
flash: FlashData::EMPTY,
|
|
||||||
};
|
|
||||||
|
|
||||||
Template::render("gamenights", &data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub async fn index() -> Redirect {
|
pub async fn index() -> io::Result<NamedFile> {
|
||||||
Redirect::to(uri!(gamenights))
|
NamedFile::open("../frontend/build/index.html").await
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct GameNightAddData {
|
|
||||||
post_url: String,
|
|
||||||
flash: FlashData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/gamenight/add")]
|
|
||||||
pub async fn add_game_night(flash: Option<FlashMessage<'_>>) -> Template {
|
|
||||||
let flash_data = match flash {
|
|
||||||
None => FlashData::EMPTY,
|
|
||||||
Some(flash) => FlashData {
|
|
||||||
has_data: true,
|
|
||||||
message: Cow::Owned(flash.message().to_string()),
|
|
||||||
kind: Cow::Owned(flash.kind().to_string()),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = GameNightAddData {
|
|
||||||
post_url: "/api/gamenight".to_string(),
|
|
||||||
flash: flash_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
Template::render("gamenight_add", &data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct RegisterData {
|
|
||||||
flash: FlashData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/register")]
|
|
||||||
pub async fn register(flash: Option<FlashMessage<'_>>) -> Template {
|
|
||||||
let flash_data = match flash {
|
|
||||||
None => FlashData::EMPTY,
|
|
||||||
Some(flash) => FlashData {
|
|
||||||
has_data: true,
|
|
||||||
message: Cow::Owned(flash.message().to_string()),
|
|
||||||
kind: Cow::Owned(flash.kind().to_string()),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = RegisterData { flash: flash_data };
|
|
||||||
|
|
||||||
Template::render("register", &data)
|
|
||||||
}
|
}
|
||||||
|
1571
frontend/package-lock.json
generated
1571
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,11 @@
|
|||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"watch": "npm-watch"
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"build": "src/"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
@ -34,5 +38,8 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"npm-watch": "^0.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>It's gamenight</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
@ -28,6 +28,19 @@
|
|||||||
color: #61dafb;
|
color: #61dafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset label {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit] {
|
||||||
|
margin:5px;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
@keyframes App-logo-spin {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
@ -1,25 +1,99 @@
|
|||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import React from 'react';
|
||||||
|
import MenuBar from './components/MenuBar';
|
||||||
|
import Login from './components/Login';
|
||||||
|
import Gamenights from "./components/Gamenights"
|
||||||
|
|
||||||
function App() {
|
class App extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.handleLogin = this.handleLogin.bind(this);
|
||||||
|
this.onLogout = this.onLogout.bind(this);
|
||||||
|
this.state = {
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
gamenights: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
console.log("component update?")
|
||||||
|
if (prevState.token !== this.state.token) {
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Authorization': `bearer: ${this.state.token}` },
|
||||||
|
};
|
||||||
|
fetch('api/gamenights', requestOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if(data.result === "Ok") {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
gamenights: data.gamenights
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
flash_data: {
|
||||||
|
type: "Error",
|
||||||
|
message: data.message
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
console.log(this.state)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if(this.state.token === null) {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<header className="App-header">
|
<Login onChange={this.handleLogin}/>
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
|
||||||
<p>
|
|
||||||
Edit <code>src/App.js</code> and save to reload.
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
className="App-link"
|
|
||||||
href="https://reactjs.org"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<MenuBar user={this.state.user} onLogout={this.onLogout}/>
|
||||||
|
<Gamenights />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogout() {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
flash_data: null
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogin(input) {
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(input)
|
||||||
|
};
|
||||||
|
fetch('api/login', requestOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if(data.result === "Ok") {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
token: data.jwt
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
flash_data: {
|
||||||
|
type: "Error",
|
||||||
|
message: data.message
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
console.log(this.state)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
21
frontend/src/components/Gamenights.jsx
Normal file
21
frontend/src/components/Gamenights.jsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class Gamenights extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let gamenights = this.props.gamenights.map(g =>
|
||||||
|
(<li>{g.name}</li>)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
{gamenights}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
56
frontend/src/components/Login.jsx
Normal file
56
frontend/src/components/Login.jsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class Login extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleUsernameChange = this.handleUsernameChange.bind(this);
|
||||||
|
this.handlePasswordChange = this.handlePasswordChange.bind(this);
|
||||||
|
this.handleLogin = this.handleLogin.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="Login-Component">
|
||||||
|
<form onSubmit={this.handleLogin}>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Login</legend>
|
||||||
|
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input id="username" name="username" type="text"
|
||||||
|
value={this.state.username}
|
||||||
|
onChange={this.handleUsernameChange} />
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input id="password" name="password" type="password"
|
||||||
|
value={this.state.password}
|
||||||
|
onChange={this.handlePasswordChange} />
|
||||||
|
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUsernameChange(event) {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
username: event.target.value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePasswordChange(event) {
|
||||||
|
this.setState((state, props) => ({
|
||||||
|
password: event.target.value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogin(event) {
|
||||||
|
this.props.onChange({ username: this.state.username, password: this.state.password });
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
25
frontend/src/components/MenuBar.jsx
Normal file
25
frontend/src/components/MenuBar.jsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class MenuBar extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a>Gamenight</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a>User</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={this.props.onLogout}>Logout</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user