Initial work on the frontend

This commit is contained in:
Jori Lallo
2018-03-04 15:39:17 -08:00
parent 3d6b9466fb
commit 06a6573feb
6 changed files with 142 additions and 45 deletions

View File

@ -2,7 +2,8 @@
import React from 'react';
import { Provider } from 'mobx-react';
import stores from 'stores';
import SettingsStore from 'stores/SettingsStore';
import ApiKeysStore from 'stores/settings/ApiKeysStore';
import MembersStore from 'stores/settings/MembersStore';
import DocumentsStore from 'stores/DocumentsStore';
import CollectionsStore from 'stores/CollectionsStore';
import CacheStore from 'stores/CacheStore';
@ -22,7 +23,8 @@ const Auth = ({ children }: Props) => {
const { user, team } = stores.auth;
const cache = new CacheStore(user.id);
authenticatedStores = {
settings: new SettingsStore(),
apiKeys: new ApiKeysStore(),
members: new MembersStore(),
documents: new DocumentsStore({
ui: stores.ui,
cache,

View File

@ -1,5 +1,6 @@
// @flow
import React, { Component } from 'react';
import invariant from 'invariant';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import styled from 'styled-components';
@ -7,17 +8,20 @@ import Flex from 'shared/components/Flex';
import Avatar from 'components/Avatar';
import { color } from 'shared/styles/constants';
import AuthStore from 'stores/AuthStore';
import ErrorsStore from 'stores/ErrorsStore';
import SettingsStore from 'stores/SettingsStore';
import MembersStore from 'stores/settings/MembersStore';
import CenteredContent from 'components/CenteredContent';
import LoadingPlaceholder from 'components/LoadingPlaceholder';
import PageTitle from 'components/PageTitle';
import MemberMenu from './components/MemberMenu';
@observer
class Members extends Component {
props: {
auth: AuthStore,
errors: ErrorsStore,
settings: SettingsStore,
members: MembersStore,
};
@observable members;
@ -27,28 +31,37 @@ class Members extends Component {
@observable isInviting: boolean = false;
componentDidMount() {
this.props.settings.fetchMembers();
this.props.members.fetchMembers();
}
render() {
const user = this.props.auth.user;
invariant(user, 'User should exist');
return (
<CenteredContent>
<PageTitle title="Members" />
<h1>Members</h1>
{!this.props.settings.isFetching ? (
{!this.props.members.isFetching ? (
<Flex column>
{this.props.settings.members && (
{this.props.members.members && (
<MemberList column>
{this.props.settings.members.map(member => (
{this.props.members.members.map(member => (
<Member key={member.id} justify="space-between" auto>
<Flex>
<Avatar src={member.avatarUrl} />
<UserName>
{member.name} {member.email && `(${member.email})`}
{member.isAdmin && <AdminBadge>Admin</AdminBadge>}
{member.isAdmin && (
<Badge admin={member.isAdmin}>Admin</Badge>
)}
{member.isSuspended && <Badge>Suspended</Badge>}
</UserName>
</Flex>
<Flex>
{user.id !== member.id && <MemberMenu user={member} />}
</Flex>
</Member>
))}
</MemberList>
@ -84,12 +97,15 @@ const UserName = styled.span`
padding-left: 8px;
`;
const AdminBadge = styled.span`
const Badge = styled.span`
margin-left: 10px;
color: #777;
font-size: 13px;
padding: 2px 6px;
background-color: ${({ admin }) => (admin ? color.primary : color.smokeDark)};
color: ${({ admin }) => (admin ? color.white : color.text)};
border-radius: 4px;
font-size: 11px;
text-transform: uppercase;
font-weight: normal;
`;
export default inject('errors', 'settings')(Members);
export default inject('auth', 'errors', 'members')(Members);

View File

@ -5,7 +5,7 @@ import { observer, inject } from 'mobx-react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import ApiToken from './components/ApiToken';
import SettingsStore from 'stores/SettingsStore';
import ApiKeysStore from 'stores/settings/ApiKeysStore';
import { color } from 'shared/styles/constants';
import Button from 'components/Button';
@ -19,11 +19,11 @@ import Subheading from 'components/Subheading';
class Tokens extends Component {
@observable name: string = '';
props: {
settings: SettingsStore,
apiKeys: ApiKeysStore,
};
componentDidMount() {
this.props.settings.fetchApiKeys();
this.props.apiKeys.fetchApiKeys();
}
handleUpdate = (ev: SyntheticInputEvent) => {
@ -32,13 +32,13 @@ class Tokens extends Component {
handleSubmit = async (ev: SyntheticEvent) => {
ev.preventDefault();
await this.props.settings.createApiKey(this.name);
await this.props.apiKeys.createApiKey(this.name);
this.name = '';
};
render() {
const { settings } = this.props;
const hasApiKeys = settings.apiKeys.length > 0;
const { apiKeys } = this.props;
const hasApiKeys = apiKeys.apiKeys.length > 0;
return (
<CenteredContent>
@ -49,13 +49,13 @@ class Tokens extends Component {
<Subheading>Your tokens</Subheading>,
<Table>
<tbody>
{settings.apiKeys.map(key => (
{apiKeys.apiKeys.map(key => (
<ApiToken
id={key.id}
key={key.id}
name={key.name}
secret={key.secret}
onDelete={settings.deleteApiKey}
onDelete={apiKeys.deleteApiKey}
/>
))}
</tbody>
@ -78,7 +78,7 @@ class Tokens extends Component {
<Button
type="submit"
value="Create Token"
disabled={settings.isSaving}
disabled={apiKeys.isSaving}
/>
</form>
</CenteredContent>
@ -96,4 +96,4 @@ const Table = styled.table`
}
`;
export default inject('settings')(Tokens);
export default inject('apiKeys')(Tokens);

View File

@ -0,0 +1,67 @@
// @flow
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import styled from 'styled-components';
import MembersStore from 'stores/settings/MembersStore';
import MoreIcon from 'components/Icon/MoreIcon';
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
import type { User } from 'types';
type Props = {
user: User,
members: MembersStore,
};
@observer
class MemberMenu extends Component {
props: Props;
handlePromote = (ev: SyntheticEvent) => {
ev.preventDefault();
};
handleDemote = (ev: SyntheticEvent) => {
ev.preventDefault();
};
handleSuspend = (ev: SyntheticEvent) => {
ev.preventDefault();
};
handleActivate = (ev: SyntheticEvent) => {
ev.preventDefault();
};
render() {
const { user } = this.props;
return (
<span>
<DropdownMenu label={<MoreIcon />}>
{!user.isSuspended &&
(user.isAdmin ? (
<DropdownMenuItem onClick={this.handleDemote}>
Make {user.name} a member
</DropdownMenuItem>
) : (
<DropdownMenuItem onClick={this.handlePromote}>
Make {user.name} an admin
</DropdownMenuItem>
))}
{user.isSuspended ? (
<DropdownMenuItem onClick={this.handleActivate}>
Activate account
</DropdownMenuItem>
) : (
<DropdownMenuItem onClick={this.handleSuspend}>
Suspend account
</DropdownMenuItem>
)}
</DropdownMenu>
</span>
);
}
}
export default inject('members')(MemberMenu);

View File

@ -2,11 +2,10 @@
import { observable, action, runInAction } from 'mobx';
import invariant from 'invariant';
import { client } from 'utils/ApiClient';
import type { ApiKey, User } from 'types';
import type { ApiKey } from 'types';
class SettingsStore {
class SettingsApiKeysStore {
@observable apiKeys: ApiKey[] = [];
@observable members: User[] = [];
@observable isFetching: boolean = false;
@observable isSaving: boolean = false;
@ -56,24 +55,6 @@ class SettingsStore {
console.error('Something went wrong');
}
};
@action
fetchMembers = async () => {
this.isFetching = true;
try {
const res = await client.post('/team.users');
invariant(res && res.data, 'Data should be available');
const { data } = res;
runInAction('fetchMembers', () => {
this.members = data.reverse();
});
} catch (e) {
console.error('Something went wrong');
}
this.isFetching = false;
};
}
export default SettingsStore;
export default SettingsApiKeysStore;

View File

@ -0,0 +1,31 @@
// @flow
import { observable, action, runInAction } from 'mobx';
import invariant from 'invariant';
import { client } from 'utils/ApiClient';
import type { User } from 'types';
class SettingsUsersStore {
@observable members: User[] = [];
@observable isFetching: boolean = false;
@observable isSaving: boolean = false;
@action
fetchMembers = async () => {
this.isFetching = true;
try {
const res = await client.post('/team.users');
invariant(res && res.data, 'Data should be available');
const { data } = res;
runInAction('fetchMembers', () => {
this.members = data.reverse();
});
} catch (e) {
console.error('Something went wrong');
}
this.isFetching = false;
};
}
export default SettingsUsersStore;