fix: Add translation hooks on groups screen (#2303)
* Refactor groups page to functional component and translate strings * Update app/scenes/GroupNew.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupEdit.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupDelete.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupMembers/GroupMembers.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Format GroupMember.js * Change Trans usage * Format GroupDelete * Revert "Format GroupDelete" This reverts commit 880128f94d4d6cad7030c3aabc67261823e22b5d. * Update app/scenes/GroupNew.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/scenes/GroupNew.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update GroupNew * Remove newlines Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
parent
5cd4dbd9d7
commit
5689d96cc4
|
@ -1,59 +1,57 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { groupSettings } from "shared/utils/routeHelpers";
|
||||
import UiStore from "stores/UiStore";
|
||||
import Group from "models/Group";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
import HelpText from "components/HelpText";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
history: RouterHistory,
|
||||
type Props = {|
|
||||
group: Group,
|
||||
ui: UiStore,
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|};
|
||||
|
||||
@observer
|
||||
class GroupDelete extends React.Component<Props> {
|
||||
@observable isDeleting: boolean;
|
||||
function GroupDelete({ group, onSubmit }: Props) {
|
||||
const { ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [isDeleting, setIsDeleting] = React.useState();
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
const handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
this.isDeleting = true;
|
||||
setIsDeleting(true);
|
||||
|
||||
try {
|
||||
await this.props.group.delete();
|
||||
this.props.history.push(groupSettings());
|
||||
this.props.onSubmit();
|
||||
await group.delete();
|
||||
history.push(groupSettings());
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message, { type: "error" });
|
||||
ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
this.isDeleting = false;
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { group } = this.props;
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
Are you sure about that? Deleting the <strong>{group.name}</strong>{" "}
|
||||
group will cause its members to lose access to collections and
|
||||
documents that it is associated with.
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{this.isDeleting ? "Deleting…" : "I’m sure – Delete"}
|
||||
</Button>
|
||||
</form>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Flex column>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Are you sure about that? Deleting the <em>{{groupName}}</em> group will cause its members to lose access to collections and documents that it is associated with."
|
||||
values={{ groupName: group.name }}
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{isDeleting ? `${t("Deleting")}…` : t("I’m sure – Delete")}
|
||||
</Button>
|
||||
</form>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default inject("ui")(withRouter(GroupDelete));
|
||||
export default observer(GroupDelete);
|
||||
|
|
|
@ -1,70 +1,71 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import UiStore from "stores/UiStore";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import Group from "models/Group";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
import HelpText from "components/HelpText";
|
||||
import Input from "components/Input";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
history: RouterHistory,
|
||||
ui: UiStore,
|
||||
group: Group,
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer
|
||||
class GroupEdit extends React.Component<Props> {
|
||||
@observable name: string = this.props.group.name;
|
||||
@observable isSaving: boolean;
|
||||
function GroupEdit({ group, onSubmit }: Props) {
|
||||
const { ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = React.useState(group.name);
|
||||
const [isSaving, setIsSaving] = React.useState();
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
this.isSaving = true;
|
||||
const handleSubmit = React.useCallback(
|
||||
async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
await this.props.group.save({ name: this.name });
|
||||
this.props.onSubmit();
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await group.save({ name: name });
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[group, onSubmit, ui, name]
|
||||
);
|
||||
|
||||
handleNameChange = (ev: SyntheticInputEvent<*>) => {
|
||||
this.name = ev.target.value;
|
||||
};
|
||||
const handleNameChange = React.useCallback((ev: SyntheticInputEvent<*>) => {
|
||||
setName(ev.target.value);
|
||||
}, []);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HelpText>
|
||||
<Trans>
|
||||
You can edit the name of this group at any time, however doing so too
|
||||
often might confuse your team mates.
|
||||
</HelpText>
|
||||
<Flex>
|
||||
<Input
|
||||
type="text"
|
||||
label="Name"
|
||||
onChange={this.handleNameChange}
|
||||
value={this.name}
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
/>
|
||||
</Flex>
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<Flex>
|
||||
<Input
|
||||
type="text"
|
||||
label={t("Name")}
|
||||
onChange={handleNameChange}
|
||||
value={name}
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Button type="submit" disabled={this.isSaving || !this.name}>
|
||||
{this.isSaving ? "Saving…" : "Save"}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
<Button type="submit" disabled={isSaving || !name}>
|
||||
{isSaving ? `${t("Saving")}…` : t("Save")}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default inject("ui")(withRouter(GroupEdit));
|
||||
export default observer(GroupEdit);
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation, type TFunction } from "react-i18next";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import GroupMembershipsStore from "stores/GroupMembershipsStore";
|
||||
import PoliciesStore from "stores/PoliciesStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
import UsersStore from "stores/UsersStore";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import Group from "models/Group";
|
||||
import User from "models/User";
|
||||
import Button from "components/Button";
|
||||
|
@ -20,112 +14,99 @@ import PaginatedList from "components/PaginatedList";
|
|||
import Subheading from "components/Subheading";
|
||||
import AddPeopleToGroup from "./AddPeopleToGroup";
|
||||
import GroupMemberListItem from "./components/GroupMemberListItem";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
ui: UiStore,
|
||||
auth: AuthStore,
|
||||
group: Group,
|
||||
users: UsersStore,
|
||||
policies: PoliciesStore,
|
||||
groupMemberships: GroupMembershipsStore,
|
||||
t: TFunction,
|
||||
};
|
||||
|
||||
@observer
|
||||
class GroupMembers extends React.Component<Props> {
|
||||
@observable addModalOpen: boolean = false;
|
||||
function GroupMembers({ group }: Props) {
|
||||
const [addModalOpen, setAddModalOpen] = React.useState();
|
||||
const { users, groupMemberships, policies, ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const can = policies.abilities(group.id);
|
||||
|
||||
handleAddModalOpen = () => {
|
||||
this.addModalOpen = true;
|
||||
const handleAddModal = (state) => {
|
||||
setAddModalOpen(state);
|
||||
};
|
||||
|
||||
handleAddModalClose = () => {
|
||||
this.addModalOpen = false;
|
||||
};
|
||||
|
||||
handleRemoveUser = async (user: User) => {
|
||||
const { t } = this.props;
|
||||
|
||||
const handleRemoveUser = async (user: User) => {
|
||||
try {
|
||||
await this.props.groupMemberships.delete({
|
||||
groupId: this.props.group.id,
|
||||
await groupMemberships.delete({
|
||||
groupId: group.id,
|
||||
userId: user.id,
|
||||
});
|
||||
this.props.ui.showToast(
|
||||
ui.showToast(
|
||||
t(`{{userName}} was removed from the group`, { userName: user.name }),
|
||||
{ type: "success" }
|
||||
);
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(t("Could not remove user"), { type: "error" });
|
||||
ui.showToast(t("Could not remove user"), { type: "error" });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { group, users, groupMemberships, policies, t, auth } = this.props;
|
||||
const { user } = auth;
|
||||
if (!user) return null;
|
||||
|
||||
const can = policies.abilities(group.id);
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
{can.update ? (
|
||||
<>
|
||||
<HelpText>
|
||||
Add and remove team members in the <strong>{group.name}</strong>{" "}
|
||||
group. Adding people to the group will give them access to any
|
||||
collections this group has been added to.
|
||||
</HelpText>
|
||||
<span>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={this.handleAddModalOpen}
|
||||
icon={<PlusIcon />}
|
||||
neutral
|
||||
>
|
||||
{t("Add people")}…
|
||||
</Button>
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
return (
|
||||
<Flex column>
|
||||
{can.update ? (
|
||||
<>
|
||||
<HelpText>
|
||||
Listing team members in the <strong>{group.name}</strong> group.
|
||||
<Trans
|
||||
defaults="Add and remove team members in the <em>{{groupName}}</em> group. Adding people to the group will give them access to any collections this group has been added to."
|
||||
values={{ groupName: group.name }}
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
)}
|
||||
<span>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleAddModal(true)}
|
||||
icon={<PlusIcon />}
|
||||
neutral
|
||||
>
|
||||
{t("Add people")}…
|
||||
</Button>
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Listing team members in the <em>{{groupName}}</em> group."
|
||||
values={{ groupName: group.name }}
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
)}
|
||||
|
||||
<Subheading>Members</Subheading>
|
||||
<PaginatedList
|
||||
items={users.inGroup(group.id)}
|
||||
fetch={groupMemberships.fetchPage}
|
||||
options={{ id: group.id }}
|
||||
empty={<Empty>{t("This group has no members.")}</Empty>}
|
||||
renderItem={(item) => (
|
||||
<GroupMemberListItem
|
||||
key={item.id}
|
||||
user={item}
|
||||
onRemove={
|
||||
can.update ? () => this.handleRemoveUser(item) : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{can.update && (
|
||||
<Modal
|
||||
title={`Add people to ${group.name}`}
|
||||
onRequestClose={this.handleAddModalClose}
|
||||
isOpen={this.addModalOpen}
|
||||
>
|
||||
<AddPeopleToGroup
|
||||
group={group}
|
||||
onSubmit={this.handleAddModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
<Subheading>
|
||||
<Trans>Members</Trans>
|
||||
</Subheading>
|
||||
<PaginatedList
|
||||
items={users.inGroup(group.id)}
|
||||
fetch={groupMemberships.fetchPage}
|
||||
options={{ id: group.id }}
|
||||
empty={<Empty>{t("This group has no members.")}</Empty>}
|
||||
renderItem={(item) => (
|
||||
<GroupMemberListItem
|
||||
key={item.id}
|
||||
user={item}
|
||||
onRemove={can.update ? () => handleRemoveUser(item) : undefined}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
/>
|
||||
{can.update && (
|
||||
<Modal
|
||||
title={t(`Add people to {{groupName}}`, { groupName: group.name })}
|
||||
onRequestClose={() => handleAddModal(false)}
|
||||
isOpen={addModalOpen}
|
||||
>
|
||||
<AddPeopleToGroup
|
||||
group={group}
|
||||
onSubmit={() => handleAddModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default withTranslation()<GroupMembers>(
|
||||
inject("auth", "users", "policies", "groupMemberships", "ui")(GroupMembers)
|
||||
);
|
||||
export default observer(GroupMembers);
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import GroupsStore from "stores/GroupsStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import Group from "models/Group";
|
||||
import GroupMembers from "scenes/GroupMembers";
|
||||
import Button from "components/Button";
|
||||
|
@ -12,79 +9,80 @@ import Flex from "components/Flex";
|
|||
import HelpText from "components/HelpText";
|
||||
import Input from "components/Input";
|
||||
import Modal from "components/Modal";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
history: RouterHistory,
|
||||
ui: UiStore,
|
||||
groups: GroupsStore,
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer
|
||||
class GroupNew extends React.Component<Props> {
|
||||
@observable name: string = "";
|
||||
@observable isSaving: boolean;
|
||||
@observable group: Group;
|
||||
function GroupNew({ onSubmit }: Props) {
|
||||
const { ui, groups } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = React.useState();
|
||||
const [isSaving, setIsSaving] = React.useState();
|
||||
const [group, setGroup] = React.useState();
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
const handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
this.isSaving = true;
|
||||
setIsSaving(true);
|
||||
const group = new Group(
|
||||
{
|
||||
name: this.name,
|
||||
name: name,
|
||||
},
|
||||
this.props.groups
|
||||
groups
|
||||
);
|
||||
|
||||
try {
|
||||
this.group = await group.save();
|
||||
setGroup(await group.save());
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message, { type: "error" });
|
||||
ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
handleNameChange = (ev: SyntheticInputEvent<*>) => {
|
||||
this.name = ev.target.value;
|
||||
const handleNameChange = (ev: SyntheticInputEvent<*>) => {
|
||||
setName(ev.target.value);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HelpText>
|
||||
<Trans>
|
||||
Groups are for organizing your team. They work best when centered
|
||||
around a function or a responsibility — Support or Engineering for
|
||||
example.
|
||||
</HelpText>
|
||||
<Flex>
|
||||
<Input
|
||||
type="text"
|
||||
label="Name"
|
||||
onChange={this.handleNameChange}
|
||||
value={this.name}
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
/>
|
||||
</Flex>
|
||||
<HelpText>You’ll be able to add people to the group next.</HelpText>
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<Flex>
|
||||
<Input
|
||||
type="text"
|
||||
label="Name"
|
||||
onChange={handleNameChange}
|
||||
value={name}
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
/>
|
||||
</Flex>
|
||||
<HelpText>
|
||||
<Trans>You’ll be able to add people to the group next.</Trans>
|
||||
</HelpText>
|
||||
|
||||
<Button type="submit" disabled={this.isSaving || !this.name}>
|
||||
{this.isSaving ? "Creating…" : "Continue"}
|
||||
</Button>
|
||||
</form>
|
||||
<Modal
|
||||
title="Group members"
|
||||
onRequestClose={this.props.onSubmit}
|
||||
isOpen={!!this.group}
|
||||
>
|
||||
<GroupMembers group={this.group} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
<Button type="submit" disabled={isSaving || !name}>
|
||||
{isSaving ? `${t("Creating")}…` : t("Continue")}
|
||||
</Button>
|
||||
</form>
|
||||
<Modal
|
||||
title={t("Group members")}
|
||||
onRequestClose={onSubmit}
|
||||
isOpen={!!group}
|
||||
>
|
||||
<GroupMembers group={group} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default inject("groups", "ui")(withRouter(GroupNew));
|
||||
export default observer(GroupNew);
|
||||
|
|
|
@ -209,6 +209,7 @@
|
|||
"Contents": "Contents",
|
||||
"Headings you add to the document will appear here": "Headings you add to the document will appear here",
|
||||
"Table of contents": "Table of contents",
|
||||
"Contents": "Contents",
|
||||
"By {{ author }}": "By {{ author }}",
|
||||
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
||||
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
|
||||
|
@ -328,11 +329,20 @@
|
|||
"We were unable to load the document while offline.": "We were unable to load the document while offline.",
|
||||
"Your account has been suspended": "Your account has been suspended",
|
||||
"A team admin (<em>{{ suspendedContactEmail }}</em>) has suspended your account. To re-activate your account, please reach out to them directly.": "A team admin (<em>{{ suspendedContactEmail }}</em>) has suspended your account. To re-activate your account, please reach out to them directly.",
|
||||
"Are you sure about that? Deleting the <em>{{groupName}}</em> group will cause its members to lose access to collections and documents that it is associated with.": "Are you sure about that? Deleting the <em>{{groupName}}</em> group will cause its members to lose access to collections and documents that it is associated with.",
|
||||
"You can edit the name of this group at any time, however doing so too often might confuse your team mates.": "You can edit the name of this group at any time, however doing so too often might confuse your team mates.",
|
||||
"{{userName}} was added to the group": "{{userName}} was added to the group",
|
||||
"Add team members below to give them access to the group. Need to add someone who’s not yet on the team yet?": "Add team members below to give them access to the group. Need to add someone who’s not yet on the team yet?",
|
||||
"Invite them to {{teamName}}": "Invite them to {{teamName}}",
|
||||
"{{userName}} was removed from the group": "{{userName}} was removed from the group",
|
||||
"Add and remove team members in the <em>{{groupName}}</em> group. Adding people to the group will give them access to any collections this group has been added to.": "Add and remove team members in the <em>{{groupName}}</em> group. Adding people to the group will give them access to any collections this group has been added to.",
|
||||
"Listing team members in the <em>{{groupName}}</em> group.": "Listing team members in the <em>{{groupName}}</em> group.",
|
||||
"This group has no members.": "This group has no members.",
|
||||
"Add people to {{groupName}}": "Add people to {{groupName}}",
|
||||
"Groups are for organizing your team. They work best when centered around a function or a responsibility — Support or Engineering for example.": "Groups are for organizing your team. They work best when centered around a function or a responsibility — Support or Engineering for example.",
|
||||
"You’ll be able to add people to the group next.": "You’ll be able to add people to the group next.",
|
||||
"Continue": "Continue",
|
||||
"Group members": "Group members",
|
||||
"Recently viewed": "Recently viewed",
|
||||
"Created by me": "Created by me",
|
||||
"Navigation": "Navigation",
|
||||
|
|
Reference in New Issue