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:
Saumya Pandey 2021-07-13 00:24:55 +05:30 committed by GitHub
parent 5cd4dbd9d7
commit 5689d96cc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 221 additions and 233 deletions

View File

@ -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…" : "Im 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("Im sure  Delete")}
</Button>
</form>
</Flex>
);
}
export default inject("ui")(withRouter(GroupDelete));
export default observer(GroupDelete);

View File

@ -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);

View File

@ -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);

View File

@ -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>Youll 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>Youll 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);

View File

@ -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 whos not yet on the team yet?": "Add team members below to give them access to the group. Need to add someone whos 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.",
"Youll be able to add people to the group next.": "Youll 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",