added change password, style updates, api updates
This commit is contained in:
parent
14a3c0bab1
commit
6617160c75
@ -12,6 +12,7 @@
|
|||||||
# below:
|
# below:
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
|
.vscode
|
||||||
|
|
||||||
venv
|
venv
|
||||||
node_modules
|
node_modules
|
||||||
@ -19,4 +20,4 @@ node_modules
|
|||||||
# Python pycache:
|
# Python pycache:
|
||||||
__pycache__/
|
__pycache__/
|
||||||
# Ignored by the build system
|
# Ignored by the build system
|
||||||
/setup.cfg
|
/setup.cfg
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ node_modules/
|
|||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from flask import Blueprint, session, request, jsonify
|
from flask import Blueprint, session, request, jsonify
|
||||||
from google.cloud import firestore
|
from google.cloud import firestore
|
||||||
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
blueprint = Blueprint('api', __name__)
|
blueprint = Blueprint('api', __name__)
|
||||||
db = firestore.Client()
|
db = firestore.Client()
|
||||||
@ -20,17 +21,64 @@ def get_playlists():
|
|||||||
error = {'error': 'multiple usernames?'}
|
error = {'error': 'multiple usernames?'}
|
||||||
return jsonify(error), 500
|
return jsonify(error), 500
|
||||||
|
|
||||||
docs = playlists.stream()
|
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'playlists': [i.to_dict() for i in docs]
|
'playlists': [i.to_dict() for i in playlists.stream()]
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonify(response)
|
return jsonify(response), 200
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error = {'error': 'username not in session, not logged in?'}
|
error = {'error': 'not logged in'}
|
||||||
return jsonify(error), 500
|
return jsonify(error), 401
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/playlist', methods=['GET', 'POST', 'PUT'])
|
||||||
|
def get_playlist():
|
||||||
|
|
||||||
|
if 'username' in session:
|
||||||
|
|
||||||
|
users = db.collection(u'spotify_users').where(u'username', u'==', session['username']).stream()
|
||||||
|
|
||||||
|
users = [i for i in users]
|
||||||
|
|
||||||
|
if len(users) == 1:
|
||||||
|
playlists = db.document(u'spotify_users/{}'.format(users[0].id)).collection(u'playlists')
|
||||||
|
else:
|
||||||
|
error = {'error': 'multiple usernames?'}
|
||||||
|
return jsonify(error), 500
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
playlist_name = request.args.get('name', None)
|
||||||
|
|
||||||
|
if playlist_name:
|
||||||
|
|
||||||
|
playlist = [i for i in playlists.where(u'name', u'==', playlist_name).stream()]
|
||||||
|
|
||||||
|
if len(playlist) == 0:
|
||||||
|
return jsonify({'error': 'no playlist found'}), 404
|
||||||
|
elif len(playlist) > 1:
|
||||||
|
return jsonify({'error': 'multiple playlists found'}), 500
|
||||||
|
|
||||||
|
return jsonify(playlist[0].to_dict()), 200
|
||||||
|
|
||||||
|
else:
|
||||||
|
response = {"error": 'no name requested'}
|
||||||
|
return jsonify(response), 400
|
||||||
|
|
||||||
|
elif request.method == 'POST' or request.method == 'PUT':
|
||||||
|
|
||||||
|
request_json = request.get_json()
|
||||||
|
|
||||||
|
if 'name' not in request_json:
|
||||||
|
return jsonify({'error': "no name provided"}), 400
|
||||||
|
|
||||||
|
playlist_name = request_json['name']
|
||||||
|
|
||||||
|
return 404
|
||||||
|
|
||||||
|
else:
|
||||||
|
error = {'error': 'not logged in'}
|
||||||
|
return jsonify(error), 401
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/user', methods=['GET'])
|
@blueprint.route('/user', methods=['GET'])
|
||||||
@ -55,13 +103,59 @@ def user():
|
|||||||
'validated': doc['validated']
|
'validated': doc['validated']
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonify(response)
|
return jsonify(response), 200
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error = {'error': 'username not in session, not logged in?'}
|
error = {'error': 'not logged in'}
|
||||||
return jsonify(error), 404
|
return jsonify(error), 401
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/user/password', methods=['POST'])
|
||||||
|
def change_password():
|
||||||
|
|
||||||
|
request_json = request.get_json()
|
||||||
|
|
||||||
|
if 'username' in session:
|
||||||
|
|
||||||
|
if 'new_password' in request_json and 'current_password' in request_json:
|
||||||
|
|
||||||
|
if len(request_json['new_password']) == 0:
|
||||||
|
response = {"error": 'zero length password'}
|
||||||
|
return jsonify(response), 400
|
||||||
|
|
||||||
|
if len(request_json['new_password']) > 30:
|
||||||
|
response = {"error": 'password too long'}
|
||||||
|
return jsonify(response), 400
|
||||||
|
|
||||||
|
users = db.collection(u'spotify_users').where(u'username', u'==', session['username']).stream()
|
||||||
|
users = [i for i in users]
|
||||||
|
|
||||||
|
if len(users) == 1:
|
||||||
|
current_user = db.collection(u'spotify_users').document(u'{}'.format(users[0].id))
|
||||||
|
else:
|
||||||
|
error = {'error': 'multiple usernames?'}
|
||||||
|
return jsonify(error), 500
|
||||||
|
|
||||||
|
if check_password_hash(current_user.get().to_dict()['password'], request_json['current_password']):
|
||||||
|
|
||||||
|
current_user.update({'password': generate_password_hash(request_json['new_password'])})
|
||||||
|
|
||||||
|
response = {"message": 'password changed', "status": "success"}
|
||||||
|
return jsonify(response), 200
|
||||||
|
|
||||||
|
else:
|
||||||
|
error = {'error': 'wrong password provided'}
|
||||||
|
return jsonify(error), 403
|
||||||
|
|
||||||
|
else:
|
||||||
|
error = {'error': 'malformed request, no old_password/new_password'}
|
||||||
|
return jsonify(error), 400
|
||||||
|
|
||||||
|
else:
|
||||||
|
error = {'error': 'not logged in'}
|
||||||
|
return jsonify(error), 401
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/playlist', methods=['GET', 'PUT', 'POST'])
|
@blueprint.route('/playlist', methods=['GET', 'PUT', 'POST'])
|
||||||
def playlist():
|
def playlist():
|
||||||
return 404
|
return 401
|
||||||
|
@ -21,7 +21,11 @@ def login():
|
|||||||
|
|
||||||
users = [i for i in users]
|
users = [i for i in users]
|
||||||
|
|
||||||
if len(users) != 1:
|
if len(users) == 0:
|
||||||
|
flash('user not found')
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
if len(users) > 1:
|
||||||
flash('multiple users found')
|
flash('multiple users found')
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@ -69,9 +69,9 @@ def token():
|
|||||||
return redirect('/app')
|
return redirect('/app')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/app')
|
@app.route('/app', defaults={'path': ''})
|
||||||
@app.route('/app/<path>')
|
@app.route('/app/<path:path>')
|
||||||
def app_route(path = None):
|
def app_route(path):
|
||||||
|
|
||||||
if 'username' not in session:
|
if 'username' not in session:
|
||||||
flash('please log in')
|
flash('please log in')
|
||||||
|
13
src/js/Admin/Admin.js
Normal file
13
src/js/Admin/Admin.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
class Admin extends Component {
|
||||||
|
render(){
|
||||||
|
return (
|
||||||
|
<p>admin</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Admin
|
7
src/js/Error/NotFound.js
Normal file
7
src/js/Error/NotFound.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function NotFound (props) {
|
||||||
|
return <p style={{textAlign: "center"}}>404: Path Not Found</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFound;
|
@ -5,10 +5,8 @@ class Index extends Component{
|
|||||||
|
|
||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {}
|
||||||
|
// this.pingPlaylists();
|
||||||
}
|
|
||||||
this.pingPlaylists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pingPlaylists(){
|
pingPlaylists(){
|
@ -6,8 +6,9 @@ class PlaylistView extends Component{
|
|||||||
|
|
||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
|
console.log(this.props);
|
||||||
this.state = {
|
this.state = {
|
||||||
name: props.name
|
name: this.props.match.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,58 +2,21 @@ import React, { Component } from "react";
|
|||||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
|
import PlaylistsView from "./PlaylistsView.js"
|
||||||
|
|
||||||
class Playlists extends Component {
|
class Playlists extends Component {
|
||||||
|
render(){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ul className="navbar" style={{width: "100%"}}>
|
||||||
|
<li><Link to={`${this.props.match.url}/add`}>add</Link></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Route path={`${this.props.match.url}/`} component={PlaylistsView} />
|
||||||
|
|
||||||
constructor(props){
|
</div>
|
||||||
super(props);
|
);
|
||||||
this.state = {
|
|
||||||
isLoading: true
|
|
||||||
}
|
|
||||||
this.getPlaylists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaylists(){
|
|
||||||
var self = this;
|
|
||||||
axios.get('/api/playlists')
|
|
||||||
.then((response) => {
|
|
||||||
self.setState({
|
|
||||||
playlists: response.data.playlists,
|
|
||||||
isLoading: false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const table = <div><Table playlists={this.state.playlists}/></div>;
|
|
||||||
const loadingMessage = <p className="center-text">loading...</p>;
|
|
||||||
|
|
||||||
return this.state.isLoading ? loadingMessage : table;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Table(props){
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{ props.playlists.map((playlist) => <Row playlist={ playlist } key={ playlist.name }/>) }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Row(props){
|
|
||||||
return (
|
|
||||||
<PlaylistLink playlist={props.playlist}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function PlaylistLink(props){
|
|
||||||
return (
|
|
||||||
<Link to={ getPlaylistLink(props.playlist.name) }>{ props.playlist.name }</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlaylistLink(playlistName){
|
|
||||||
return '/app/playlist/' + playlistName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Playlists;
|
export default Playlists;
|
65
src/js/Playlist/PlaylistsView.js
Normal file
65
src/js/Playlist/PlaylistsView.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
class PlaylistsView extends Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isLoading: true
|
||||||
|
}
|
||||||
|
this.getPlaylists();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaylists(){
|
||||||
|
var self = this;
|
||||||
|
axios.get('/api/playlists')
|
||||||
|
.then((response) => {
|
||||||
|
self.setState({
|
||||||
|
playlists: response.data.playlists,
|
||||||
|
isLoading: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const table = <div><Table playlists={this.state.playlists}/></div>;
|
||||||
|
const loadingMessage = <p className="center-text">loading...</p>;
|
||||||
|
|
||||||
|
return this.state.isLoading ? loadingMessage : table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Table(props){
|
||||||
|
return (
|
||||||
|
<table className="app-table max-width">
|
||||||
|
<tbody>
|
||||||
|
{ props.playlists.map((playlist) => <Row playlist={ playlist } key={ playlist.name }/>) }
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Row(props){
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<PlaylistLink playlist={props.playlist}/>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlaylistLink(props){
|
||||||
|
return (
|
||||||
|
<td>
|
||||||
|
<Link to={ getPlaylistLink(props.playlist.name) } className="button full-width">{ props.playlist.name }</Link>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylistLink(playlistName){
|
||||||
|
return '/app/playlist/' + playlistName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlaylistsView;
|
@ -1,29 +1,71 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
import { BrowserRouter as Router, Route, Link, Switch, Redirect} from "react-router-dom";
|
||||||
|
|
||||||
import Index from "./Index.js";
|
import Index from "./Index/Index.js";
|
||||||
import Playlists from "./Playlist/Playlists.js";
|
import Playlists from "./Playlist/Playlists.js";
|
||||||
import PlaylistView from "./Playlist/PlaylistView.js";
|
import PlaylistView from "./Playlist/PlaylistView.js";
|
||||||
import Settings from "./Settings.js";
|
import Settings from "./Settings/Settings.js";
|
||||||
|
import Admin from "./Admin/Admin.js";
|
||||||
|
|
||||||
|
import NotFound from "./Error/NotFound.js";
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
class PlaylistManager extends Component {
|
class PlaylistManager extends Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
type: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.userInfoCancelToken.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserInfo(){
|
||||||
|
this.userInfoCancelToken = axios.CancelToken.source();
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
axios.get('/api/user', {
|
||||||
|
cancelToken: this.userInfoCancelToken.token
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
self.setState({
|
||||||
|
type: response.data.type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<div className="card pad-12">
|
<div className="card pad-12">
|
||||||
<ul className="sidebar pad-3">
|
<table className="sidebar pad-3">
|
||||||
<li><Link to="/app">home</Link></li>
|
<tbody>
|
||||||
<li><Link to="/app/playlists">playlists</Link></li>
|
<tr><td><span><Link to="/app">home</Link></span></td></tr>
|
||||||
<li><Link to="/app/settings">settings</Link></li>
|
<tr><td><Link to="/app/playlists">playlists</Link></td></tr>
|
||||||
<li><a href="/auth/logout">logout</a></li>
|
<tr><td><Link to="/app/settings">settings</Link></td></tr>
|
||||||
<li><a href="https://sarsoo.xyz">sarsoo.xyz</a></li>
|
{ this.state.type == 'admin' && <tr><td><Link to="/app/admin">admin</Link></td></tr> }
|
||||||
</ul>
|
<tr><td><a href="/auth/logout">logout</a></td></tr>
|
||||||
|
<tr><td><a href="https://sarsoo.xyz">sarsoo.xyz</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<div className="pad-9">
|
<div className="pad-9">
|
||||||
<Route path="/app" exact component={Index} />
|
<Switch>
|
||||||
<Route path="/app/playlists" exact component={Playlists} />
|
<Route path="/app" exact component={Index} />
|
||||||
<Route path="/app/settings" component={Settings} />
|
<Route path="/app/playlists" exact component={Playlists} />
|
||||||
|
<Route path="/app/settings" component={Settings} />
|
||||||
|
{ this.state.type == 'admin' && <Route path="/app/admin" component={Admin} /> }
|
||||||
|
<Route path='/app/playlist/:name' component={PlaylistView} />
|
||||||
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
function Settings(props){
|
|
||||||
|
|
||||||
return <p>settings</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Settings;
|
|
126
src/js/Settings/ChangePassword.js
Normal file
126
src/js/Settings/ChangePassword.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
class ChangePassword extends Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
current: "",
|
||||||
|
new1: "",
|
||||||
|
new2: "",
|
||||||
|
error: false,
|
||||||
|
errorValue: null
|
||||||
|
}
|
||||||
|
this.handleCurrentChange = this.handleCurrentChange.bind(this);
|
||||||
|
this.handleNewChange = this.handleNewChange.bind(this);
|
||||||
|
this.handleNew2Change = this.handleNew2Change.bind(this);
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCurrentChange(event){
|
||||||
|
this.setState({
|
||||||
|
'current': event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleNewChange(event){
|
||||||
|
this.setState({
|
||||||
|
'new1': event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleNew2Change(event){
|
||||||
|
this.setState({
|
||||||
|
'new2': event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event){
|
||||||
|
|
||||||
|
if(this.state.current.length == 0){
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorValue: "enter current password"
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
if(this.state.new1.length == 0){
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorValue: "enter new password"
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
if(this.state.new1 != this.state.new2){
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorValue: "new password mismatch"
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
|
||||||
|
axios.post('/api/user/password',{
|
||||||
|
current_password: this.state.current,
|
||||||
|
new_password: this.state.new1
|
||||||
|
}).then((response) => {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorValue: "password changed"
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
errorValue: "error changing password"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>change password</h1>
|
||||||
|
<form onSubmit={this.handleSubmit}>
|
||||||
|
<table className="app-table max-width">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td className="ui-text center-text">current:</td>
|
||||||
|
<td><input
|
||||||
|
type="password"
|
||||||
|
name="current"
|
||||||
|
value={this.state.current}
|
||||||
|
onChange={this.handleCurrentChange}
|
||||||
|
className="full-width" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="ui-text center-text">new:</td>
|
||||||
|
<td><input
|
||||||
|
type="password"
|
||||||
|
name="new1"
|
||||||
|
value={this.state.new1}
|
||||||
|
onChange={this.handleNewChange}
|
||||||
|
className="full-width" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="ui-text center-text">new again:</td>
|
||||||
|
<td><input
|
||||||
|
type="password"
|
||||||
|
name="new2"
|
||||||
|
value={this.state.new2}
|
||||||
|
onChange={this.handleNew2Change}
|
||||||
|
className="full-width" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan="2"><input type="submit" style={{width: "100%"}} className="button" value="change" /></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
{ this.state.error && <p style={{color: "red"}}>{this.state.errorValue}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangePassword;
|
24
src/js/Settings/Settings.js
Normal file
24
src/js/Settings/Settings.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { BrowserRouter as Router, Route, Link, Switch, Redirect} from "react-router-dom";
|
||||||
|
|
||||||
|
import ChangePassword from "./ChangePassword.js"
|
||||||
|
|
||||||
|
class Settings extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ul className="navbar" style={{width: "100%"}}>
|
||||||
|
<li><Link to={`${this.props.match.url}/password`}>password</Link></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Route path={`${this.props.match.url}/password`} component={ChangePassword} />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default Settings;
|
@ -1,6 +1,7 @@
|
|||||||
$font-stack: 'Lato', arial;
|
$font-stack: 'Lato', arial;
|
||||||
$background-colour: #202124;
|
$background-colour: #202124;
|
||||||
$ui-colour: #131313;
|
$ui-colour: #131313;
|
||||||
|
$light-ui: #575757;
|
||||||
$text-colour: white;
|
$text-colour: white;
|
||||||
|
|
||||||
$pad-px: 20px;
|
$pad-px: 20px;
|
||||||
@ -30,6 +31,10 @@ p {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-text {
|
||||||
|
color: $text-colour;
|
||||||
|
}
|
||||||
|
|
||||||
.center-text {
|
.center-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -40,34 +45,17 @@ p {
|
|||||||
|
|
||||||
.button {
|
.button {
|
||||||
background-color: #505050;
|
background-color: #505050;
|
||||||
color: white;
|
color: $text-colour;
|
||||||
border-radius: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 4px auto;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 15px;
|
|
||||||
box-shadow: 2px 2px 4px black;
|
|
||||||
/*-webkit-transition-duration: 0.4s;
|
|
||||||
transition-duration: 0.4s;*/
|
|
||||||
|
|
||||||
text: {
|
|
||||||
align: center;
|
|
||||||
decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #505050;
|
|
||||||
color: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
margin: 4px auto;
|
margin: 4px auto;
|
||||||
cursor: pointer;
|
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
box-shadow: 2px 2px 4px black;
|
box-shadow: 2px 2px 4px black;
|
||||||
/*-webkit-transition-duration: 0.4s;
|
/*-webkit-transition-duration: 0.4s;
|
||||||
transition-duration: 0.4s;*/
|
transition-duration: 0.4s;*/
|
||||||
@ -127,6 +115,12 @@ h1.title {
|
|||||||
font-size: 6em;
|
font-size: 6em;
|
||||||
font-family: 'Pirata One', arial;
|
font-family: 'Pirata One', arial;
|
||||||
text-shadow: 3px 3px 3px #aaa;
|
text-shadow: 3px 3px 3px #aaa;
|
||||||
|
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: block;
|
display: block;
|
||||||
@ -152,7 +146,7 @@ h1.title {
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: $text-colour;
|
||||||
text-shadow: 1px 1px 2px #4f4f4f;
|
text-shadow: 1px 1px 2px #4f4f4f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +156,7 @@ h1.title {
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
box-shadow: 2px 2px 2px black;
|
box-shadow: 2px 2px 2px black;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -172,7 +166,7 @@ h1.title {
|
|||||||
|
|
||||||
h1.sectiontitle {
|
h1.sectiontitle {
|
||||||
text-align:center;
|
text-align:center;
|
||||||
color: white;
|
color: $text-colour;
|
||||||
font-family: 'Megrim', sans-serif;
|
font-family: 'Megrim', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +177,7 @@ ul.navbar {
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: $ui-colour;
|
background-color: $light-ui;
|
||||||
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
float: left;
|
float: left;
|
||||||
@ -195,9 +188,10 @@ ul.navbar {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
color: white;
|
color: $text-colour;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-shadow: 1px 1px 2px black;
|
text-shadow: 1px 1px 2px black;
|
||||||
-webkit-transition: background-color 0.4s;
|
-webkit-transition: background-color 0.4s;
|
||||||
@ -214,31 +208,77 @@ ul.navbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
li {
|
|
||||||
// float: left;
|
background-color: $light-ui;
|
||||||
position: -webkit-sticky;
|
list-style-type: none;
|
||||||
position: sticky;
|
margin-left: 10px;
|
||||||
top: 0;
|
margin-right: 10px;
|
||||||
height: 50px;
|
border-radius: 5px;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
a {
|
tr {
|
||||||
// display: block;
|
// float: left;
|
||||||
color: white;
|
// position: -webkit-sticky;
|
||||||
text-align: center;
|
// position: sticky;
|
||||||
padding: 14px 16px;
|
top: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
// padding: 10px;
|
||||||
|
|
||||||
|
td {
|
||||||
|
|
||||||
text-decoration: none;
|
height: 50px;
|
||||||
text-shadow: 1px 1px 2px black;
|
text-align: center;
|
||||||
-webkit-transition: background-color 0.4s;
|
vertical-align: center;
|
||||||
transition: background-color 0.4s;
|
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
// margin-top: 10px;
|
||||||
|
// margin-bottom: 10px;
|
||||||
|
// margin: auto;
|
||||||
|
|
||||||
|
a {
|
||||||
|
// display: block;
|
||||||
|
color: $text-colour;
|
||||||
|
// text-align: center;
|
||||||
|
// vertical-align: center;
|
||||||
|
// margin: 25px;
|
||||||
|
// padding: 14px 16px;
|
||||||
|
// border-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 1px 1px 2px black;
|
||||||
|
-webkit-transition: background-color 0.4s;
|
||||||
|
transition: background-color 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #080808;
|
background-color: black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-selected {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-table {
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
td {
|
||||||
|
padding: $pad-px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.max-width {
|
||||||
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@ -257,7 +297,7 @@ footer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 768px) {
|
||||||
ul.navbar li.right,
|
ul.navbar li.right,
|
||||||
ul.navbar li {float: none;}
|
ul.navbar li {float: none;}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user