Merge pull request #392 from mz8i/feature/63-delete-account-frontend
Feature 63: delete user account (frontend code)
This commit is contained in:
commit
2127521094
@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
import { authUser, getNewUserAPIKey, logout } from './services/user';
|
||||
import { authUser, createUser, getUserById, getNewUserAPIKey, deleteUser, logout } from './services/user';
|
||||
import { queryLocation } from './services/search';
|
||||
|
||||
import buildingsRouter from './routes/buildingsRouter';
|
||||
@ -16,6 +16,35 @@ server.use(bodyParser.json());
|
||||
server.use('/buildings', buildingsRouter);
|
||||
server.use('/users', usersRouter);
|
||||
|
||||
// GET own user info
|
||||
server.route('/users/me')
|
||||
.get(function (req, res) {
|
||||
if (!req.session.user_id) {
|
||||
res.send({ error: 'Must be logged in' });
|
||||
return
|
||||
}
|
||||
|
||||
getUserById(req.session.user_id).then(function (user) {
|
||||
res.send(user);
|
||||
}).catch(function (error) {
|
||||
res.send(error);
|
||||
});
|
||||
})
|
||||
.delete((req, res) => {
|
||||
if (!req.session.user_id) {
|
||||
return res.send({ error: 'Must be logged in' });
|
||||
}
|
||||
console.log(`Deleting user ${req.session.user_id}`);
|
||||
|
||||
deleteUser(req.session.user_id).then(
|
||||
() => logout(req.session)
|
||||
).then(() => {
|
||||
res.send({ success: true });
|
||||
}).catch(err => {
|
||||
res.send({ error: err });
|
||||
});
|
||||
})
|
||||
|
||||
// POST user auth
|
||||
server.post('/login', function (req, res) {
|
||||
authUser(req.body.username, req.body.password).then(function (user: any) { // TODO: remove any
|
||||
@ -40,7 +69,6 @@ server.post('/logout', function (req, res) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// POST generate API key
|
||||
server.post('/api/key', function (req, res) {
|
||||
if (!req.session.user_id) {
|
||||
|
7
app/src/frontend/confirmation-modal.css
Normal file
7
app/src/frontend/confirmation-modal.css
Normal file
@ -0,0 +1,7 @@
|
||||
.modal.modal-show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal.modal-hide {
|
||||
display: none;
|
||||
}
|
61
app/src/frontend/confirmation-modal.tsx
Normal file
61
app/src/frontend/confirmation-modal.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
|
||||
import './confirmation-modal.css';
|
||||
|
||||
interface ConfirmationModalProps {
|
||||
show: boolean,
|
||||
title: string,
|
||||
description: string,
|
||||
confirmButtonText?: string,
|
||||
confirmButtonClass?: string,
|
||||
cancelButtonClass?: string,
|
||||
onConfirm: () => void,
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
const ConfirmationModal: React.FunctionComponent<ConfirmationModalProps> = ({
|
||||
confirmButtonText = 'OK',
|
||||
confirmButtonClass = 'btn-primary',
|
||||
cancelButtonClass = '',
|
||||
...props
|
||||
}) => {
|
||||
const modalShowClass = props.show ? 'modal-show': 'modal-hide';
|
||||
return (
|
||||
<div className={`modal ${modalShowClass}`} tabIndex={-1} role="dialog">
|
||||
<div className="modal-backdrop">
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">{props.title}</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="close"
|
||||
aria-label="Close"
|
||||
onClick={() => props.onCancel()}
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>{props.description}</p>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-block ${confirmButtonClass}`}
|
||||
onClick={() => props.onConfirm()}
|
||||
>{confirmButtonText}</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-block ${cancelButtonClass}`}
|
||||
onClick={() => props.onCancel()}
|
||||
>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmationModal;
|
@ -1,8 +1,9 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, FormEvent } from 'react';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import ErrorBox from './error-box';
|
||||
import ConfirmationModal from './confirmation-modal';
|
||||
|
||||
class MyAccountPage extends Component<any, any> { // TODO: add proper types
|
||||
static propTypes = { // TODO: generate propTypes from TS
|
||||
@ -20,7 +21,8 @@ class MyAccountPage extends Component<any, any> { // TODO: add proper types
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: undefined
|
||||
error: undefined,
|
||||
showDeleteConfirm: false
|
||||
};
|
||||
this.handleLogout = this.handleLogout.bind(this);
|
||||
this.handleGenerateKey = this.handleGenerateKey.bind(this);
|
||||
@ -66,6 +68,37 @@ class MyAccountPage extends Component<any, any> { // TODO: add proper types
|
||||
);
|
||||
}
|
||||
|
||||
confirmDelete(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
this.setState({ showDeleteConfirm: true });
|
||||
}
|
||||
|
||||
hideConfirmDelete() {
|
||||
this.setState({ showDeleteConfirm: false });
|
||||
}
|
||||
|
||||
async handleDelete() {
|
||||
this.setState({ error: undefined });
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/users/me', {
|
||||
method: 'DELETE',
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if(data.error) {
|
||||
this.setState({ error: data.error });
|
||||
} else {
|
||||
this.props.logout();
|
||||
}
|
||||
} catch (err) {
|
||||
this.setState({ error: err });
|
||||
} finally {
|
||||
this.hideConfirmDelete();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.user && !this.props.user.error) {
|
||||
return (
|
||||
@ -111,6 +144,26 @@ class MyAccountPage extends Component<any, any> { // TODO: add proper types
|
||||
<h3 className="h3">GitHub</h3>
|
||||
<a href="http://github.com/tomalrussell/colouring-london/">Colouring London Github repository</a>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2 className="h2">Account actions</h2>
|
||||
<form
|
||||
onSubmit={e => this.confirmDelete(e)}
|
||||
className="form-group mb-3"
|
||||
>
|
||||
<input className="btn btn-danger" type="submit" value="Delete account" />
|
||||
</form>
|
||||
|
||||
<ConfirmationModal
|
||||
show={this.state.showDeleteConfirm}
|
||||
title="Confirm account deletion"
|
||||
description="Are you sure you want to delete your account? This cannot be undone."
|
||||
confirmButtonText="Delete account"
|
||||
confirmButtonClass="btn-danger"
|
||||
onConfirm={() => this.handleDelete()}
|
||||
onCancel={() => this.hideConfirmDelete()}
|
||||
/>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user