Merge pull request #24 from ONLYOFFICE/feature/translations

Feature/translations
This commit is contained in:
Alexey Safronov 2020-02-02 11:28:03 +03:00 committed by GitHub
commit dde3cdd895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 667 additions and 655 deletions

View File

@ -7,7 +7,6 @@ import {
Icons
} from "asc-web-components";
import { selectGroup } from '../../../store/people/actions';
import { departments } from './../../../helpers/customNames';
const getItems = data => {
return data.map(item => {
@ -109,7 +108,7 @@ class ArticleBodyContent extends React.Component {
};
};
const getTreeGroups = (groups) => {
const getTreeGroups = (groups, departments) => {
const treeData = [
{
key: "root",
@ -128,7 +127,7 @@ const getTreeGroups = (groups) => {
function mapStateToProps(state) {
return {
data: getTreeGroups(state.people.groups),
data: getTreeGroups(state.people.groups, state.auth.settings.customNames.groupsCaption),
selectedKeys: state.people.selectedGroup ? [state.people.selectedGroup] : ["root"]
};
}

View File

@ -10,8 +10,8 @@ import {
import { InviteDialog } from './../../dialogs';
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from '../i18n';
import { typeUser, typeGuest, department } from './../../../helpers/customNames';
import { store } from 'asc-web-common';
import { store, utils } from 'asc-web-common';
const { changeLanguage } = utils;
const { isAdmin } = store.auth.selectors;
class PureArticleMainButtonContent extends React.Component {
@ -50,6 +50,7 @@ class PureArticleMainButtonContent extends React.Component {
render() {
console.log("People ArticleMainButtonContent render");
const { isAdmin, settings, t } = this.props;
const { userCaption, guestCaption, groupCaption } = settings.customNames;
const { dialogVisible } = this.state;
return (
isAdmin ?
@ -60,18 +61,18 @@ class PureArticleMainButtonContent extends React.Component {
text={t('Actions')}
>
<DropDownItem
icon="CatalogEmployeeIcon"
label={t('CustomNewEmployee', { typeUser })}
icon="AddEmployeeIcon"
label={userCaption}
onClick={this.goToEmployeeCreate}
/>
<DropDownItem
icon="CatalogGuestIcon"
label={t('CustomNewGuest', { typeGuest })}
icon="AddGuestIcon"
label={guestCaption}
onClick={this.goToGuestCreate}
/>
<DropDownItem
icon="CatalogDepartmentsIcon"
label={t('CustomNewDepartment', { department })}
icon="AddDepartmentIcon"
label={groupCaption}
onClick={this.goToGroupCreate}
/>
<DropDownItem isSeparator />
@ -110,8 +111,7 @@ class PureArticleMainButtonContent extends React.Component {
const ArticleMainButtonContentContainer = withTranslation()(PureArticleMainButtonContent);
const ArticleMainButtonContent = (props) => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return (<I18nextProvider i18n={i18n}><ArticleMainButtonContentContainer {...props} /></I18nextProvider>);
};
@ -123,7 +123,6 @@ ArticleMainButtonContent.propTypes = {
const mapStateToProps = (state) => {
return {
isAdmin: isAdmin(state.auth.user),
language: state.auth.user.cultureName || state.auth.settings.culture,
settings: state.auth.settings
}
}

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,16 +37,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -2,8 +2,5 @@
"InviteLinkTitle": "Invitation link",
"ImportPeople": "Import people",
"Actions": "Actions",
"LblInviteAgain": "Invite again",
"CustomNewEmployee": "New {{typeUser, lowercase}}",
"CustomNewGuest": "New {{typeGuest, lowercase}}",
"CustomNewDepartment": "New {{department, lowercase}}"
"LblInviteAgain": "Invite again"
}

View File

@ -2,8 +2,5 @@
"InviteLinkTitle": "Пригласительная ссылка",
"ImportPeople": "Импортировать людей",
"Actions": "Действия",
"LblInviteAgain": "Отправить приглашение ещё раз",
"CustomNewEmployee": "Новый {{typeUser, lowercase}}",
"CustomNewGuest": "Новый {{typeGuest, lowercase}}",
"CustomNewDepartment": "Новый {{department, lowercase}}"
"LblInviteAgain": "Отправить приглашение ещё раз"
}

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,7 +37,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
toastr,
ModalDialog,
@ -13,14 +11,15 @@ import {
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import ModalDialogContainer from '../ModalDialogContainer';
import { api } from "asc-web-common";
import { api, utils } from "asc-web-common";
const { sendInstructionsToChangeEmail } = api.people;
const { changeLanguage } = utils;
class ChangeEmailDialogComponent extends React.Component {
constructor(props) {
super(props);
const { user, language } = props;
const { user } = props;
const { email } = user;
this.state = {
@ -32,7 +31,7 @@ class ChangeEmailDialogComponent extends React.Component {
emailErrors: []
};
i18n.changeLanguage(language);
changeLanguage(i18n);
}
componentDidMount() {
@ -169,10 +168,5 @@ ChangeEmailDialog.propTypes = {
user: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
};
}
export default connect(mapStateToProps, {})(withRouter(ChangeEmailDialog));
export default ChangeEmailDialog;

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,7 +37,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
toastr,
ModalDialog,
@ -11,20 +9,19 @@ import {
} from "asc-web-components";
import { withTranslation, Trans } from "react-i18next";
import i18n from "./i18n";
import { api } from "asc-web-common";
import { api, utils } from "asc-web-common";
const { sendInstructionsToChangePassword } = api.people;
const { changeLanguage } = utils;
class ChangePasswordDialogComponent extends React.Component {
constructor(props) {
super(props);
const { language } = props;
constructor() {
super();
this.state = {
isRequestRunning: false
};
i18n.changeLanguage(language);
changeLanguage(i18n);
}
onSendPasswordChangeInstructions = () => {
const { email, onClose } = this.props;
@ -91,10 +88,4 @@ ChangePasswordDialog.propTypes = {
email: PropTypes.string.isRequired,
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
};
}
export default connect(mapStateToProps, {})(withRouter(ChangePasswordDialog));
export default ChangePasswordDialog;

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,7 +37,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
toastr,
ModalDialog,
@ -10,19 +8,18 @@ import {
} from "asc-web-components";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import { api } from "asc-web-common";
import { api, utils } from "asc-web-common";
const { changeLanguage } = utils;
class ChangePhoneDialogComponent extends React.Component {
constructor(props) {
super(props);
const { language } = props;
this.state = {
isRequestRunning: false
};
i18n.changeLanguage(language);
changeLanguage(i18n);
}
// TODO: add real api request for executing change phone
@ -48,7 +45,7 @@ class ChangePhoneDialogComponent extends React.Component {
headerContent={t('MobilePhoneChangeTitle')}
bodyContent={
<Text>
{t('MessageChangePhone')}
{t('MobilePhoneEraseDescription')}
</Text>
}
footerContent={
@ -78,10 +75,4 @@ ChangePhoneDialog.propTypes = {
user: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
};
}
export default connect(mapStateToProps, {})(withRouter(ChangePhoneDialog));
export default ChangePhoneDialog;

View File

@ -1,6 +1,5 @@
{
"SendButton": "Send",
"MobilePhoneChangeTitle": "Change mobile phone",
"MessageChangePhone": "The instructions on how to change the user mobile number will be sent to the user email address"
"MobilePhoneEraseDescription": "The instructions on how to change the user mobile number will be sent to the user email address"
}

View File

@ -1,6 +1,5 @@
{
"SendButton": "Отправить",
"MobilePhoneChangeTitle": "Изменение номера телефона",
"MessageChangePhone": "Инструкция по смене номера мобильного телефона будет отправлена на Вашу почту"
"MobilePhoneEraseDescription": "Инструкция по смене номера мобильного телефона будет отправлена на Вашу почту"
}

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,7 +37,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import PropTypes from "prop-types";
import {
toastr,
ModalDialog,
@ -10,24 +10,22 @@ import {
} from "asc-web-components";
import { withTranslation, Trans } from "react-i18next";
import i18n from "./i18n";
import { api } from "asc-web-common";
import { typeUser } from "../../../helpers/customNames";
import { api, utils } from "asc-web-common";
import { fetchPeople } from '../../../store/people/actions';
import ModalDialogContainer from '../ModalDialogContainer';
const { deleteUser } = api.people;
const { Filter } = api;
const { changeLanguage } = utils;
class DeleteProfileEverDialogComponent extends React.Component {
constructor(props) {
super(props);
const { language } = props;
this.state = {
isRequestRunning: false
};
i18n.changeLanguage(language);
changeLanguage(i18n);
}
onDeleteProfileEver = () => {
const { onClose, filter, fetchPeople, user, t } = this.props;
@ -51,7 +49,7 @@ class DeleteProfileEverDialogComponent extends React.Component {
render() {
console.log("DeleteProfileEverDialog render");
const { t, visible, user, onClose } = this.props;
const { t, visible, user, onClose, userCaption } = this.props;
const { isRequestRunning } = this.state;
return (
@ -64,7 +62,7 @@ class DeleteProfileEverDialogComponent extends React.Component {
<>
<Text>
<Trans i18nKey='DeleteUserConfirmation' i18n={i18n}>
{{ typeUser }} <strong>{{ user: user.displayName }}</strong> will be deleted.
{{ userCaption }} <strong>{{ user: user.displayName }}</strong> will be deleted.
</Trans>
</Text>
<Text>{t('NotBeUndone')}</Text>
@ -120,7 +118,7 @@ DeleteProfileEverDialog.propTypes = {
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
userCaption: state.auth.settings.customNames.userCaption
};
}

View File

@ -1,6 +1,6 @@
{
"Confirmation": "Confirmation",
"DeleteUserConfirmation": "{{typeUser}} <strong>{{user}}</strong> will be deleted.",
"DeleteUserConfirmation": "{{userCaption}} <strong>{{user}}</strong> will be deleted.",
"NotBeUndone": "Note: this action cannot be undone.",
"DeleteUserDataConfirmation": "User's personal documents that are available to others will be deleted. To avoid this, you must start the data reassign process before deleting.",
"OKButton": "OK",

View File

@ -1,6 +1,6 @@
{
"Confirmation": "Подтверждение",
"DeleteUserConfirmation": "{{typeUser}} <strong>{{user}}</strong> будет удален.",
"DeleteUserConfirmation": "{{userCaption}} <strong>{{user}}</strong> будет удален.",
"NotBeUndone": "Внимание: это действие необратимо.",
"DeleteUserDataConfirmation": "Будут удалены личные документы пользователя, доступные для других. Чтобы избежать этого, нужно перед удалением запустить процесс передачи данных.",
"OKButton": "OK",

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,15 +10,11 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,7 +37,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
toastr,
ModalDialog,
@ -12,20 +10,19 @@ import {
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import ModalDialogContainer from '../ModalDialogContainer';
import { api } from "asc-web-common";
import { api, utils } from "asc-web-common";
const { sendInstructionsToDelete } = api.people;
const { changeLanguage } = utils;
class DeleteSelfProfileDialogComponent extends React.Component {
constructor(props) {
super(props);
const { language } = props;
this.state = {
isRequestRunning: false
};
i18n.changeLanguage(language);
changeLanguage(i18n);
}
onDeleteSelfProfileInstructions = () => {
const { onClose } = this.props;
@ -98,10 +95,4 @@ DeleteSelfProfileDialog.propTypes = {
email: PropTypes.string.isRequired,
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
};
}
export default connect(mapStateToProps, {})(withRouter(DeleteSelfProfileDialog));
export default DeleteSelfProfileDialog;

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,7 +10,7 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
@ -39,7 +41,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,7 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
toastr,
ModalDialog,
@ -13,39 +12,37 @@ import {
} from "asc-web-components";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import { typeGuests } from "./../../../helpers/customNames";
import ModalDialogContainer from '../ModalDialogContainer';
import copy from "copy-to-clipboard";
import { api } from "asc-web-common";
import { api, utils } from "asc-web-common";
const { getShortenedLink } = api.portal;
const { changeLanguage } = utils;
const textAreaName = "link-textarea";
class InviteDialogComponent extends React.Component {
constructor(props) {
super(props);
const { language, userInvitationLink, guestInvitationLink } = this.props;
const { userInvitationLink, guestInvitationLink } = props;
this.state = {
isGuest: false,
userInvitationLink,
guestInvitationLink,
isLoading: false,
isLinkShort: false,
visible: false
visible: false,
};
i18n.changeLanguage(language);
}
onCopyLinkToClipboard = () => {
// console.log("COPY");
// console.log("COPY", this.props);
const { t } = this.props;
copy(
this.state.isGuest
? this.state.guestInvitationLink
: this.state.userInvitationLink
);
toastr.success(t("LinkCopySuccess"));
};
@ -79,7 +76,12 @@ class InviteDialogComponent extends React.Component {
};
componentDidMount() {
this.onCopyLinkToClipboard();
const { t } = this.props;
copy(this.state.userInvitationLink);
changeLanguage(i18n)
.then(()=> this.setState({visible: true}))
.then(()=> toastr.success(t("LinkCopySuccess")));
}
onClickToCloseButton = () =>
@ -88,9 +90,10 @@ class InviteDialogComponent extends React.Component {
render() {
console.log("InviteDialog render");
const { t, visible, settings } = this.props;
const { t, visible, settings, guestsCaption } = this.props;
return (
this.state.visible &&
<ModalDialogContainer>
<ModalDialog
visible={visible}
@ -125,7 +128,7 @@ class InviteDialogComponent extends React.Component {
)}
</div>
<Checkbox
label={t("InviteUsersAsCollaborators", { typeGuests })}
label={t("InviteUsersAsCollaborators", { guestsCaption })}
isChecked={this.state.isGuest}
onChange={this.onCheckedGuest}
isDisabled={this.state.isLoading}
@ -171,7 +174,7 @@ const mapStateToProps = state => {
settings: state.auth.settings.hasShortenService,
userInvitationLink: state.portal.inviteLinks.userLink,
guestInvitationLink: state.portal.inviteLinks.guestLink,
language: state.auth.user.cultureName,
guestsCaption: state.auth.settings.customNames.guestsCaption
};
};
@ -187,4 +190,4 @@ InviteDialog.propTypes = {
onCloseButton: PropTypes.func.isRequired
};
export default connect(mapStateToProps)(withRouter(InviteDialog));
export default connect(mapStateToProps)(InviteDialog);

View File

@ -6,6 +6,6 @@
"CloseButton": "Close",
"LinkCopySuccess": "Link has been copied to the clipboard",
"GetShortenLink": "Get shortened link",
"InviteUsersAsCollaborators": "Add users as {{typeGuests, lowercase}}",
"InviteUsersAsCollaborators": "Add users as {{guestsCaption, lowercase}}",
"LoadingProcessing": "Loading..."
}

View File

@ -6,6 +6,6 @@
"CloseButton": "Закрыть",
"LinkCopySuccess": "Ссылка скопирована в буфер обмена",
"GetShortenLink": "Получить сокращенную ссылку",
"InviteUsersAsCollaborators": "Добавить со статусом {{typeGuests, lowercase}}",
"InviteUsersAsCollaborators": "Добавить со статусом {{guestsCaption, lowercase}}",
"LoadingProcessing": "Загрузка..."
}

View File

@ -15,11 +15,6 @@ import {
resetGroup,
updateGroup
} from "../../../../../store/group/actions";
import {
department,
headOfDepartment,
typeUser
} from "../../../../../helpers/customNames";
import { GUID_EMPTY } from "../../../../../helpers/constants";
import PropTypes from "prop-types";
@ -87,12 +82,16 @@ class SectionBodyContent extends React.Component {
mapPropsToState = () => {
const { group, users, groups, t } = this.props;
const buttonLabel = group
? t('SaveButton')
: t("AddButton");
const newState = {
id: group ? group.id : "",
groupName: group ? group.name : "",
searchValue: "",
error: null,
buttonLabel,
inLoading: false,
isHeadSelectorOpen: false,
isUsersSelectorOpen: false,
@ -105,7 +104,7 @@ class SectionBodyContent extends React.Component {
}
: {
key: 0,
label: t("CustomAddEmployee", { typeUser })
label: t("LblSelect")
},
groupMembers:
group && group.members
@ -124,7 +123,7 @@ class SectionBodyContent extends React.Component {
}
: {
key: GUID_EMPTY,
label: t("CustomAddEmployee", { typeUser }),
label: t("LblSelect"),
default: true
}
};
@ -191,7 +190,7 @@ class SectionBodyContent extends React.Component {
};
onSave = () => {
const { group } = this.props;
const { group, t, groupCaption } = this.props;
const { groupName, groupManager, groupMembers } = this.state;
if (!groupName || !groupName.trim().length) return false;
@ -208,7 +207,7 @@ class SectionBodyContent extends React.Component {
this.save(newGroup)
.then(group => {
toastr.success(`Group '${group.name}' has been saved successfully`);
toastr.success(t('SuccessSaveGroup', {groupCaption, groupName: group.name }));
})
.catch(error => {
toastr.error(error);
@ -255,7 +254,7 @@ class SectionBodyContent extends React.Component {
}
render() {
const { t } = this.props;
const { t, groupHeadCaption, groupsCaption } = this.props;
const {
groupName,
groupMembers,
@ -264,7 +263,8 @@ class SectionBodyContent extends React.Component {
inLoading,
error,
searchValue,
groupManager
groupManager,
buttonLabel
} = this.state;
return (
<MainContainer>
@ -273,7 +273,7 @@ class SectionBodyContent extends React.Component {
isRequired={true}
hasError={false}
isVertical={true}
labelText={t("CustomDepartmentName", { department })}
labelText={t("Name")}
>
<TextInput
id="group-name"
@ -293,7 +293,7 @@ class SectionBodyContent extends React.Component {
isRequired={false}
hasError={false}
isVertical={true}
labelText={t("CustomHeadOfDepartment", { headOfDepartment })}
labelText={groupHeadCaption}
>
<ComboBox
id="head-selector_button"
@ -313,6 +313,7 @@ class SectionBodyContent extends React.Component {
isOpen={isHeadSelectorOpen}
onSelect={this.onHeadSelectorSelect}
onCancel={this.onCancelSelector}
groupsCaption={groupsCaption}
/>
</FieldContainer>
<FieldContainer
@ -345,6 +346,8 @@ class SectionBodyContent extends React.Component {
isMultiSelect={true}
onSelect={this.onUsersSelectorSelect}
onCancel={this.onCancelSelector}
searchPlaceHolderLabel={t('SearchAddedMembers')}
groupsCaption={groupsCaption}
/>
</FieldContainer>
{groupMembers && groupMembers.length > 0 && (
@ -376,7 +379,7 @@ class SectionBodyContent extends React.Component {
{error && <div><strong>{error}</strong></div>}
<div className="buttons_container">
<Button
label={t("SaveButton")}
label={buttonLabel}
primary
type="submit"
isLoading={inLoading}
@ -435,7 +438,10 @@ function mapStateToProps(state) {
settings: state.auth.settings,
group: state.group.targetGroup,
groups: convertGroups(state.people.groups),
users: convertUsers(state.people.selector.users) //TODO: replace to api requests with search
users: convertUsers(state.people.selector.users), //TODO: replace to api requests with search
groupHeadCaption: state.auth.settings.customNames.groupHeadCaption,
groupsCaption: state.auth.settings.customNames.groupsCaption,
groupCaption: state.auth.settings.customNames.groupCaption
};
}

View File

@ -5,7 +5,6 @@ import PropTypes from "prop-types";
import { IconButton } from "asc-web-components";
import { Headline } from "asc-web-common";
import { withTranslation } from "react-i18next";
import { department } from "./../../../../../helpers/customNames";
import { resetGroup } from "../../../../../store/group/actions";
import styled from "styled-components";
@ -24,6 +23,18 @@ const Wrapper = styled.div`
`;
class SectionHeaderContent extends React.Component {
constructor(props) {
super(props);
const { group, t, groupCaption } = props;
const headerText = group
? group.name
: t("CustomNewDepartment", { groupCaption });
this.state = {
headerText
}
}
onClickBack = () => {
const { history, settings, resetGroup } = this.props;
@ -32,10 +43,7 @@ class SectionHeaderContent extends React.Component {
};
render() {
const { group, t } = this.props;
const headerText = group
? t("CustomEditDepartment", { department })
: t("CustomNewDepartment", { department });
const { headerText } = this.state;
return (
<Wrapper>
<IconButton
@ -67,7 +75,8 @@ SectionHeaderContent.defaultProps = {
function mapStateToProps(state) {
return {
settings: state.auth.settings,
group: state.group.targetGroup
group: state.group.targetGroup,
groupCaption: state.auth.settings.customNames.groupCaption
};
}

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,7 +10,7 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
@ -40,7 +42,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -1,12 +1,13 @@
import React from "react";
import { connect } from "react-redux";
import { Loader } from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { PageLayout, utils } from "asc-web-common";
import { ArticleHeaderContent, ArticleMainButtonContent, ArticleBodyContent } from '../../Article';
import { SectionHeaderContent, SectionBodyContent } from './Section';
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
import { fetchGroup, resetGroup } from "../../../store/group/actions";
const { changeLanguage } = utils;
class GroupAction extends React.Component {
@ -32,9 +33,9 @@ class GroupAction extends React.Component {
render() {
console.log("GroupAction render")
const { group, match, language } = this.props;
const { group, match } = this.props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return (
<I18nextProvider i18n={i18n}>
@ -62,7 +63,6 @@ class GroupAction extends React.Component {
function mapStateToProps(state) {
return {
settings: state.auth.settings,
language: state.auth.user.cultureName || state.auth.settings.culture,
group: state.group.targetGroup
};
}

View File

@ -1,15 +1,14 @@
{
"SaveButton": "Save",
"CancelButton": "Cancel",
"CustomHeadOfDepartment": "{{headOfDepartment}}",
"CustomAddEmployee": "Add {{typeUser, lowercase}}",
"CustomNewDepartment": "New {{department, lowercase}}",
"CustomEditDepartment": "Edit {{department, lowercase}}",
"CustomDepartmentName": "{{department}} name",
"Members": "Members",
"AddMembers": "Add members",
"LblSelect": "Select",
"Name": "Name",
"AddButton": "Add",
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' has been saved successfully",
"CustomNewDepartment": "{{groupCaption}} (creation)",
"SearchAddedMembers": "Search added members"
}

View File

@ -1,15 +1,14 @@
{
"SaveButton": "Сохранить",
"CancelButton": "Отмена",
"CustomHeadOfDepartment": "{{headOfDepartment}}",
"CustomAddEmployee": "Добавить {{typeUser, lowercase}}",
"CustomNewDepartment": "Новый {{department, lowercase}}",
"CustomEditDepartment": "Редактирование {{department, lowercase}}",
"CustomDepartmentName": "Имя {{department}}",
"Members": "Участники",
"AddMembers": "Добавить участников",
"LblSelect": "Выбрать",
"Name": "Название",
"AddButton": "Добавить",
"SuccessSaveGroup": "Успешное сохранение: {{groupCaption, lowercase}} '{{ groupName }}'",
"CustomNewDepartment": "{{groupCaption}} (создание)",
"SearchAddedMembers": "Поиск добавленных участников"
}

View File

@ -11,6 +11,7 @@ import {
Link,
RowContainer,
Text,
utils
} from "asc-web-components";
import UserContent from "./userContent";
import {
@ -32,6 +33,7 @@ import { Loader } from "asc-web-components";
import { store, api, constants } from 'asc-web-common';
import i18n from '../../i18n';
import { ChangeEmailDialog, ChangePasswordDialog, DeleteSelfProfileDialog, DeleteProfileEverDialog } from '../../../../dialogs';
const { isArrayEqual } = utils.array;
const { isAdmin, isMe } = store.auth.selectors;
const { resendUserInvites } = api.people;
const { EmployeeStatus } = constants;
@ -303,6 +305,9 @@ class SectionBodyContent extends React.PureComponent {
if (!isEqual(currentProps.data, nextProps.data)) {
return true;
}
if (!isArrayEqual(currentProps.contextOptions, nextProps.contextOptions)) {
return true;
}
return false;
};

View File

@ -5,11 +5,6 @@ import { fetchPeople } from "../../../../../store/people/actions";
import find from "lodash/find";
import result from "lodash/result";
import { withTranslation } from "react-i18next";
import {
typeGuest,
typeUser,
department
} from "./../../../../../helpers/customNames";
import { withRouter } from "react-router";
import { getFilterByLocation } from "../../../../../helpers/converters";
import { store } from 'asc-web-common';
@ -65,7 +60,7 @@ class SectionFilterContent extends React.Component {
const newFilter = getFilterByLocation(location);
if(!newFilter || newFilter.equals(filter)) return;
if (!newFilter || newFilter.equals(filter)) return;
onLoading(true);
fetchPeople(newFilter).finally(() => onLoading(false));
@ -98,28 +93,29 @@ class SectionFilterContent extends React.Component {
};
getData = () => {
const { user, groups, t } = this.props;
const { user, groups, t, settings } = this.props;
const { guestCaption, userCaption, groupCaption } = settings.customNames;
const options = !isAdmin(user)
? []
: [
{
key: "filter-status",
group: "filter-status",
label: t("UserStatus"),
isHeader: true
},
{
key: "1",
group: "filter-status",
label: t("LblActive")
},
{
key: "2",
group: "filter-status",
label: t("LblTerminated")
}
];
{
key: "filter-status",
group: "filter-status",
label: t("UserStatus"),
isHeader: true
},
{
key: "1",
group: "filter-status",
label: t("LblActive")
},
{
key: "2",
group: "filter-status",
label: t("LblTerminated")
}
];
const groupOptions = groups.map(group => {
return {
@ -158,12 +154,12 @@ class SectionFilterContent extends React.Component {
{
key: "user",
group: "filter-type",
label: t("CustomTypeUser", { typeUser })
label: userCaption
},
{
key: "guest",
group: "filter-type",
label: t("CustomTypeGuest", { typeGuest })
label: guestCaption
},
{
key: "filter-other",
@ -175,8 +171,8 @@ class SectionFilterContent extends React.Component {
key: "filter-type-group",
group: "filter-other",
subgroup: "filter-group",
label: t("CustomDepartment", { department }),
defaultSelectLabel: t("DefaultSelectLabel")
label: groupCaption,
defaultSelectLabel: t("LblSelect")
},
...groupOptions
];
@ -236,9 +232,17 @@ class SectionFilterContent extends React.Component {
return selectedFilterData;
};
needForUpdate = (currentProps, nextProps) => {
if (currentProps.language !== nextProps.language) {
return true;
}
return false;
};
render() {
const selectedFilterData = this.getSelectedFilterData();
const { t } = this.props;
const { t, i18n } = this.props;
return (
<FilterInput
getFilterData={this.getData}
@ -247,7 +251,9 @@ class SectionFilterContent extends React.Component {
onFilter={this.onFilter}
directionAscLabel={t("DirectionAscLabel")}
directionDescLabel={t("DirectionDescLabel")}
placeholder={t("FilterPlaceholder")}
placeholder={t("Search")}
needForUpdate={this.needForUpdate}
language={i18n.language}
/>
);
}

View File

@ -19,11 +19,6 @@ import {
updateUserType,
fetchPeople
} from "../../../../../store/people/actions";
import {
typeUser,
typeGuest,
department
} from "../../../../../helpers/../helpers/customNames";
import { deleteGroup } from "../../../../../store/group/actions";
import { store, api, constants } from 'asc-web-common';
import { InviteDialog } from '../../../../dialogs';
@ -112,20 +107,20 @@ const SectionHeaderContent = props => {
const onSentInviteAgain = useCallback(() => {
resendUserInvites(selectedUserIds)
.then(() => toastr.success("The invitation was successfully sent"))
.then(() => toastr.success(t('SuccessSendInvitation')))
.catch(error => toastr.error(error));
}, [selectedUserIds]);
}, [selectedUserIds, t]);
const onDelete = useCallback(() => {
onLoading(true);
deleteUsers(selectedUserIds)
.then(() => {
toastr.success("Users have been removed successfully");
toastr.success(t('SuccessfullyRemovedUsers'));
return fetchPeople(filter);
})
.catch(error => toastr.error(error))
.finally(() => onLoading(false));
}, [selectedUserIds, onLoading, filter]);
}, [selectedUserIds, onLoading, filter, t]);
const menuItems = [
{
@ -142,12 +137,12 @@ const SectionHeaderContent = props => {
onSelect: item => onSelect(item.key)
},
{
label: t("CustomMakeUser", { typeUser }),
label: t('ChangeToUser', { userCaption: settings.customNames.userCaption }),
disabled: !selection.length,
onClick: onSetEmployee
},
{
label: t("CustomMakeGuest", { typeGuest }),
label: t('ChangeToGuest', { guestCaption: settings.customNames.guestCaption }),
disabled: !selection.length,
onClick: onSetGuest
},
@ -169,7 +164,7 @@ const SectionHeaderContent = props => {
{
label: t("LblSendEmail"),
disabled: !selection.length,
onClick: toastr.success.bind(this, "Send e-mail action")
onClick: toastr.success.bind(this, t("SendEmailAction"))
},
{
label: t("DeleteButton"),
@ -185,9 +180,9 @@ const SectionHeaderContent = props => {
const onDeleteGroup = useCallback(() => {
deleteGroup(group.id).then(() =>
toastr.success("Group has been removed successfully")
toastr.success(t("SuccessfullyRemovedGroup"))
);
}, [deleteGroup, group]);
}, [deleteGroup, group, t]);
const getContextOptionsGroup = useCallback(() => {
return [
@ -221,20 +216,21 @@ const SectionHeaderContent = props => {
);
const getContextOptionsPlus = useCallback(() => {
const { guestCaption, userCaption, groupCaption } = settings.customNames;
return [
{
key: "new-employee",
label: t("CustomNewEmployee", { typeUser }),
label: userCaption,
onClick: goToEmployeeCreate
},
{
key: "new-guest",
label: t("CustomNewGuest", { typeGuest }),
label: guestCaption,
onClick: goToGuestCreate
},
{
key: "new-group",
label: t("CustomNewDepartment", { department }),
label: groupCaption,
onClick: goToGroupCreate
},
{ key: 'separator', isSeparator: true },
@ -245,11 +241,11 @@ const SectionHeaderContent = props => {
}/* ,
{
key: "send-invitation",
label: t("SendInvitationAgain"),
label: t("SendInviteAgain"),
onClick: onSentInviteAgain
} */
];
}, [t, goToEmployeeCreate, goToGuestCreate, goToGroupCreate, onInvitationDialogClick/* , onSentInviteAgain */]);
}, [settings, t, goToEmployeeCreate, goToGuestCreate, goToGroupCreate, onInvitationDialogClick/* , onSentInviteAgain */]);
return (
<StyledContainer isHeaderVisible={isHeaderVisible}>
@ -287,7 +283,7 @@ const SectionHeaderContent = props => {
</>
) : (
<>
<Headline className='headline-header' truncate={true} type="content">Departments</Headline>
<Headline className='headline-header' truncate={true} type="content">{settings.customNames.groupsCaption}</Headline>
{isAdmin && (
<>
<ContextMenuButton

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,16 +10,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -40,16 +38,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -3,7 +3,7 @@ import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { RequestLoader } from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { PageLayout, utils } from "asc-web-common";
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from "./i18n";
@ -19,6 +19,7 @@ import {
SectionPagingContent
} from "./Section";
import { setSelected } from "../../../store/people/actions";
const { changeLanguage } = utils;
class PureHome extends React.Component {
constructor(props) {
@ -148,8 +149,7 @@ class PureHome extends React.Component {
const HomeContainer = withTranslation()(PureHome);
const Home = (props) => {
const {language} = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return (<I18nextProvider i18n={i18n}><HomeContainer {...props}/></I18nextProvider>);
}
@ -164,8 +164,7 @@ function mapStateToProps(state) {
users: state.people.users,
selection: state.people.selection,
selected: state.people.selected,
isLoaded: state.auth.isLoaded,
language: state.auth.user.cultureName || state.auth.settings.culture,
isLoaded: state.auth.isLoaded
};
}

View File

@ -32,6 +32,15 @@
"People": "People",
"PreviousPage": "Previous",
"NextPage": "Next",
"MessageEmailActivationInstuctionsSentOnEmail": "The email activation instructions have been sent to the <1>{{email}}</1> email address",
"Search": "Search",
"SendInviteAgain": "Send invitation once again",
"SuccessSendInvitation": "The invitation was successfully sent",
"CountPerPage": "{{count}} per page",
"PageOfTotalPage": "{{page}} of {{totalPage}}",
"ChangeToUser": "Change to {{userCaption}}",
"ChangeToGuest": "Change to {{guestCaption}}",
"LblInviteAgain": "Invite again",
"ByFirstNameSorting": "By first name",
@ -39,27 +48,10 @@
"LblInvited": "Invited",
"LblSetActive": "Set active",
"LblSetDisabled": "Set disabled",
"MessageEmailActivationInstuctionsSentOnEmail": "The email activation instructions have been sent to the <1>{{email}}</1> email address",
"CustomHeadOfDepartment": " {{headOfDepartment}}",
"CustomTypeGuest": "{{typeGuest}}",
"CustomTypeUser": "{{typeUser}}",
"CustomMakeUser": "Make {{typeUser, lowercase}}",
"CustomMakeGuest": "Make {{typeGuest, lowercase}}",
"CustomDepartment": "{{department}}",
"CountPerPage": "{{count}} per page",
"PageOfTotalPage": "{{page}} of {{totalPage}}",
"DirectionAscLabel":"A-Z",
"DirectionDescLabel":"Z-A",
"DefaultSelectLabel": "Select",
"FilterPlaceholder": "Search",
"CustomNewEmployee": "New {{typeUser, lowercase}}",
"CustomNewGuest": "New {{typeGuest, lowercase}}",
"CustomNewDepartment": "New {{department, lowercase}}",
"MakeInvitationLink": "Make invitation link",
"SendInvitationAgain": "Send invitation once"
"SuccessfullyRemovedUsers": "Users have been removed successfully",
"SendEmailAction": "Send e-mail action",
"SuccessfullyRemovedGroup": "Group has been removed successfully"
}

View File

@ -32,34 +32,26 @@
"People": "Люди",
"PreviousPage": "Предыдущая",
"NextPage": "Следующая",
"MessageEmailActivationInstuctionsSentOnEmail": "Инструкции по активации электронной почты были отправлены на адрес <1>{{email}}</1>",
"Search": "Поиск",
"SendInviteAgain": "Отправить приглашение ещё раз",
"SuccessSendInvitation": "Приглашение успешно отправлено",
"PageOfTotalPage": "{{page}} из {{totalPage}}",
"CountPerPage": "{{count}} на странице",
"ChangeToUser": "Изменить на {{userCaption}}",
"ChangeToGuest": "Изменить на {{guestCaption}}",
"LblInviteAgain": "Выслать прилашение ещё раз",
"ByFirstNameSorting": "По имени",
"ByLastNameSorting": "По фамилии",
"LblInvited": "Invited",
"LblInvited": "Приглашен",
"LblSetActive": "Разблокировать",
"LblSetDisabled": "Заблокировать",
"MessageEmailActivationInstuctionsSentOnEmail": "Инструкции по активации электронной почты были отправлены на адрес <1>{{email}}</1>",
"CustomHeadOfDepartment": " {{headOfDepartment}}",
"CustomTypeGuest": "{{typeGuest}}",
"CustomTypeUser": "{{typeUser}}",
"CustomMakeUser": "Сделать {{typeUser, lowercase}}",
"CustomMakeGuest": "Сделать {{typeGuest, lowercase}}",
"CustomDepartment": "{{department}}",
"CountPerPage": "{{count}} на странице",
"PageOfTotalPage": "{{page}} из {{totalPage}}",
"DirectionAscLabel":"А-Я",
"DirectionDescLabel":"Я-А",
"DefaultSelectLabel": "Выберите",
"FilterPlaceholder": "Поиск",
"CustomNewEmployee": "Новый {{typeUser, lowercase}}",
"CustomNewGuest": "Новый {{typeGuest, lowercase}}",
"CustomNewDepartment": "Новый {{department, lowercase}}",
"MakeInvitationLink": "Создать пригласительную ссылку",
"SendInvitationAgain": "Отправить приглашение ещё раз"
"SuccessfullyRemovedUsers": "Пользователи были успешно удалены",
"SendEmailAction": "Действие отправки e-mail",
"SuccessfullyRemovedGroup": "Группа была успешно удалена"
}

View File

@ -1,6 +1,5 @@
import React from "react";
import { Trans } from 'react-i18next';
import { department as departmentName, position, employedSinceDate } from '../../../../../../helpers/customNames';
import {
Text,
IconButton,
@ -10,8 +9,11 @@ import {
HelpButton
} from "asc-web-components";
import styled from 'styled-components';
import { history, api } from "asc-web-common";
import { history, api, store as commonStore } from "asc-web-common";
import { connect } from "react-redux";
import store from "../../../../../../store/store";
const { resendUserInvites } = api.people;
const { getCurrentCustomSchema, getModules } = commonStore.auth.actions;
const InfoContainer = styled.div`
margin-bottom: 24px;
@ -123,11 +125,13 @@ class ProfileInfo extends React.PureComponent {
onLanguageSelect = (language) => {
console.log("onLanguageSelect", language);
const { profile, updateProfileCulture } = this.props;
const { profile, updateProfileCulture, nameSchemaId } = this.props;
if (profile.cultureName === language.key) return;
updateProfileCulture(profile.id, language.key);
updateProfileCulture(profile.id, language.key)
.then(() => getModules(store.dispatch))
.then(() => getCurrentCustomSchema(store.dispatch, nameSchemaId));
}
getLanguages = () => {
@ -142,8 +146,8 @@ class ProfileInfo extends React.PureComponent {
const { isVisitor, email, activationStatus, department, groups, title, mobilePhone, sex, workFrom, birthday, location, cultureName, currentCulture } = this.props.profile;
const isAdmin = this.props.isAdmin;
const isSelf = this.props.isSelf;
const { t, i18n } = this.props;
const type = isVisitor ? "Guest" : "Employee";
const { t, i18n, userPostCaption, regDateCaption, groupCaption, userCaption, guestCaption } = this.props;
const type = isVisitor ? guestCaption : userCaption;
const language = cultureName || currentCulture || this.props.culture;
const languages = this.getLanguages();
const selectedLanguage = languages.find(item => item.key === language);
@ -238,7 +242,7 @@ class ProfileInfo extends React.PureComponent {
{title &&
<InfoItem>
<InfoItemLabel>
{t("CustomPosition", { position })}:
{userPostCaption}:
</InfoItemLabel>
<InfoItemValue>
{title}
@ -248,7 +252,7 @@ class ProfileInfo extends React.PureComponent {
{department &&
<InfoItem>
<InfoItemLabel>
{t("CustomDepartment", { department: departmentName })}:
{groupCaption}:
</InfoItemLabel>
<InfoItemValue>
{formatedDepartments}
@ -268,7 +272,7 @@ class ProfileInfo extends React.PureComponent {
{workFrom &&
<InfoItem>
<InfoItemLabel>
{t("CustomEmployedSinceDate", { employedSinceDate })}:
{regDateCaption}:
</InfoItemLabel>
<InfoItemValue>
{workFromDate}
@ -293,13 +297,13 @@ class ProfileInfo extends React.PureComponent {
className='language-combo'
/>
<HelpButton
place="bottom"
offsetLeft={50}
offsetRight={0}
tooltipContent={tooltipLanguage}
helpButtonHeaderContent={t('Language')}
className="help-icon"
/>
place="bottom"
offsetLeft={50}
offsetRight={0}
tooltipContent={tooltipLanguage}
helpButtonHeaderContent={t('Language')}
className="help-icon"
/>
</InfoItemValue>
</InfoItem>
@ -309,4 +313,15 @@ class ProfileInfo extends React.PureComponent {
}
};
export default ProfileInfo;
function mapStateToProps(state) {
return {
groupCaption: state.auth.settings.customNames.groupCaption,
regDateCaption: state.auth.settings.customNames.regDateCaption,
userPostCaption: state.auth.settings.customNames.userPostCaption,
userCaption: state.auth.settings.customNames.userCaption,
guestCaption: state.auth.settings.customNames.guestCaption,
nameSchemaId: state.auth.settings.nameSchemaId
}
}
export default connect(mapStateToProps)(ProfileInfo);

View File

@ -406,9 +406,9 @@ class SectionHeaderContent extends React.PureComponent {
headerLabel={t("editAvatar")}
chooseFileLabel={t("chooseFileLabel")}
chooseMobileFileLabel={t("chooseMobileFileLabel")}
unknownTypeError={t("unknownTypeError")}
unknownTypeError={t("ErrorUnknownFileImageType")}
maxSizeFileError={t("maxSizeFileError")}
unknownError={t("unknownError")}
unknownError={t("Error")}
/>
{dialogsVisible.deleteSelfProfile &&

View File

@ -1,12 +1,14 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance.use(Backend).init({
lng: "en",
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
@ -33,7 +35,7 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: "en",
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -2,12 +2,13 @@ import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { Loader, toastr } from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { PageLayout, utils } from "asc-web-common";
import { ArticleHeaderContent, ArticleMainButtonContent, ArticleBodyContent } from '../../Article';
import { SectionHeaderContent, SectionBodyContent } from './Section';
import { fetchProfile } from '../../../store/profile/actions';
import i18n from "./i18n";
import { I18nextProvider, withTranslation } from "react-i18next";
const { changeLanguage } = utils;
class PureProfile extends React.Component {
@ -68,9 +69,8 @@ class PureProfile extends React.Component {
const ProfileContainer = withTranslation()(PureProfile);
const Profile = (props) => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return <I18nextProvider i18n={i18n}><ProfileContainer {...props} /></I18nextProvider>
};
@ -86,7 +86,6 @@ Profile.propTypes = {
function mapStateToProps(state) {
return {
profile: state.profile.targetUser,
language: state.auth.user.cultureName || state.auth.settings.culture,
isVisitor: state.auth.user.isVisitor,
};
}

View File

@ -23,26 +23,21 @@
"Actions": "Actions",
"ChangeEmailSuccess": "Mail has been successfully changed",
"NotFoundLanguage": "In case you cannot find your language in the list of the available ones, feel free to write to us at <1>{{supportEmail}}</1> to take part in the translation and get up to 1 year free of charge.",
"LearnMore": "Learn more...",
"ErrorUnknownFileImageType": "Unknown image file type",
"Error": "Error",
"SocialProfiles": "Social profiles",
"PhoneChange": "Change phone",
"PhoneLbl": "Phone",
"EditSubscriptionsBtn": "Edit subscriptions",
"InviteAgainLbl": "Invite again",
"LDAPLbl": "LDAP",
"SocialProfiles": "Social profiles",
"CustomEmployedSinceDate": "{{employedSinceDate}}",
"CustomPosition": "{{position}}",
"CustomDepartment": "{{department}}",
"Culture_en": "English (United Kingdom)",
"Culture_en-US": "English (United States)",
"Culture_ru-RU": "Russian (Russia)",
"LearnMore": "Learn more...",
"chooseFileLabel": "Drop file here, or click to select file",
"chooseMobileFileLabel": "Click to select files",
"unknownTypeError": "Unknown image file type",
"maxSizeFileError": "Maximum file size exceeded",
"unknownError": "Error",
"editAvatar": "Edit photo"
"editAvatar": "Edit"
}

View File

@ -23,26 +23,21 @@
"Actions": "Действия",
"ChangeEmailSuccess": "Email успешно изменен",
"NotFoundLanguage": "Если Вы не можете найти свой язык в списке доступных, Вы всегда можете написать нам по адресу <1>{{supportEmail}}</1>, чтобы принять участие в переводе и получить до 1 года бесплатного использования.",
"LearnMore": "Подробнее...",
"ErrorUnknownFileImageType": "Неизвестный тип файла изображения",
"Error": "Ошибка",
"SocialProfiles": "Социальные профили",
"PhoneChange": "Измененить номер телефона",
"PhoneChange": "Изменить номер телефона",
"PhoneLbl": "Основной телефон",
"EditSubscriptionsBtn": "Изменить подписки",
"InviteAgainLbl": "Активировать адрес email ещё раз",
"LDAPLbl": "LDAP",
"SocialProfiles": "Социальные профили",
"CustomEmployedSinceDate": "{{employedSinceDate}}",
"CustomPosition": "{{position}}",
"CustomDepartment": "{{department}}",
"Culture_en": "Английский (Великобритания)",
"Culture_en-US": "Английский (США)",
"Culture_ru-RU": "Русский (Россия)",
"LearnMore": "Подробнее...",
"chooseFileLabel": "Перетащите файл сюда или нажмите, чтобы выбрать файл",
"chooseMobileFileLabel": "Нажмите сюда, чтобы выбрать файл",
"unknownTypeError": "Неизвестный тип файла изображения",
"maxSizeFileError": "Превышен максимальный размер файла",
"unknownError": "Ошибка",
"editAvatar": "Изменить фотографию"
"editAvatar": "Изменить"
}

View File

@ -29,8 +29,7 @@ class DepartmentField extends React.Component {
selectorIsVisible,
selectorSelectedOptions,
selectorOnSelectGroups,
searchPlaceHolderLabel
selectorOnSelectGroups
} = this.props;
return (
@ -51,7 +50,6 @@ class DepartmentField extends React.Component {
isMultiSelect={true}
onSelect={selectorOnSelectGroups}
onCancel={onCloseGroupSelector}
searchPlaceHolderLabel={searchPlaceHolderLabel}
/>
{selectorSelectedOptions.map(option => (
<SelectedItem

View File

@ -14,7 +14,6 @@ import RadioField from './FormFields/RadioField'
import DepartmentField from './FormFields/DepartmentField'
import ContactsField from './FormFields/ContactsField'
import InfoFieldContainer from './FormFields/InfoFieldContainer'
import { departments, department, position, employedSinceDate } from '../../../../../helpers/customNames';
import { api } from "asc-web-common";
const {
createThumbnailsAvatar,
@ -320,6 +319,7 @@ class CreateUserForm extends React.Component {
render() {
const { isLoading, errors, profile, selector } = this.state;
const { t, settings, i18n } = this.props;
const { regDateCaption, userPostCaption, groupCaption } = settings.customNames;
const pattern = getUserContactsPattern();
const contacts = getUserContacts(profile.contacts);
@ -333,7 +333,7 @@ class CreateUserForm extends React.Component {
role={getUserRole(profile)}
editing={true}
source={this.state.croppedAvatarImage}
editLabel={t("AddPhoto")}
editLabel={t("AddButton")}
editAction={this.openAvatarEditor}
/>
<AvatarEditor
@ -342,12 +342,12 @@ class CreateUserForm extends React.Component {
onClose={this.onCloseAvatarEditor}
onSave={this.onSaveAvatar}
onLoadFile={this.onLoadFileAvatar}
headerLabel={t("editAvatar")}
headerLabel={t("AddPhoto")}
chooseFileLabel ={t("chooseFileLabel")}
chooseMobileFileLabel={t("chooseMobileFileLabel")}
unknownTypeError={t("unknownTypeError")}
unknownTypeError={t("ErrorUnknownFileImageType")}
maxSizeFileError={t("maxSizeFileError")}
unknownError ={t("unknownError")}
unknownError ={t("Error")}
/>
</AvatarContainer>
<MainFieldsContainer ref={this.mainFieldsContainerRef}>
@ -419,7 +419,7 @@ class CreateUserForm extends React.Component {
passwordSettings={settings.passwordSettings}
/>
<DateField
calendarHeaderContent={t("CalendarSelectDate")}
calendarHeaderContent={`${t("CalendarSelectDate")}:`}
labelText={`${t("Birthdate")}:`}
inputName="birthday"
inputValue={profile.birthday ? new Date(profile.birthday) : undefined}
@ -432,15 +432,15 @@ class CreateUserForm extends React.Component {
radioName="sex"
radioValue={profile.sex}
radioOptions={[
{ value: 'male', label: t("SexMale") },
{ value: 'female', label: t("SexFemale") }
{ value: 'male', label: t("MaleSexStatus") },
{ value: 'female', label: t("FemaleSexStatus") }
]}
radioIsDisabled={isLoading}
radioOnChange={this.onInputChange}
/>
<DateField
calendarHeaderContent={t("CalendarSelectDate")}
labelText={`${t("CustomEmployedSinceDate", { employedSinceDate })}:`}
calendarHeaderContent={`${t("CalendarSelectDate")}:`}
labelText={`${regDateCaption}:`}
inputName="workFrom"
inputValue={profile.workFrom ? new Date(profile.workFrom) : undefined}
inputIsDisabled={isLoading}
@ -456,7 +456,7 @@ class CreateUserForm extends React.Component {
inputTabIndex={7}
/>
<TextField
labelText={`${t("CustomPosition", { position })}:`}
labelText={`${userPostCaption}:`}
inputName="title"
inputValue={profile.title}
inputIsDisabled={isLoading}
@ -464,17 +464,15 @@ class CreateUserForm extends React.Component {
inputTabIndex={8}
/>
<DepartmentField
labelText={`${t("CustomDepartment", { department })}:`}
labelText={`${groupCaption}:`}
isDisabled={isLoading}
showGroupSelectorButtonTitle={t("AddButton")}
onShowGroupSelector={this.onShowGroupSelector}
onCloseGroupSelector={this.onCloseGroupSelector}
onRemoveGroup={this.onRemoveGroup}
selectorIsVisible={selector.visible}
searchPlaceHolderLabel={t("SearchDepartments")}
selectorOptions={selector.options}
selectorSelectedOptions={selector.selected}
selectorAddButtonText={t("CustomAddDepartments", { departments })}
selectorSelectAllText={t("SelectAll")}
selectorOnSearchGroups={this.onSearchGroups}
selectorOnSelectGroups={this.onSelectGroups}
@ -482,7 +480,7 @@ class CreateUserForm extends React.Component {
</MainFieldsContainer>
</MainContainer>
<InfoFieldContainer headerText={t("Comments")}>
<Textarea placeholder={t("AddСomment")} name="notes" value={profile.notes} isDisabled={isLoading} onChange={this.onInputChange} tabIndex={9}/>
<Textarea placeholder={t("WriteComment")} name="notes" value={profile.notes} isDisabled={isLoading} onChange={this.onInputChange} tabIndex={9}/>
</InfoFieldContainer>
<InfoFieldContainer headerText={t("ContactInformation")}>
<ContactsField

View File

@ -13,7 +13,6 @@ import RadioField from './FormFields/RadioField'
import DepartmentField from './FormFields/DepartmentField'
import ContactsField from './FormFields/ContactsField'
import InfoFieldContainer from './FormFields/InfoFieldContainer'
import { departments, department, position, employedSinceDate, typeGuest, typeUser } from '../../../../../helpers/customNames';
import styled from "styled-components";
import { api } from "asc-web-common";
import { ChangeEmailDialog, ChangePasswordDialog, ChangePhoneDialog } from '../../../../dialogs';
@ -366,7 +365,8 @@ class UpdateUserForm extends React.Component {
render() {
const { isLoading, errors, profile, selector, dialogsVisible } = this.state;
const { t, i18n } = this.props;
const { t, i18n, settings } = this.props;
const { guestCaption, userCaption, regDateCaption, userPostCaption, groupCaption } = settings.customNames;
const pattern = getUserContactsPattern();
const contacts = getUserContacts(profile.contacts);
@ -389,12 +389,12 @@ class UpdateUserForm extends React.Component {
</Th>
<Th>
<Text isBold fontSize='13px'>
{t("Employee")}
{userCaption}
</Text>
</Th>
<Th>
<Text isBold fontSize='13px'>
{t("GuestCaption")}
{guestCaption}
</Text>
</Th>
</tr>
@ -455,7 +455,7 @@ class UpdateUserForm extends React.Component {
source={profile.avatarMax}
userName={profile.displayName}
editing={true}
editLabel={t("EditPhoto")}
editLabel={t("editAvatar")}
editAction={this.openAvatarEditor}
/>
<AvatarEditor
@ -464,12 +464,12 @@ class UpdateUserForm extends React.Component {
onClose={this.onCloseAvatarEditor}
onSave={this.onSaveAvatar}
onLoadFile={this.onLoadFileAvatar}
headerLabel={t("editAvatar")}
headerLabel={t("EditPhoto")}
chooseFileLabel={t("chooseFileLabel")}
chooseMobileFileLabel={t("chooseMobileFileLabel")}
unknownTypeError={t("unknownTypeError")}
unknownTypeError={t("ErrorUnknownFileImageType")}
maxSizeFileError={t("maxSizeFileError")}
unknownError={t("unknownError")}
unknownError={t("Error")}
/>
</AvatarContainer>
<MainFieldsContainer ref={this.mainFieldsContainerRef}>
@ -541,7 +541,7 @@ class UpdateUserForm extends React.Component {
maxLength={50}
/>
<DateField
calendarHeaderContent={t("CalendarSelectDate")}
calendarHeaderContent={`${t("CalendarSelectDate")}:`}
labelText={`${t("Birthdate")}:`}
inputName="birthday"
inputValue={profile.birthday ? new Date(profile.birthday) : undefined}
@ -554,8 +554,8 @@ class UpdateUserForm extends React.Component {
radioName="sex"
radioValue={profile.sex}
radioOptions={[
{ value: 'male', label: t("SexMale") },
{ value: 'female', label: t("SexFemale") }
{ value: 'male', label: t("MaleSexStatus") },
{ value: 'female', label: t("FemaleSexStatus") }
]}
radioIsDisabled={isLoading}
radioOnChange={this.onInputChange}
@ -565,8 +565,8 @@ class UpdateUserForm extends React.Component {
radioName="isVisitor"
radioValue={profile.isVisitor.toString()}
radioOptions={[
{ value: "true", label: t("CustomTypeGuest", { typeGuest }) },
{ value: "false", label: t("CustomTypeUser", { typeUser }) }
{ value: "true", label: guestCaption },
{ value: "false", label: userCaption }
]}
radioIsDisabled={isLoading}
radioOnChange={this.onUserTypeChange}
@ -575,8 +575,8 @@ class UpdateUserForm extends React.Component {
helpButtonHeaderContent={t('UserType')}
/>
<DateField
calendarHeaderContent={t("CalendarSelectDate")}
labelText={`${t("CustomEmployedSinceDate", { employedSinceDate })}:`}
calendarHeaderContent={`${t("CalendarSelectDate")}:`}
labelText={`${regDateCaption}:`}
inputName="workFrom"
inputValue={profile.workFrom ? new Date(profile.workFrom) : undefined}
inputIsDisabled={isLoading}
@ -592,7 +592,7 @@ class UpdateUserForm extends React.Component {
inputTabIndex={8}
/>
<TextField
labelText={`${t("CustomPosition", { position })}:`}
labelText={`${userPostCaption}:`}
inputName="title"
inputValue={profile.title}
inputIsDisabled={isLoading}
@ -600,17 +600,15 @@ class UpdateUserForm extends React.Component {
inputTabIndex={9}
/>
<DepartmentField
labelText={`${t("CustomDepartment", { department })}:`}
labelText={`${groupCaption}:`}
isDisabled={isLoading}
showGroupSelectorButtonTitle={t("AddButton")}
onShowGroupSelector={this.onShowGroupSelector}
onCloseGroupSelector={this.onCloseGroupSelector}
onRemoveGroup={this.onRemoveGroup}
selectorIsVisible={selector.visible}
searchPlaceHolderLabel={t("SearchDepartments")}
selectorOptions={selector.options}
selectorSelectedOptions={selector.selected}
selectorAddButtonText={t("CustomAddDepartments", { departments })}
selectorSelectAllText={t("SelectAll")}
selectorOnSearchGroups={this.onSearchGroups}
selectorOnSelectGroups={this.onSelectGroups}
@ -618,7 +616,7 @@ class UpdateUserForm extends React.Component {
</MainFieldsContainer>
</MainContainer>
<InfoFieldContainer headerText={t("Comments")}>
<Textarea placeholder={t("AddСomment")} name="notes" value={profile.notes} isDisabled={isLoading} onChange={this.onInputChange} tabIndex={10}/>
<Textarea placeholder={t("WriteComment")} name="notes" value={profile.notes} isDisabled={isLoading} onChange={this.onInputChange} tabIndex={10}/>
</InfoFieldContainer>
<InfoFieldContainer headerText={t("ContactInformation")}>
<ContactsField

View File

@ -5,7 +5,6 @@ import { withRouter } from "react-router";
import { IconButton } from 'asc-web-components';
import { Headline } from 'asc-web-common';
import { useTranslation } from 'react-i18next';
import { typeUser, typeGuest } from './../../../../../helpers/customNames';
const Wrapper = styled.div`
display: flex;
@ -24,16 +23,17 @@ const Wrapper = styled.div`
`;
const SectionHeaderContent = (props) => {
const { profile, history, match } = props;
const { profile, history, match, settings } = props;
const { userCaption, guestCaption } = settings.customNames;
const { type } = match.params;
const { t } = useTranslation();
const headerText = type
? type === "guest"
? t('CustomNewGuest', { typeGuest })
: t('CustomNewEmployee', { typeUser })
? t('CustomCreation', { user: guestCaption })
: t('CustomCreation', { user: userCaption })
: profile
? `${t('EditProfile')} (${profile.displayName})`
? `${t('EditUserDialogTitle')} (${profile.displayName})`
: "";
const onClickBack = useCallback(() => {

View File

@ -1,6 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -8,12 +10,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false // not needed for react as it escapes by default
escapeValue: false, // not needed for react as it escapes by default
},
react: {
@ -36,12 +38,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false // not needed for react as it escapes by default
escapeValue: false, // not needed for react as it escapes by default
},
react: {

View File

@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { Loader } from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { PageLayout, utils } from "asc-web-common";
import {
ArticleHeaderContent,
ArticleMainButtonContent,
@ -16,6 +16,7 @@ import {
import { fetchProfile } from "../../../store/profile/actions";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
const { changeLanguage } = utils;
class ProfileAction extends React.Component {
componentDidMount() {
@ -41,10 +42,10 @@ class ProfileAction extends React.Component {
console.log("ProfileAction render");
let loaded = false;
const { profile, isVisitor, match, language } = this.props;
const { profile, isVisitor, match } = this.props;
const { userId, type } = match.params;
i18n.changeLanguage(language);
changeLanguage(i18n);
if (type) {
loaded = true;
@ -88,7 +89,6 @@ ProfileAction.propTypes = {
function mapStateToProps(state) {
return {
profile: state.profile.targetUser,
language: state.auth.user.cultureName || state.auth.settings.culture,
isVisitor: state.auth.user.isVisitor,
};
}

View File

@ -28,36 +28,27 @@
"CommunityProduct": "Community",
"People": "People",
"Message": "Talk",
"WriteComment": "Add comment",
"MaleSexStatus": "Male",
"FemaleSexStatus": "Female",
"EditUserDialogTitle": "Edit profile",
"ErrorUnknownFileImageType": "Unknown image file type",
"Error": "Error",
"CustomCreation": "{{user}} (creation)",
"ActivationLink": "Activation link",
"AddPhoto": "Add photo",
"AddСomment": "Add comment",
"TemporaryPassword": "Temporary password",
"SexMale": "Male",
"SexFemale": "Female",
"RequiredField": "Required field",
"ChangeButton": "Change",
"Phone": "Phone",
"ChangesSavedSuccessfully": "Changes saved successfully",
"EditProfile": "Edit profile",
"SearchDepartments": "Search departments",
"CustomEmployedSinceDate": "{{employedSinceDate}}",
"CustomPosition": "{{position}}",
"CustomDepartment": "{{department}}",
"CustomTypeGuest": "{{typeGuest}}",
"CustomTypeUser": "{{typeUser}}",
"CustomNewEmployee": "New {{typeUser, lowercase}}",
"CustomNewGuest": "New {{typeGuest, lowercase}}",
"CustomAddDepartments": "Add {{departments, lowercase}}",
"chooseFileLabel": "Drop file here, or click to select file",
"chooseMobileFileLabel": "Click to select files",
"unknownTypeError": "Unknown image file type",
"maxSizeFileError": "Maximum file size exceeded",
"unknownError": "Error",
"editAvatar": "Edit photo",
"CalendarSelectDate": "Select date:",
"Employee": "Employee",
"editAvatar": "Edit",
"CalendarSelectDate": "Select date",
"Calendar": "Calendar"
}

View File

@ -20,7 +20,6 @@
"EmailPopupHelper": "Основной email нужен для восстановления доступа к порталу в случае потери пароля, а также для отправки оповещений. <1>Вы можете создать новый email на домене в качестве основного. В этом случае потребуется задать одноразовый пароль, чтобы пользователь смог войти на портал в первый раз.</1> Основной email можно использовать как логин при входе на портал.",
"ProfileTypePopupHelper": "Гости имеют ограниченный доступ к некоторым функциям и модулям портала",
"ProductsAndInstruments_Products": "Модули",
"GuestCaption": "Гость",
"TermsOfUsePopupHelperLink": "Подробнее об условиях использования",
"Mail": "Почта",
"DocumentsProduct": "Документы",
@ -28,36 +27,27 @@
"CommunityProduct": "Сообщество",
"People": "Люди",
"Message": "Чат",
"WriteComment": "Добавить комментарий",
"MaleSexStatus": "Мужской",
"FemaleSexStatus": "Женский",
"EditUserDialogTitle": "Редактирование профиля",
"ErrorUnknownFileImageType": "Неизвестный тип файла изображения",
"Error": "Ошибка",
"CustomCreation": "{{user}} (создание)",
"ActivationLink": "Activation link",
"ActivationLink": "Ссылка для активации",
"AddPhoto": "Добавить фотографию",
"AddСomment": "Добавить комментарий",
"TemporaryPassword": "Temporary password",
"SexMale": "Мужской",
"SexFemale": "Женский",
"TemporaryPassword": "Временный пароль",
"RequiredField": "Обязательное поле",
"ChangeButton": "Изменить",
"Phone": "Телефон",
"ChangesSavedSuccessfully": "Изменения успешно сохранены",
"EditProfile": "Редактирование профиля",
"SearchDepartments": "Поиск группы",
"CustomEmployedSinceDate": "{{employedSinceDate}}",
"CustomPosition": "{{position}}",
"CustomDepartment": "{{department}}",
"CustomTypeGuest": "{{typeGuest}}",
"CustomTypeUser": "{{typeUser}}",
"CustomNewEmployee": "Новый {{typeUser, lowercase}}",
"CustomNewGuest": "Новый {{typeGuest, lowercase}}",
"CustomAddDepartments": "Добавить {{departments, lowercase}}",
"chooseFileLabel": "Перетащите файл сюда или нажмите, чтобы выбрать файл",
"chooseMobileFileLabel": "Нажмите сюда, чтобы выбрать файл",
"unknownTypeError": "Неизвестный тип файла изображения",
"maxSizeFileError": "Превышен максимальный размер файла",
"unknownError": "Ошибка",
"editAvatar": "Изменить фотографию",
"CalendarSelectDate": "Выберите дату:",
"Employee": "Гость",
"editAvatar": "Изменить",
"CalendarSelectDate": "Выберите дату",
"Calendar": "Календарь"
}

View File

@ -1,8 +0,0 @@
export const departments = 'Departments';
export const department = 'Department';
export const position = 'Position';
export const employedSinceDate = 'Employed since';
export const typeGuest = 'Guest';
export const typeGuests = 'Guests';
export const typeUser = 'Employee';
export const headOfDepartment = 'Head of department';

View File

@ -43,7 +43,8 @@
"ChangePhoneDialog":{
"Resource": [
"SendButton",
"MobilePhoneChangeTitle"
"MobilePhoneChangeTitle",
"MobilePhoneEraseDescription"
]
},
"DeleteProfileEverDialog":{
@ -95,7 +96,10 @@
"EditButton",
"ChangeEmailSuccess",
"Actions",
"NotFoundLanguage"
"NotFoundLanguage",
"LearnMore",
"ErrorUnknownFileImageType",
"Error"
]
},
"ProfileAction": {
@ -113,20 +117,25 @@
"CopyEmailAndPassword",
"UserType",
"CancelButton",
"EmailPopupHelper",
"ProfileTypePopupHelper",
"ProductsAndInstruments_Products",
"GuestCaption",
"TermsOfUsePopupHelperLink",
"Mail",
"People"
"People",
"MaleSexStatus",
"FemaleSexStatus",
"EditUserDialogTitle",
"ErrorUnknownFileImageType",
"Error"
],
"FeedResource": [
"DocumentsProduct",
"ProjectsProduct",
"CommunityProduct",
"Message"
"Message",
"WriteComment"
]
},
"Home": {
@ -150,7 +159,9 @@
"Actions",
"People",
"MessageEmailActivationInstuctionsSentOnEmail",
"EnableUserButton"
"EnableUserButton",
"Search",
"SendInviteAgain"
],
"PeopleResource": [
"LblSendEmail",
@ -166,7 +177,8 @@
],
"PeopleJSResource": [
"SuccessChangeUserStatus",
"SuccessChangeUserType"
"SuccessChangeUserType",
"SuccessSendInvitation"
],
"UserControlsCommonResource": [
"NextPage",
@ -186,7 +198,18 @@
"GroupAction": {
"Resource": [
"SaveButton",
"CancelButton"
"CancelButton",
"Name"
],
"PeopleResource": [
"Members",
"AddMembers",
"AddButton"
],
"UserControlsCommonResource": [
"Members",
"AddMembers",
"LblSelect"
]
},
"Reassign":{
@ -202,7 +225,7 @@
"ReassignButton",
"ReassignsToUser"
],
"UserControlsCommonResource.resx":[
"UserControlsCommonResource":[
"NotBeUndone"
],
"PeopleJSResource":[

View File

@ -283,6 +283,26 @@ namespace ASC.Api.Settings
.ToList();
}
[Read("customschemas/{id}")]
public SchemaItemModel PeopleSchema(string id)
{
var names = CustomNamingPeople.GetPeopleNames(id);
var schemaItem = new SchemaItemModel
{
Id = names.Id,
UserCaption = names.UserCaption,
UsersCaption = names.UsersCaption,
GroupCaption = names.GroupCaption,
GroupsCaption = names.GroupsCaption,
UserPostCaption = names.UserPostCaption,
RegDateCaption = names.RegDateCaption,
GroupHeadCaption = names.GroupHeadCaption,
GuestCaption = names.GuestCaption,
GuestsCaption = names.GuestsCaption,
};
return schemaItem;
}
[Read("quota")]
public QuotaWrapper GetQuotaUsed()
{

View File

@ -1,5 +1,8 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -7,16 +10,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,16 +38,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -1,12 +1,12 @@
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { Text, Link } from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { PageLayout, utils } from "asc-web-common";
import { useTranslation } from "react-i18next";
import i18n from "./i18n";
import version from "../../../../package.json";
import styled from "styled-components";
import { Trans } from "react-i18next";
const { changeLanguage } = utils;
const BodyStyle = styled.div`
margin-top: 24px;
@ -73,12 +73,12 @@ const VersionStyle = styled.div`
padding: 8px 0px 20px 0px;
`;
const Body = ({ language }) => {
const Body = () => {
const { t } = useTranslation("translation", { i18n });
useEffect(() => {
i18n.changeLanguage(language);
}, [language]);
changeLanguage(i18n);
}, []);
const gitHub = "GitHub";
const license = "AGPL-3.0";
@ -191,10 +191,5 @@ const About = ({ language }) => (
<PageLayout sectionBodyContent={<Body language={language} />} />
);
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName || state.auth.settings.culture
};
}
export default connect(mapStateToProps)(About);
export default About;

View File

@ -1,5 +1,7 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -7,16 +9,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,16 +37,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -1,11 +1,12 @@
import React, { Suspense, lazy } from "react";
import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom";
import { Loader } from "asc-web-components";
import ConfirmRoute from "../../../helpers/confirmRoute";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
import { Error404 } from "asc-web-common";
import { Error404, utils } from "asc-web-common";
const { changeLanguage } = utils;
const ActivateUserForm = lazy(() => import("./sub-components/activateUser"));
const CreateUserForm = lazy(() => import("./sub-components/createUser"));
@ -16,9 +17,9 @@ const ChangePhoneForm = lazy(() => import("./sub-components/changePhone"));
const ProfileRemoveForm = lazy(() => import("./sub-components/profileRemove"));
const ChangeOwnerForm = lazy(() => import("./sub-components/changeOwner"));
const Confirm = ({ match, language }) => {
const Confirm = ({ match }) => {
i18n.changeLanguage(language);
changeLanguage(i18n);
//console.log("Confirm render");
return (
@ -74,10 +75,4 @@ const Confirm = ({ match, language }) => {
);
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName || state.auth.settings.culture,
};
}
export default connect(mapStateToProps)(Confirm);
export default Confirm;

View File

@ -26,5 +26,5 @@
"ConfirmOwnerPortalTitle": "Please confirm that you want to change portal owner to {{newOwner}}",
"SaveButton": "Save",
"CancelButton": "Cancel",
"ConfirmOwnerPortalSuccessMessage": "Владелец портала был успешно изменен. {0} Через 10 секунд вы будете перенаправлены {1} сюда {2}"
"ConfirmOwnerPortalSuccessMessage": "Portal owner has been successfully changed. {0}In 10 seconds you will be redirected {1}here{2}"
}

View File

@ -1,5 +1,7 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -7,16 +9,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: false,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -36,16 +34,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -3,11 +3,13 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from "react-router";
import { Loader, toastr, Text } from 'asc-web-components';
import { ModuleTile, PageLayout } from "asc-web-common";
import { ModuleTile, PageLayout, utils } from "asc-web-common";
import { useTranslation } from 'react-i18next';
import i18n from './i18n';
import styled from "styled-components";
const { changeLanguage } = utils;
const HomeContainer = styled.div`
padding: 62px 15px 0 15px;
margin: 0 auto;
@ -74,6 +76,8 @@ const Body = ({ modules, match, isLoaded }) => {
params.error && toastr.error(params.error);
}, [params.error]);
useEffect(() => changeLanguage(i18n), []);
return (
!isLoaded
? (

View File

@ -0,0 +1,3 @@
{
"NoOneModulesAvailable": "Ни один модуль не доступен"
}

View File

@ -1,11 +1,10 @@
import React from 'react';
import { utils } from 'asc-web-components';
import { connect } from 'react-redux';
import {
TreeMenu,
TreeNode,
Icons,
Link
Link,
utils
} from "asc-web-components";
import { withRouter } from "react-router";
import styled from 'styled-components';
@ -58,10 +57,9 @@ class ArticleBodyContent extends React.Component {
constructor(props) {
super(props);
const { match, history, i18n, language } = props;
const { match, history } = props;
const fullSettingsUrl = props.match.url;
const locationPathname = props.location.pathname;
i18n.changeLanguage(language);
if (locationPathname === fullSettingsUrl) {
const defaultKey = ['0'];
@ -101,20 +99,14 @@ class ArticleBodyContent extends React.Component {
};
}
componentDidUpdate() {
componentDidUpdate(prevProps, prevState) {
if (!utils.array.isArrayEqual(prevState.selectedKeys, this.state.selectedKeys)){
const { selectedKeys } = this.state;
const { match, history } = this.props;
const settingsPath = getSelectedLinkByKey(selectedKeys[0], settingsTree);
const newPath = match.path + settingsPath;
history.push(newPath);
}
shouldComponentUpdate(nextProps, nextState) {
if (!utils.array.isArrayEqual(nextState.selectedKeys, this.state.selectedKeys)) {
return true;
}
return false;
}
onSelect = value => {
@ -168,10 +160,4 @@ class ArticleBodyContent extends React.Component {
};
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName,
};
}
export default connect(mapStateToProps)(withRouter(withTranslation()(ArticleBodyContent)));
export default withRouter(withTranslation()(ArticleBodyContent));

View File

@ -33,7 +33,7 @@ const Layout = ({ currentProductId, setCurrentProductId, language, children }) =
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName
language: state.auth.user.cultureName || state.auth.settings.culture
};
}

View File

@ -4,9 +4,12 @@ import { withTranslation } from 'react-i18next';
import { FieldContainer, Text, ComboBox, Loader, Button, toastr, Link, TextInput } from "asc-web-components";
import styled from 'styled-components';
import { Trans } from 'react-i18next';
import { store } from 'asc-web-common';
import { store, utils } from 'asc-web-common';
import { setLanguageAndTime, getPortalTimezones, setGreetingTitle, restoreGreetingTitle } from '../../../../../store/settings/actions';
const { getPortalCultures } = store.auth.actions;
import { default as clientStore } from '../../../../../store/store';
const { changeLanguage } = utils;
const { getPortalCultures, getModules } = store.auth.actions;
const mapCulturesToArray = (cultures, t) => {
return cultures.map((culture) => {
@ -94,6 +97,19 @@ class Customization extends React.Component {
if (timezones.length && languages.length && !prevState.isLoadedData) {
this.setState({ isLoadedData: true });
}
if (this.props.language !== prevProps.language) {
changeLanguage(this.props.i18n)
.then(() => getModules(clientStore.dispatch))
.then(() => {
const newLocaleLanguages = mapCulturesToArray(this.props.rawCultures, this.props.t);
const newLocaleSelectedLanguage = findSelectedItemByKey(newLocaleLanguages, this.state.language.key) || newLocaleLanguages[0];
this.setState({
languages: newLocaleLanguages,
language: newLocaleSelectedLanguage
});
});
}
}
onLanguageSelect = (language) => {
@ -109,9 +125,12 @@ class Customization extends React.Component {
this.setState({ isLoading: true }, function () {
setLanguageAndTime(this.state.language.key, this.state.timezone.key)
.then(() => {
this.setState({ isLoading: false })
toastr.success(t('SuccessfullySaveSettingsMessage'));
});
const { i18n } = this.props;
changeLanguage(i18n)
.then(() => toastr.success(i18n.t("SuccessfullySaveSettingsMessage")));
})
.catch((error) => toastr.error(error))
.finally(() => this.setState({ isLoading: false }));
})
}
@ -123,10 +142,9 @@ class Customization extends React.Component {
const { setGreetingTitle, t } = this.props;
this.setState({ isLoadingGreetingSave: true }, function () {
setGreetingTitle(this.state.greetingTitle)
.then(() => {
this.setState({ isLoadingGreetingSave: false })
toastr.success(t('SuccessfullySaveGreetingSettingsMessage'));
});
.then(() => toastr.success(t('SuccessfullySaveGreetingSettingsMessage')))
.catch((error) => toastr.error(error))
.finally(() => this.setState({ isLoadingGreetingSave: false }));
})
}

View File

@ -1,15 +1,17 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import i18n from "../../i18n";
import { I18nextProvider, withTranslation } from "react-i18next";
import styled from "styled-components";
import { TabContainer } from "asc-web-components";
import { utils } from 'asc-web-common';
import OwnerSettings from "./sub-components/owner";
import AdminsSettings from "./sub-components/admins";
// import ModulesSettings from "./sub-components/modules";
const { changeLanguage } = utils;
const MainContainer = styled.div`
padding-bottom: 16px;
width: 100%;
@ -43,8 +45,6 @@ class PureAccessRights extends Component {
};
}
componentDidMount() {}
onSelectPage = page => {
const { history } = this.props;
@ -76,6 +76,7 @@ class PureAccessRights extends Component {
render() {
const { isLoading, selectedTab } = this.state;
const { t } = this.props;
console.log("accessRight render_");
@ -89,12 +90,12 @@ class PureAccessRights extends Component {
{[
{
key: "0",
title: "Owner settings",
title: t('OwnerSettings'),
content: <OwnerSettings />
},
{
key: "1",
title: "Admins settings",
title: t('AdminsSettings'),
content: <AdminsSettings />
},
// {
@ -112,9 +113,8 @@ class PureAccessRights extends Component {
const AccessRightsContainer = withTranslation()(PureAccessRights);
const AccessRights = props => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return (
<I18nextProvider i18n={i18n}>
@ -123,4 +123,4 @@ const AccessRights = props => {
);
};
export default connect(null, {})(withRouter(AccessRights));
export default withRouter(AccessRights);

View File

@ -366,7 +366,7 @@ class PureAdminsSettings extends Component {
id="people-admin-selector_button"
size="medium"
primary={true}
label="Set people admin"
label={t('SetPeopleAdmin')}
isDisabled={isLoading}
onClick={this.onShowGroupSelector}
/>
@ -384,7 +384,7 @@ class PureAdminsSettings extends Component {
id="full-admin-selector_button"
size="medium"
primary={true}
label="Set portal admin"
label={t('SetPortalAdmin')}
isDisabled={isLoading}
onClick={this.onShowFullAdminGroupSelector}
/>
@ -404,6 +404,8 @@ class PureAdminsSettings extends Component {
getFilterData={() => []}
getSortData={this.getSortData}
onFilter={this.onFilter}
directionAscLabel={t("DirectionAscLabel")}
directionDescLabel={t("DirectionDescLabel")}
/>
{admins.length > 0 ? (
@ -445,8 +447,8 @@ class PureAdminsSettings extends Component {
<Text>
{user.isAdmin
? "Full access"
: "People module admin"}
? t('AccessRightsFullAccess')
: t('PeopleAdmin')}
</Text>
{!user.isOwner ? (

View File

@ -107,7 +107,7 @@ class PureModulesSettings extends Component {
})}
</Text>
<Text fontSize='12px'>
<li>{t("ViewProfilesAndGroups")}</li>
<li>{t("ProductUserOpportunities")}</li>
</Text>
</ProjectsBody>
</ProjectsContainer>

View File

@ -216,7 +216,7 @@ class PureOwnerSettings extends Component {
className="button_offset"
size="medium"
primary={true}
label="Change portal owner"
label={t('AccessRightsChangeOwnerButtonText')}
isDisabled={!isLoading ? selectedOwner === null : false}
onClick={this.onChangeOwner}
/>

View File

@ -1,5 +1,7 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
@ -7,16 +9,12 @@ if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
@ -39,16 +37,12 @@ if (process.env.NODE_ENV === "production") {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -65,9 +65,11 @@
"NotFoundTitle": "No results matching your search could be found",
"NotFoundDescription": "No people matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the people in this section.",
"ClearButton": "Reset filter",
"ProductUserOpportunities": "View profiles and groups",
"LearnMore": "Learn more...",
"AccessRightsChangeOwnerButtonText": "Change portal owner",
"ViewProfilesAndGroups": "View profiles and groups",
"ProjectsUserCapabilityView": "View projects and take part in discussions",
"ProjectsUserCapabilityCreate": "Create and edit milestones, tasks, discussions, documents",
"ProjectsUserCapabilityTrack": "Track time for tasks, generate reports",
@ -82,9 +84,15 @@
"Culture_en": "English (United Kingdom)",
"Culture_en-US": "English (United States)",
"Culture_ru-RU": "Russian (Russia)",
"LearnMore": "Learn more...",
"CountPerPage": "{{count}} per page",
"PageOfTotalPage": "{{page}} of {{totalPage}}",
"ByFirstNameSorting": "By first name",
"ByLastNameSorting": "By last name"
"ByLastNameSorting": "By last name",
"OwnerSettings": "Owner settings",
"AdminsSettings": "Admins settings",
"SetPeopleAdmin": "Set people admin",
"SetPortalAdmin": "Set portal admin",
"PeopleAdmin": "People module admin",
"DirectionAscLabel":"A-Z",
"DirectionDescLabel":"Z-A"
}

View File

@ -64,9 +64,11 @@
"NotFoundTitle": "Результатов, соответствующих заданным критериям, не найдено",
"NotFoundDescription": "В данном разделе нет людей, соответствующих фильтру. Пожалуйста, выберите другие параметры или очистите фильтр, чтобы просмотреть всех людей в этом разделе.",
"ClearButton": "Сбросить фильтр",
"LearnMore": "Подробнее...",
"AccessRightsChangeOwnerButtonText": "Сменить владельца портала",
"ViewProfilesAndGroups": "Просматривать профили и группы",
"ProductUserOpportunities": "Просматривать профили и группы",
"AccessRightsAccessToProduct": "Доступ к модулю {{product}} предоставлен для",
"ProjectsUserCapabilityView": "Просматривать проекты и участвовать в обсуждениях",
"ProjectsUserCapabilityCreate": "Создавать и редактировать вехи, задачи, обсуждения, документы",
@ -82,9 +84,15 @@
"Culture_en": "Английский (Великобритания)",
"Culture_en-US": "Английский (США)",
"Culture_ru-RU": "Русский (Россия)",
"LearnMore": "Подробнее...",
"CountPerPage": "{{count}} на странице",
"PageOfTotalPage": "{{page}} из {{totalPage}}",
"ByFirstNameSorting": "По имени",
"ByLastNameSorting": "По фамилии"
"ByLastNameSorting": "По фамилии",
"OwnerSettings": "Настройки владельца портата",
"AdminsSettings": "Настройки администраторов",
"SetPeopleAdmin": "Установить администратора модуля Люди",
"SetPortalAdmin": "Установить администратора портала",
"PeopleAdmin": "Администратор модуля Люди",
"DirectionAscLabel":"А-Я",
"DirectionDescLabel":"Я-А"
}

View File

@ -101,7 +101,9 @@
"Employees",
"AccessRightsChangeOwnerText",
"DnsChangeMsg",
"AccessRightsChangeOwnerConfirmText"
"AccessRightsChangeOwnerConfirmText",
"LearnMore",
"AccessRightsChangeOwnerButtonText"
],
"FeedResource": [
"ProjectsProduct",
@ -110,7 +112,8 @@
"DocumentsProduct"
],
"PeopleResource": [
"Settings"
"Settings",
"ProductUserOpportunities"
],
"AuditResource": [
"LoginHistoryNav",

View File

@ -9,5 +9,6 @@ module.exports = {
env: {
browser: true,
node: true,
jest: true
},
};

View File

@ -74,4 +74,11 @@ export function getSettings() {
method: "get",
url: `/settings/whitelabel/logos.json`
});
}
export function getCurrentCustomSchema(id) {
return request({
method: "get",
url: `settings/customschemas/${id}.json`
});
}

View File

@ -1,10 +1,10 @@
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import AdvancedSelector from "../AdvancedSelector";
import { getGroupList } from "../../api/groups";
import { changeLanguage } from '../../utils';
class GroupSelector extends React.Component {
constructor(props) {
@ -15,8 +15,7 @@ class GroupSelector extends React.Component {
}
componentDidMount() {
const { language } = this.props;
i18n.changeLanguage(language);
changeLanguage(i18n);
getGroupList(this.props.useFake)
.then(groups => this.setState({ groups: this.convertGroups(groups) }))
@ -147,23 +146,9 @@ GroupSelector.defaultProps = {
const ExtendedGroupSelector = withTranslation()(GroupSelector);
const GroupSelectorWithI18n = props => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return <ExtendedGroupSelector i18n={i18n} {...props} />;
};
GroupSelectorWithI18n.propTypes = {
language: PropTypes.string
};
function mapStateToProps(state) {
return {
language:
state.auth &&
((state.auth.user && state.auth.user.cultureName) ||
(state.auth.settings && state.auth.settings.culture))
};
}
export default connect(mapStateToProps)(GroupSelectorWithI18n);
export default GroupSelectorWithI18n;

View File

@ -1,6 +1,7 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { LANGUAGE } from '../../constants';
const newInstance = i18n.createInstance();
@ -15,16 +16,12 @@ const resources = {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import { Backdrop, Aside } from "asc-web-components";
import { Backdrop, Aside, utils } from "asc-web-components";
import HeaderComponent from "./sub-components/header";
import Nav from "./sub-components/nav";
import Main from "./sub-components/main";
@ -35,8 +35,8 @@ class Layout extends React.Component {
hash += props.currentModuleId;
}
if (props.currentUser) {
const { id, displayName, email, avatarSmall } = props.currentUser;
hash += id + displayName + email + avatarSmall;
const { id, displayName, email, avatarSmall, cultureName } = props.currentUser;
hash += id + displayName + email + avatarSmall + cultureName;
}
if (props.availableModules) {
for (let i = 0, l = props.availableModules.length; i < l; i++) {
@ -44,6 +44,9 @@ class Layout extends React.Component {
hash += item.id + item.notifications;
}
}
if (props.availableModules && this.state.availableModules) {
hash += utils.array.isArrayEqual(this.state.availableModules, props.availableModules);
}
return hash;
};

View File

@ -12,8 +12,7 @@ class ProfileActions extends React.PureComponent {
this.state = {
opened: props.opened,
user: props.user,
userActions: props.userActions
user: props.user
};
this.handleClick = this.handleClick.bind(this);
@ -95,7 +94,7 @@ class ProfileActions extends React.PureComponent {
displayName={this.state.user.displayName}
email={this.state.user.email}
/>
{this.state.userActions.map(action => (
{this.props.userActions.map(action => (
<Link
noHover={true}
key={action.key}

View File

@ -1,6 +1,7 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { LANGUAGE } from '../../constants';
const newInstance = i18n.createInstance();
@ -15,7 +16,7 @@ const resources = {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,

View File

@ -3,7 +3,6 @@ import PropTypes from "prop-types";
import { Backdrop } from "asc-web-components";
import { withTranslation } from 'react-i18next';
import i18n from './i18n';
import { connect } from "react-redux";
import { ARTICLE_PINNED_KEY } from "../../constants";
import Article from "./sub-components/article";
@ -17,6 +16,7 @@ import SectionFilter from "./sub-components/section-filter";
import SectionBody from "./sub-components/section-body";
import SectionPaging from "./sub-components/section-paging";
import SectionToggler from "./sub-components/section-toggler";
import { changeLanguage } from '../../utils';
class PageLayoutComponent extends React.PureComponent {
constructor(props) {
@ -191,8 +191,7 @@ class PageLayoutComponent extends React.PureComponent {
const PageLayoutTranslated = withTranslation()(PageLayoutComponent);
const PageLayout = props => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return <PageLayoutTranslated i18n={i18n} {...props} />
}
@ -241,13 +240,5 @@ PageLayoutComponent.defaultProps = {
withBodyAutoFocus: false
};
function mapStateToProps(state) {
return {
language:
state.auth &&
((state.auth.user && state.auth.user.cultureName) ||
(state.auth.settings && state.auth.settings.culture))
};
}
export default connect(mapStateToProps)(PageLayout);
export default PageLayout;

View File

@ -1,5 +1,4 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
@ -8,6 +7,7 @@ import { getUserList } from "../../api/people";
import { getGroupList } from "../../api/groups";
import Filter from "../../api/people/filter";
import UserTooltip from "./sub-components/UserTooltip";
import { changeLanguage } from '../../utils';
class PeopleSelector extends React.Component {
constructor(props) {
@ -23,8 +23,8 @@ class PeopleSelector extends React.Component {
}
componentDidMount() {
const { language } = this.props;
i18n.changeLanguage(language);
const { groupsCaption, t } = this.props;
changeLanguage(i18n);
getGroupList(this.props.useFake)
.then(groups =>
@ -32,7 +32,7 @@ class PeopleSelector extends React.Component {
groups: [
{
key: "all",
label: "All groups",
label: t('CustomAllGroups', { groupsCaption }),
total: 0
}
].concat(this.convertGroups(groups))
@ -160,7 +160,9 @@ class PeopleSelector extends React.Component {
onSelect,
size,
onCancel,
t
t,
searchPlaceHolderLabel,
groupsCaption
} = this.props;
return (
@ -180,10 +182,10 @@ class PeopleSelector extends React.Component {
isOpen={isOpen}
isMultiSelect={isMultiSelect}
isDisabled={isDisabled}
searchPlaceHolderLabel={t("SearchUsersPlaceholder")}
searchPlaceHolderLabel={searchPlaceHolderLabel || t("SearchUsersPlaceholder")}
selectButtonLabel={t("AddMembersButtonLabel")}
selectAllLabel={t("SelectAllLabel")}
groupsHeaderLabel={t("CustomDepartments", { departments: "Groups" })} //TODO: Replace to variable from settings
groupsHeaderLabel={groupsCaption}
emptySearchOptionsLabel={t("EmptySearchUsersResult")}
emptyOptionsLabel={t("EmptyUsers")}
loadingLabel={t("LoadingLabel")}
@ -210,6 +212,8 @@ PeopleSelector.propTypes = {
size: PropTypes.oneOf(["full", "compact"]),
language: PropTypes.string,
t: PropTypes.func,
groupsCaption: PropTypes.string,
searchPlaceHolderLabel: PropTypes.string,
role: PropTypes.oneOf(["admin", "user", "guest"])
};
@ -223,8 +227,7 @@ PeopleSelector.defaultProps = {
const ExtendedPeopleSelector = withTranslation()(PeopleSelector);
const PeopleSelectorWithI18n = props => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return <ExtendedPeopleSelector i18n={i18n} {...props} />;
};
@ -233,13 +236,4 @@ PeopleSelectorWithI18n.propTypes = {
language: PropTypes.string
};
function mapStateToProps(state) {
return {
language:
state.auth &&
((state.auth.user && state.auth.user.cultureName) ||
(state.auth.settings && state.auth.settings.culture))
};
}
export default connect(mapStateToProps)(PeopleSelectorWithI18n);
export default PeopleSelectorWithI18n;

View File

@ -1,6 +1,7 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { LANGUAGE } from '../../constants';
const newInstance = i18n.createInstance();
@ -15,7 +16,7 @@ const resources = {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
@ -27,6 +28,7 @@ newInstance.init({
}
},
react: {
useSuspense: false
}

View File

@ -5,5 +5,5 @@
"EmptySearchUsersResult": "There are no users with such name",
"EmptyUsers": "There are no users",
"LoadingLabel": "Loading... Please wait...",
"CustomDepartments": "{{departments}}"
"CustomAllGroups":"All {{ groupsCaption, lowercase }}"
}

View File

@ -5,5 +5,5 @@
"EmptySearchUsersResult": "Нет такого пользователя",
"EmptyUsers": "Нет пользователей",
"LoadingLabel": "Загрузка... Пожалуйста подождите...",
"CustomDepartments": "{{departments}}"
"CustomAllGroups":"Все {{ groupsCaption, lowercase }}"
}

View File

@ -1,12 +1,17 @@
import React from "react";
import PropTypes from "prop-types";
import { Toast } from "asc-web-components";
import { Toast, utils } from "asc-web-components";
import Layout from "../Layout";
class PureStudioLayout extends React.Component {
shouldComponentUpdate(nextProps) {
if (this.props.availableModules && nextProps.availableModules &&
!utils.array.isArrayEqual(nextProps.availableModules, this.props.availableModules)) {
return true;
}
return this.props.hasChanges !== nextProps.hasChanges ||
this.props.currentModuleId !== nextProps.currentModuleId;
this.props.currentModuleId !== nextProps.currentModuleId ||
this.props.language !== nextProps.language;
}
onProfileClick = () => {
@ -75,6 +80,7 @@ class PureStudioLayout extends React.Component {
}
PureStudioLayout.propTypes = {
availableModules: PropTypes.array,
logout: PropTypes.func.isRequired,
language: PropTypes.string,
hasChanges: PropTypes.bool,

View File

@ -6,6 +6,7 @@ import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import { logout } from "../../store/auth/actions";
import PureStudioLayout from "./PureStudioLayout";
import { changeLanguage } from '../../utils';
const getSeparator = id => {
return {
@ -65,8 +66,7 @@ const getAvailableModules = (modules, currentUser) => {
const StudioLayoutContainer = withTranslation()(PureStudioLayout);
const StudioLayout = props => {
const { language } = props;
i18n.changeLanguage(language);
changeLanguage(i18n);
return <StudioLayoutContainer i18n={i18n} {...props} />;
};
@ -82,8 +82,7 @@ function mapStateToProps(state) {
availableModules: getAvailableModules(state.auth.modules, state.auth.user),
currentUser: state.auth.user,
currentModuleId: state.auth.settings.currentProductId,
settings: state.auth.settings,
language: state.auth.user.cultureName || state.auth.settings.culture
settings: state.auth.settings
};
}

View File

@ -1,6 +1,7 @@
import i18n from "i18next";
import en from "./locales/en/translation.json";
import ru from "./locales/ru/translation.json";
import { LANGUAGE } from '../../constants';
const newInstance = i18n.createInstance();
@ -15,16 +16,12 @@ const resources = {
newInstance.init({
resources: resources,
lng: 'en',
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {

View File

@ -1,4 +1,5 @@
export const AUTH_KEY = 'asc_auth_key';
export const LANGUAGE = 'language';
export const ARTICLE_PINNED_KEY = 'asc_article_pinned_key';
/**

View File

@ -8,12 +8,13 @@ export const SET_IS_LOADED = "SET_IS_LOADED";
export const LOGOUT = "LOGOUT";
export const SET_PASSWORD_SETTINGS = "SET_PASSWORD_SETTINGS";
export const SET_NEW_EMAIL = "SET_NEW_EMAIL";
export const GET_PORTAL_CULTURES = "GET_PORTAL_CULTURES";
export const SET_PORTAL_CULTURES = "SET_PORTAL_CULTURES";
export const SET_PORTAL_LANGUAGE_AND_TIME = "SET_PORTAL_LANGUAGE_AND_TIME";
export const GET_TIMEZONES = "GET_TIMEZONES";
export const SET_TIMEZONES = "SET_TIMEZONES";
export const SET_CURRENT_PRODUCT_ID = "SET_CURRENT_PRODUCT_ID";
export const SET_CURRENT_PRODUCT_HOME_PAGE = "SET_CURRENT_PRODUCT_HOME_PAGE";
export const SET_GREETING_SETTINGS = "SET_GREETING_SETTINGS";
export const SET_CUSTOM_NAMES = "SET_CUSTOM_NAMES";
export function setCurrentUser(user) {
return {
@ -66,7 +67,7 @@ export function setNewEmail(email) {
export function setPortalCultures(cultures) {
return {
type: GET_PORTAL_CULTURES,
type: SET_PORTAL_CULTURES,
cultures
};
}
@ -80,7 +81,7 @@ export function setPortalLanguageAndTime(newSettings) {
export function setTimezones(timezones) {
return {
type: GET_TIMEZONES,
type: SET_TIMEZONES,
timezones
};
}
@ -106,6 +107,13 @@ export function setGreetingSettings(title) {
};
}
export function setCustomNames(customNames) {
return {
type: SET_CUSTOM_NAMES,
customNames
};
}
export function getUser(dispatch) {
return api.people.getUser().then(user => dispatch(setCurrentUser(user)));
}
@ -113,7 +121,16 @@ export function getUser(dispatch) {
export function getPortalSettings(dispatch) {
return api.settings
.getSettings()
.then(settings => dispatch(setSettings(settings)));
.then(settings => {
dispatch(setSettings(settings));
settings.nameSchemaId && getCurrentCustomSchema(dispatch, settings.nameSchemaId);
});
}
export function getCurrentCustomSchema(dispatch, id) {
return api.settings
.getCurrentCustomSchema(id)
.then(customNames => dispatch(setCustomNames(customNames)));
}
export function getModules(dispatch) {

View File

@ -1,8 +1,9 @@
import {
SET_CURRENT_USER, SET_MODULES, SET_SETTINGS, SET_IS_LOADED, LOGOUT, SET_PASSWORD_SETTINGS, SET_NEW_EMAIL,
GET_PORTAL_CULTURES, SET_PORTAL_LANGUAGE_AND_TIME, GET_TIMEZONES, SET_CURRENT_PRODUCT_ID, SET_CURRENT_PRODUCT_HOME_PAGE, SET_GREETING_SETTINGS,
} from './actions';
SET_PORTAL_CULTURES, SET_PORTAL_LANGUAGE_AND_TIME, SET_TIMEZONES, SET_CURRENT_PRODUCT_ID, SET_CURRENT_PRODUCT_HOME_PAGE, SET_GREETING_SETTINGS,
SET_CUSTOM_NAMES } from './actions';
import isEmpty from "lodash/isEmpty";
import { LANGUAGE } from '../../constants';
const initialState = {
isAuthenticated: false,
@ -35,6 +36,9 @@ const initialState = {
const authReducer = (state = initialState, action) => {
switch (action.type) {
case SET_CURRENT_USER:
action.user.cultureName
&& localStorage.getItem(LANGUAGE) !== action.user.cultureName
&& localStorage.setItem(LANGUAGE, action.user.cultureName);
return Object.assign({}, state, {
isAuthenticated: !isEmpty(action.user),
user: action.user
@ -44,10 +48,13 @@ const authReducer = (state = initialState, action) => {
modules: action.modules
});
case SET_SETTINGS:
if (!localStorage.getItem(LANGUAGE)) {
localStorage.setItem(LANGUAGE, action.settings.culture);
}
return Object.assign({}, state, {
settings: { ...state.settings, ...action.settings }
});
case GET_PORTAL_CULTURES:
case SET_PORTAL_CULTURES:
return Object.assign({}, state, {
settings: { ...state.settings, cultures: action.cultures }
});
@ -64,10 +71,13 @@ const authReducer = (state = initialState, action) => {
user: { ...state.user, email: action.email }
});
case SET_PORTAL_LANGUAGE_AND_TIME:
if (!state.user.cultureName) {
localStorage.setItem(LANGUAGE, action.newSettings.lng);
}
return Object.assign({}, state, {
settings: { ...state.settings, culture: action.newSettings.lng, timezone: action.newSettings.timeZoneID }
});
case GET_TIMEZONES:
case SET_TIMEZONES:
return Object.assign({}, state, {
settings: { ...state.settings, timezones: action.timezones }
});
@ -83,6 +93,10 @@ const authReducer = (state = initialState, action) => {
return Object.assign({}, state, {
settings: { ...state.settings, greetingSettings: action.title }
});
case SET_CUSTOM_NAMES:
return Object.assign({}, state, {
settings: { ...state.settings, customNames: action.customNames }
});
case LOGOUT:
return Object.assign({}, initialState, {
settings: state.settings

View File

@ -1,3 +1,5 @@
import { LANGUAGE } from '../constants';
export const toUrlParams = (obj, skipNull) => {
let str = "";
for (var key in obj) {
@ -28,3 +30,11 @@ export function getObjectByLocation(location) {
return object;
}
export function changeLanguage(i18nInstance) {
return localStorage.getItem(LANGUAGE)
? (i18nInstance.language !== localStorage.getItem(LANGUAGE)
? i18nInstance.changeLanguage(localStorage.getItem(LANGUAGE))
: Promise.resolve())
: i18nInstance.changeLanguage('en');
}

View File

@ -411,8 +411,8 @@ class FilterInput extends React.Component {
componentWillUnmount() {
window.removeEventListener('resize', this.throttledResize);
}
componentDidUpdate(){
if(this.props.needForUpdate){
componentDidUpdate(prevProps){
if(this.props.needForUpdate && this.props.needForUpdate(prevProps, this.props)){
let internalFilterData = convertToInternalData(this.props.getFilterData(), cloneObjectsArray(this.props.selectedFilterData.filterValues));
this.updateFilter(internalFilterData);
}

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3502 0C12.5262 0 12.6786 0.053566 12.8074 0.160787C12.9357 0.267978 13 0.394823 13 0.541618V7.04151C13 7.36733 12.949 7.69119 12.8467 8.01316C12.7323 8.00444 12.6167 8 12.5 8C11.7907 8 11.1196 8.16413 10.5229 8.45648C10.8743 7.97985 11.05 7.5082 11.05 7.04151V1.62486H8.125L6.5 3.25L6.49995 11.2478C7.30564 10.8924 8.02677 10.5058 8.66324 10.0883C8.6847 10.0743 8.70601 10.0603 8.72717 10.0463C8.28239 10.7288 8.0178 11.5395 8.00087 12.4109C7.62856 12.5895 7.35999 12.7127 7.19557 12.7799C7.01625 12.8533 6.87236 12.9097 6.76415 12.9491C6.68276 12.983 6.59493 13 6.49991 13C6.405 13 6.31707 12.983 6.23575 12.9491C6.12746 12.9097 5.98357 12.8533 5.80418 12.7799C5.62482 12.7066 5.32174 12.5667 4.89518 12.3609C4.46863 12.1552 4.05897 11.9364 3.66632 11.7051C3.27367 11.4736 2.84544 11.1833 2.38157 10.8333C1.91777 10.4836 1.51836 10.1238 1.18315 9.75417C0.847951 9.38461 0.566925 8.96138 0.340183 8.4846C0.113335 8.00778 0 7.52681 0 7.04151V0.541618C0 0.394912 0.0643513 0.267949 0.192947 0.160787C0.321614 0.053566 0.473973 0 0.65013 0H5.28125L6.50014 1.21875L7.71875 0H12.3502Z" fill="#333333"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 16C14.433 16 16 14.433 16 12.5C16 10.567 14.433 9 12.5 9C10.567 9 9 10.567 9 12.5C9 14.433 10.567 16 12.5 16ZM13 10V12.0001H15V13.0001H13V15L12 15V13.0001H9.99999V12.0001H12V10H13Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.125 0C7.9701 0 9.46591 1.44649 9.46591 3.23076C9.46591 5.01517 7.9701 6.46153 6.125 6.46153C4.2799 6.46153 2.78409 5.01503 2.78409 3.23076C2.78409 1.44649 4.2799 0 6.125 0ZM8.25606 14H1.57202C0.703828 14 0 13.3432 0 12.543C0 8.97743 1.68911 7.52238 3.53122 7.48433L6.12501 11.375L8.7426 7.44861C9.53875 7.50503 10.264 7.77829 10.8457 8.3138C9.179 8.97299 8 10.5988 8 12.5C8 13.026 8.09023 13.5308 8.25606 14Z" fill="#333333"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 16C14.433 16 16 14.433 16 12.5C16 10.567 14.433 9 12.5 9C10.567 9 9 10.567 9 12.5C9 14.433 10.567 16 12.5 16ZM13 10V12H15V13H13V15H12V13H9.99999V12H12V10H13Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 813 B

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.54817 3.23076C9.54817 1.44649 8.03936 0 6.17823 0C4.31709 0 2.80828 1.44649 2.80828 3.23076C2.80828 5.01503 4.31709 6.46153 6.17823 6.46153C8.03936 6.46153 9.54817 5.01517 9.54817 3.23076ZM10.9117 8.28831C9.2102 8.93029 7.99998 10.5738 7.99998 12.5C7.99998 13.026 8.09022 13.5308 8.25604 14H1.58569C0.709944 14 0 13.3432 0 12.543C0 8.87114 1.80689 7.4375 3.72846 7.48469C4.3947 7.90803 5.20365 8.15679 6.07659 8.15679C6.98151 8.15679 7.81769 7.88951 8.49723 7.4375C9.41301 7.4375 10.2518 7.70008 10.9117 8.28831Z" fill="#333333"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 16C14.433 16 16 14.433 16 12.5C16 10.567 14.433 9 12.5 9C10.567 9 9 10.567 9 12.5C9 14.433 10.567 16 12.5 16ZM13 10V12H15V13H13V15H12V13H9.99999V12H12V10H13Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 917 B

View File

@ -145,6 +145,10 @@ import OrigShareFacebookIcon from './share.facebook.react.svg';
import OrigShareTwitterIcon from './share.twitter.react.svg';
import OrigShareLinkedInIcon from './share.linkedin.react.svg';
import OrigAddDepartmentIcon from './add.department.react.svg';
import OrigAddEmployeeIcon from './add.employee.react.svg';
import OrigAddGuestIcon from './add.guest.react.svg';
export const AZSortingIcon = createStyledIcon(
OrigAZSortingIcon,
'AZSortingIcon'
@ -189,6 +193,18 @@ export const ActionsUploadIcon = createStyledIcon(
OrigActionsUploadIcon,
'ActionsUploadIcon'
);
export const AddDepartmentIcon = createStyledIcon(
OrigAddDepartmentIcon,
'AddDepartmentIcon'
);
export const AddEmployeeIcon = createStyledIcon(
OrigAddEmployeeIcon,
'AddEmployeeIcon'
);
export const AddGuestIcon = createStyledIcon(
OrigAddGuestIcon,
'AddGuestIcon'
);
export const AdministratorIcon = createStyledIcon(
OrigAdministratorIcon,
'AdministratorIcon'