diff --git a/app/components/Checkbox.js b/app/components/Checkbox.js
index 8f7243f9a..f6420cff2 100644
--- a/app/components/Checkbox.js
+++ b/app/components/Checkbox.js
@@ -2,10 +2,12 @@
import * as React from 'react';
import styled from 'styled-components';
import HelpText from 'components/HelpText';
+import VisuallyHidden from 'components/VisuallyHidden';
export type Props = {
checked?: boolean,
label?: string,
+ labelHidden?: boolean,
className?: string,
note?: string,
small?: boolean,
@@ -30,18 +32,26 @@ const Label = styled.label`
export default function Checkbox({
label,
+ labelHidden,
note,
className,
small,
short,
...rest
}: Props) {
+ const wrappedLabel = {label};
+
return (
{note && {note}}
diff --git a/app/components/Tabs.js b/app/components/Tabs.js
index 578f6615f..a92da1dd4 100644
--- a/app/components/Tabs.js
+++ b/app/components/Tabs.js
@@ -7,4 +7,12 @@ const Tabs = styled.nav`
margin-bottom: 12px;
`;
+export const Separator = styled.span`
+ border-left: 1px solid ${props => props.theme.divider};
+ position: relative;
+ top: 2px;
+ margin-right: 24px;
+ margin-top: 6px;
+`;
+
export default Tabs;
diff --git a/app/menus/UserMenu.js b/app/menus/UserMenu.js
index 0336cd8ab..a6a70d1cd 100644
--- a/app/menus/UserMenu.js
+++ b/app/menus/UserMenu.js
@@ -42,7 +42,7 @@ class UserMenu extends React.Component {
const { user, users } = this.props;
if (
!window.confirm(
- "Are you want to suspend this account? Suspended users won't be able to access Outline."
+ 'Are you want to suspend this account? Suspended users will be prevented from logging in.'
)
) {
return;
@@ -50,6 +50,12 @@ class UserMenu extends React.Component {
users.suspend(user);
};
+ handleRevoke = (ev: SyntheticEvent<>) => {
+ ev.preventDefault();
+ const { user, users } = this.props;
+ users.delete(user, { confirmation: true });
+ };
+
handleActivate = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { user, users } = this.props;
@@ -71,15 +77,21 @@ class UserMenu extends React.Component {
Make {user.name} an admin…
))}
- {user.isSuspended ? (
-
- Activate account
-
- ) : (
-
- Suspend account…
+ {!user.lastActiveAt && (
+
+ Revoke invite…
)}
+ {user.lastActiveAt &&
+ (user.isSuspended ? (
+
+ Activate account
+
+ ) : (
+
+ Suspend account…
+
+ ))}
);
}
diff --git a/app/models/Team.js b/app/models/Team.js
index 5706396ba..d149ab4b8 100644
--- a/app/models/Team.js
+++ b/app/models/Team.js
@@ -1,4 +1,5 @@
// @flow
+import { computed } from 'mobx';
import BaseModel from './BaseModel';
class Team extends BaseModel {
@@ -9,8 +10,18 @@ class Team extends BaseModel {
googleConnected: boolean;
sharing: boolean;
documentEmbeds: boolean;
+ guestSignin: boolean;
subdomain: ?string;
url: string;
+
+ @computed
+ get signinMethods(): string {
+ if (this.slackConnected && this.googleConnected) {
+ return 'Slack or Google';
+ }
+ if (this.slackConnected) return 'Slack';
+ return 'Google';
+ }
}
export default Team;
diff --git a/app/scenes/Invite.js b/app/scenes/Invite.js
index 68b048f6a..6854fef33 100644
--- a/app/scenes/Invite.js
+++ b/app/scenes/Invite.js
@@ -1,14 +1,15 @@
// @flow
import * as React from 'react';
-import { withRouter, type RouterHistory } from 'react-router-dom';
+import { Link, withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import { CloseIcon } from 'outline-icons';
import styled from 'styled-components';
import Flex from 'shared/components/Flex';
-import CopyToClipboard from 'components/CopyToClipboard';
import Button from 'components/Button';
import Input from 'components/Input';
+import CopyToClipboard from 'components/CopyToClipboard';
+import Checkbox from 'components/Checkbox';
import HelpText from 'components/HelpText';
import Tooltip from 'components/Tooltip';
import NudeButton from 'components/NudeButton';
@@ -16,6 +17,7 @@ import NudeButton from 'components/NudeButton';
import UiStore from 'stores/UiStore';
import AuthStore from 'stores/AuthStore';
import UsersStore from 'stores/UsersStore';
+import PoliciesStore from 'stores/PoliciesStore';
const MAX_INVITES = 20;
@@ -23,6 +25,7 @@ type Props = {
auth: AuthStore,
users: UsersStore,
history: RouterHistory,
+ policies: PoliciesStore,
ui: UiStore,
onSubmit: () => void,
};
@@ -32,10 +35,10 @@ class Invite extends React.Component {
@observable isSaving: boolean;
@observable linkCopied: boolean = false;
@observable
- invites: { email: string, name: string }[] = [
- { email: '', name: '' },
- { email: '', name: '' },
- { email: '', name: '' },
+ invites: { email: string, name: string, guest: boolean }[] = [
+ { email: '', name: '', guest: false },
+ { email: '', name: '', guest: false },
+ { email: '', name: '', guest: false },
];
handleSubmit = async (ev: SyntheticEvent<>) => {
@@ -57,6 +60,10 @@ class Invite extends React.Component {
this.invites[index][ev.target.name] = ev.target.value;
};
+ handleGuestChange = (ev, index) => {
+ this.invites[index][ev.target.name] = ev.target.checked;
+ };
+
handleAdd = () => {
if (this.invites.length >= MAX_INVITES) {
this.props.ui.showToast(
@@ -64,10 +71,11 @@ class Invite extends React.Component {
);
}
- this.invites.push({ email: '', name: '' });
+ this.invites.push({ email: '', name: '', guest: false });
};
- handleRemove = (index: number) => {
+ handleRemove = (ev: SyntheticEvent<>, index: number) => {
+ ev.preventDefault();
this.invites.splice(index, 1);
};
@@ -81,23 +89,40 @@ class Invite extends React.Component {
if (!team || !user) return null;
const predictedDomain = user.email.split('@')[1];
+ const can = this.props.policies.abilities(team.id);
return (
-
+