Skip to content
This repository has been archived by the owner on Apr 30, 2020. It is now read-only.

Basic implementation for Admin user page #82

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
"express-async-errors": "^3.1.1",
"handlebars": "^4.0.11",
"jsonwebtoken": "5.0.4",
"lodash": "^4.17.11",
"method-override": "^2.3.5",
"moment": "^2.10.3",
"mongoose": "^4.13.18",
"morgan": "^1.6.1",
"nodemailer": "^1.4.0",
"nodemailer-smtp-transport": "^1.0.3",
"passport-local": "^1.0.0",
"query-string": "^6.5.0",
"request": "^2.60.0",
"underscore": "^1.8.3",
"validator": "^3.40.1"
Expand Down
12 changes: 11 additions & 1 deletion app/server/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,17 @@ UserController.admitUser = function(id, user, callback){
}, {
new: true
},
callback);
function (err, user) {
if (err) {
return callback(err);
}

if (!user) {
return callback({ message: 'user cannot be admitted' })
}

return callback(null, user);
});
});
};

Expand Down
6 changes: 3 additions & 3 deletions app/server/routes/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ router.post('/:id/unfavoriteFirebaseEvent/:firebaseId', isOwnerOrAdmin, async fu
});

/**
* Admit a user. ADMIN ONLY, DUH
* Admit a user. Admin only.
*
* Also attaches the user who did the admitting, for liabaility.
*/
Expand All @@ -137,7 +137,7 @@ router.post('/:id/admit', isAdmin, function (req, res) {
});

/**
* Check in a user. Admin or Organizer
* Check in a user. Admin or Organizer.
*/
router.post('/:id/checkin', isOrganizerOrAdmin, function (req, res) {
const id = req.params.id;
Expand All @@ -147,7 +147,7 @@ router.post('/:id/checkin', isOrganizerOrAdmin, function (req, res) {
});

/**
* Check in a user. Admin or Organizer
* Check out a user. Admin or Organizer.
*/
router.post('/:id/checkout', isOrganizerOrAdmin, function (req, res) {
const id = req.params.id;
Expand Down
32 changes: 32 additions & 0 deletions client/src/components/Admin/AdminNav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Menu } from 'semantic-ui-react';

@observer
class AdminNav extends React.Component {
render() {
const { activePage } = this.props;

return (
<Menu size='huge' fluid attached>
<Menu.Item name='stats'
active={activePage === 'stats'}
onClick={() => this.props.pushHistory('/admin/stats')} />
<Menu.Item name='users'
active={activePage === 'users'}
onClick={() => this.props.pushHistory('/admin/users')} />
<Menu.Item name='settings'
active={activePage === 'settings'}
onClick={() => this.props.pushHistory('/admin/settings')} />
<Menu.Item name='events'
active={activePage === 'events'}
onClick={() => this.props.pushHistory('/admin/events')} />
<Menu.Item name='actions'
active={activePage === 'actions'}
onClick={() => this.props.pushHistory('/admin/actions')} />
</Menu>
)
}
}

export default AdminNav;
16 changes: 16 additions & 0 deletions client/src/components/Admin/AdminStatsDisplay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from 'react';
import * as _ from 'lodash';

class AdminStatsDisplay extends React.Component {
render() {
const { stats } = this.props;

return (
<pre>
{JSON.stringify(stats, null, 2)}
</pre>
)
}
}

export default AdminStatsDisplay;
17 changes: 17 additions & 0 deletions client/src/components/Admin/AdminUsers/UserModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Modal } from 'semantic-ui-react';

const UserModal = ({ user, ...rest}) => {
return (
<Modal
{...rest}
closeIcon
content={<pre>
{JSON.stringify(user, null, '\t')}
</pre>}
/>
);
}

export default observer(UserModal);
40 changes: 40 additions & 0 deletions client/src/components/Admin/AdminUsers/UsersLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Grid, Header } from 'semantic-ui-react';
import LoadingDisplay from '../../LoadingDisplay';

@observer
class UsersLayout extends React.Component {
render() {
const {
usersSearch,
usersTable,
loading,
userModal,
admitModal,
checkinModal,
} = this.props;

return (
<Grid padded>
<Grid.Row>
<Grid.Column width={3}>
<Header as='h3' content="Search" />
{usersSearch}
</Grid.Column>
<Grid.Column width={13}>
<Header as='h3' content="Users" />
{loading
? <LoadingDisplay />
: usersTable}
</Grid.Column>
</Grid.Row>
{userModal}
{admitModal}
{checkinModal}
</Grid>
)
}
}

export default UsersLayout;
69 changes: 69 additions & 0 deletions client/src/components/Admin/AdminUsers/UsersSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { observer } from 'mobx-react';
import * as _ from 'lodash';
import { Button, Input, Label } from 'semantic-ui-react';

@observer
class UsersSearch extends React.Component {
constructor(props) {
super(props);

this.state = { intermediatePageSize: props.pageSize };
}

componentDidUpdate(prevProps) {
if (prevProps.pageSize !== this.props.pageSize) {
this.setState({ intermediatePageSize: this.props.pageSize });
}
}

render() {
return (
<div>
<div style={{textAlign: 'center', margin: '10px 0'}}>
<Button circular icon='angle double left'
onClick={() => this.props.onChangePage(0)} />
<Button circular icon='angle left'
onClick={() => this.props.onChangePage(this.props.page - 1)} />
<Label size='large' content={this.props.page} />
<Button circular icon='angle right'
onClick={() => this.props.onChangePage(Number(this.props.page) + 1)} />
<Button circular icon='angle double right'
onClick={() => this.props.onChangePage(this.props.numPages - 1)} />
</div>

<Input fluid style={{margin: '10px 0'}}
type='number'
label='Size'
action={{
color:'teal',
content: 'Update',
onClick: this.handlePageSizeSubmit,
}}
value={this.state.intermediatePageSize}
onChange={this.handlePageSizeChange}
/>

<Input fluid style={{margin: '10px 0'}}
icon='search'
placeholder='Search'
onChange={_.debounce(this.handleSearchChange, 250)}
/>
</div>
);
}

handleSearchChange = (e, { value }) => {
this.props.onChangeQuery(value);
}

handlePageSizeChange = (e, { value }) => {
this.setState({ intermediatePageSize: value });
}

handlePageSizeSubmit = () => {
this.props.onChangePageSize(Number(this.state.intermediatePageSize));
}
}

export default UsersSearch;
Loading