Merge branch 'master' into feature/components-custom-styling

This commit is contained in:
Ilya Oleshko 2019-11-27 11:02:38 +03:00
commit d8ce7aab73
162 changed files with 2675 additions and 2823 deletions

2
.gitignore vendored
View File

@ -7,9 +7,11 @@
**/node_modules/
**/storybook-static/
/web/ASC.Web.Components/dist
/web/ASC.Web.Common/dist
/build/deploy
*.log
/packages/asc-web-components
/packages/asc-web-common
/products/ASC.People/Data/
Data/
Logs/

View File

@ -4,6 +4,10 @@
"name": "ASC.Web.Components"
"path": "./web/ASC.Web.Components"
},
{
"name": "ASC.Web.Common"
"path": "./web/ASC.Web.Common"
},
{
"name": "ASC.Web.Client"
"path": "./web/ASC.Web.Client"

View File

@ -10,12 +10,19 @@ echo "ASC.Web.Components"
call yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build\ASC.Web.Components.log
call yarn link --cwd packages/asc-web-components
echo "ASC.Web.Common"
call yarn link "asc-web-components" --cwd web/ASC.Web.Common
call yarn install --cwd web/ASC.Web.Common --frozen-lockfile > build\ASC.Web.Common.log
call yarn link --cwd packages/asc-web-common
echo "ASC.Web.Client"
call yarn link "asc-web-components" --cwd web/ASC.Web.Client
call yarn link "asc-web-common" --cwd web/ASC.Web.Client
call yarn install --cwd web/ASC.Web.Client --frozen-lockfile > build\ASC.Web.Client.log
echo "ASC.Web.People.Client"
call yarn link "asc-web-components" --cwd products/ASC.People/Client
call yarn link "asc-web-common" --cwd products/ASC.People/Client
call yarn install --cwd products/ASC.People/Client --frozen-lockfile > build\ASC.Web.People.Client.log
xcopy build\cra\*.* products\ASC.People\Client\node_modules\ /E /R /Y

View File

@ -4,20 +4,29 @@ call start\stop.bat
PUSHD %~dp0..
del /s /q packages\asc-web-components
del /s /q packages\asc-web-common
echo "ASC.Web.Components"
call yarn install --cwd web/ASC.Web.Components > build\ASC.Web.Components.log
REM xcopy web\ASC.Web.Components\node_modules packages\asc-web-components\node_modules\ /E /R /Y >> build\ASC.Web.Components.log
call yarn install --cwd packages/asc-web-components >> build\ASC.Web.Components.log
call yarn link --cwd packages/asc-web-components
echo "ASC.Web.Common"
call yarn link "asc-web-components" --cwd web/ASC.Web.Common
call yarn install --cwd web/ASC.Web.Common > build\ASC.Web.Common.log
REM xcopy web\ASC.Web.Common\node_modules packages\asc-web-common\node_modules\ /E /R /Y >> build\ASC.Web.Components.log
call yarn install --cwd packages/asc-web-common >> build\ASC.Web.Common.log
call yarn link --cwd packages/asc-web-common
echo "ASC.Web.Client"
call yarn link "asc-web-components" --cwd web/ASC.Web.Client
call yarn link "asc-web-common" --cwd web/ASC.Web.Client
call yarn install --cwd web/ASC.Web.Client > build\ASC.Web.Client.log
echo "ASC.Web.People.Client"
call yarn link "asc-web-components" --cwd products/ASC.People/Client
call yarn link "asc-web-common" --cwd products/ASC.People/Client
call yarn install --cwd products/ASC.People/Client > build\ASC.Web.People.Client.log
xcopy build\cra\*.* products\ASC.People\Client\node_modules\ /E /R /Y

View File

@ -5,6 +5,7 @@
"homepage": "/products/people",
"dependencies": {
"asc-web-components": "file:../../../packages/asc-web-components",
"asc-web-common": "file:../../../packages/asc-web-common",
"axios": "^0.19.0",
"bootstrap": "4.3.1",
"connected-react-router": "6.5.2",

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,17 +1,15 @@
import React, { Suspense } from "react";
import { connect } from "react-redux";
import { Router, Switch } from "react-router-dom";
import { Router, Switch, Redirect } from "react-router-dom";
import { Loader } from "asc-web-components";
import PeopleLayout from "./components/Layout/index";
import Home from "./components/pages/Home";
import PrivateRoute from "./helpers/privateRoute";
import Profile from './components/pages/Profile';
import ProfileAction from './components/pages/ProfileAction';
import GroupAction from './components/pages/GroupAction';
import { Error404 } from "./components/pages/Error";
import Reassign from './components/pages/Reassign';
import Import from './components/pages/Import';
import history from './history';
import { history, PrivateRoute, PublicRoute, Login, Error404 } from "asc-web-common";
/*const Profile = lazy(() => import("./components/pages/Profile"));
const ProfileAction = lazy(() => import("./components/pages/ProfileAction"));
@ -26,6 +24,7 @@ const App = ({ settings }) => {
fallback={<Loader className="pageLoader" type="rombs" size={40} />}
>
<Switch>
<Redirect exact from="/" to={`${homepage}`} />
<PrivateRoute exact path={[homepage, `${homepage}/filter`]} component={Home} />
<PrivateRoute
path={`${homepage}/view/:userId`}
@ -62,6 +61,7 @@ const App = ({ settings }) => {
component={Import}
restricted
/>
<PublicRoute exact path={["/login","/login/error=:error", "/login/confirmed-email=:confirmedEmail"]} component={Login} />
<PrivateRoute component={Error404} />
</Switch>
</Suspense>

View File

@ -1,7 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
import { Text } from 'asc-web-components';
import { getCurrentModule } from '../../../store/auth/selectors';
import { store } from 'asc-web-common';
const { getCurrentModule } = store.auth.selectors;
const ArticleHeaderContent = ({currentModuleName}) => {
return <Text.MenuHeader>{currentModuleName}</Text.MenuHeader>;

View File

@ -8,10 +8,11 @@ import {
toastr
} from "asc-web-components";
import InviteDialog from './../../dialogs/Invite';
import { isAdmin } from '../../../store/auth/selectors';
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from '../i18n';
import { typeUser, typeGuest, department } from './../../../helpers/customNames';
import { store } from 'asc-web-common';
const { isAdmin } = store.auth.selectors;
class PureArticleMainButtonContent extends React.Component {
constructor(props) {

View File

@ -3,10 +3,10 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from "react-router";
import { Layout, Toast } from 'asc-web-components';
import { logout } from '../../store/auth/actions';
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from "./i18n";
//import { isAdmin } from "../../store/auth/selectors";
import { store } from 'asc-web-common';
const { logout } = store.auth.actions;
class PurePeopleLayout extends React.Component {
shouldComponentUpdate(nextProps) {

View File

@ -11,12 +11,13 @@ import {
Textarea,
Text
} from "asc-web-components";
import { getShortenedLink } from "../../../store/services/api";
import { withTranslation, I18nextProvider } from "react-i18next";
import i18n from "./i18n";
import { typeGuests } from "./../../../helpers/customNames";
import styled from "styled-components";
import copy from "copy-to-clipboard";
import { api } from "asc-web-common";
const { getShortenedLink } = api.portal;
const ModalDialogContainer = styled.div`
.margin-text {

View File

@ -1,61 +0,0 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: '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: {
useSuspense: true
},
backend: {
loadPath: `${config.homepage}/locales/Error/{{lng}}/{{ns}}.json`
}
});
} else if (process.env.NODE_ENV === "development") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
},
ru: {
translation: require("./locales/ru/translation.json")
}
};
newInstance.init({
resources: resources,
lng: '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: {
useSuspense: true
}
});
}
export default newInstance;

View File

@ -30,16 +30,13 @@ import {
getUserStatus,
getUserRole
} from "../../../../../store/people/selectors";
import { isAdmin, isMe } from "../../../../../store/auth/selectors";
import { EmployeeStatus } from "../../../../../helpers/constants";
import {
resendUserInvites,
sendInstructionsToDelete,
sendInstructionsToChangePassword,
deleteUser
} from "../../../../../store/services/api";
import { isMobileOnly } from "react-device-detect";
import isEqual from "lodash/isEqual";
import { store, api, constants } from 'asc-web-common';
const { isAdmin, isMe } = store.auth.selectors;
const { resendUserInvites, sendInstructionsToDelete, sendInstructionsToChangePassword, deleteUser } = api.people;
const { EmployeeStatus } = constants;
class SectionBodyContent extends React.PureComponent {
constructor(props) {

View File

@ -4,7 +4,7 @@ import { RowContent, Link, LinkWithDropdown, Icons, Text } from "asc-web-compone
import { connect } from "react-redux";
import { getUserStatus } from "../../../../../store/people/selectors";
import { useTranslation } from 'react-i18next';
import history from "../../../../../history";
import { history } from "asc-web-common";
const getFormatedGroups = (user, status) => {
let temp = [];

View File

@ -4,7 +4,6 @@ import { FilterInput } from "asc-web-components";
import { fetchPeople } from "../../../../../store/people/actions";
import find from "lodash/find";
import result from "lodash/result";
import { isAdmin } from "../../../../../store/auth/selectors";
import { withTranslation } from "react-i18next";
import {
typeGuest,
@ -13,6 +12,8 @@ import {
} from "./../../../../../helpers/customNames";
import { withRouter } from "react-router";
import { getFilterByLocation } from "../../../../../helpers/converters";
import { store } from 'asc-web-common';
const { isAdmin } = store.auth.selectors;
const getEmployeeStatus = filterValues => {
const employeeStatus = result(

View File

@ -14,23 +14,21 @@ import {
getSelectedGroup,
getSelectionIds
} from "../../../../../store/people/selectors";
import { isAdmin } from "../../../../../store/auth/selectors";
import { withTranslation } from "react-i18next";
import {
updateUserStatus,
updateUserType,
fetchPeople
} from "../../../../../store/people/actions";
import { EmployeeStatus, EmployeeType } from "../../../../../helpers/constants";
import {
typeUser,
typeGuest
} from "../../../../../helpers/../helpers/customNames";
import {
resendUserInvites,
deleteUsers
} from "../../../../../store/services/api";
import { deleteGroup } from "../../../../../store/group/actions";
import { store, api, constants } from 'asc-web-common';
const { isAdmin } = store.auth.selectors;
const { resendUserInvites, deleteUsers } = api.people;
const { EmployeeStatus, EmployeeType } = constants;
const StyledContainer = styled.div`
.group-button-menu-container {

View File

@ -1,7 +1,6 @@
import React from "react";
import { Trans } from 'react-i18next';
import { department as departmentName, position, employedSinceDate } from '../../../../../../helpers/customNames';
import { resendUserInvites, sendInstructionsToChangeEmail } from "../../../../../../store/services/api";
import {
Text,
TextInput,
@ -14,7 +13,8 @@ import {
HelpButton
} from "asc-web-components";
import styled from 'styled-components';
import history from "../../../../../../history";
import { history, api } from "asc-web-common";
const { resendUserInvites, sendInstructionsToChangeEmail } = api.people;
const InfoContainer = styled.div`
margin-bottom: 24px;

View File

@ -11,9 +11,10 @@ import {
import { connect } from "react-redux";
import styled from 'styled-components';
import { getUserRole, getUserContacts } from "../../../../../store/people/selectors";
import { isAdmin, isMe } from "../../../../../store/auth/selectors";
import { updateProfileCulture } from "../../../../../store/profile/actions";
import ProfileInfo from "./ProfileInfo/ProfileInfo"
import ProfileInfo from "./ProfileInfo/ProfileInfo";
import { store } from 'asc-web-common';
const { isAdmin, isMe } = store.auth.selectors;
const ProfileWrapper = styled.div`
display: flex;

View File

@ -13,14 +13,21 @@ import {
AvatarEditor
} from "asc-web-components";
import { withRouter } from "react-router";
import { isAdmin, isMe } from "../../../../../store/auth/selectors";
import { getUserStatus, toEmployeeWrapper } from "../../../../../store/people/selectors";
import { withTranslation } from 'react-i18next';
import { resendUserInvites } from "../../../../../store/services/api";
import { EmployeeStatus } from "../../../../../helpers/constants";
import { updateUserStatus, fetchPeople } from "../../../../../store/people/actions";
import { fetchProfile } from '../../../../../store/profile/actions';
import {
getUserStatus,
toEmployeeWrapper
} from "../../../../../store/people/selectors";
import { withTranslation } from "react-i18next";
import {
updateUserStatus,
fetchPeople
} from "../../../../../store/people/actions";
import { fetchProfile } from "../../../../../store/profile/actions";
import styled from "styled-components";
import { store, api, constants } from "asc-web-common";
const { isAdmin, isMe } = store.auth.selectors;
const {
resendUserInvites,
sendInstructionsToDelete,
sendInstructionsToChangePassword,
sendInstructionsToChangeEmail,
@ -28,8 +35,8 @@ import {
loadAvatar,
deleteAvatar,
deleteUser
} from "../../../../../store/services/api";
import styled from 'styled-components';
} = api.people;
const { EmployeeStatus } = constants;
const wrapperStyle = {
display: "flex",
@ -70,38 +77,42 @@ class SectionHeaderContent extends React.PureComponent {
header: "",
body: "",
buttons: [],
newEmail: profile.email,
newEmail: profile.email
},
avatar: {
tmpFile: "",
image: profile.avatarDefault ? "data:image/png;base64," + profile.avatarDefault : null,
image: profile.avatarDefault
? "data:image/png;base64," + profile.avatarDefault
: null,
defaultWidth: 0,
defaultHeight: 0
}
};
return newState;
}
};
openAvatarEditor = () => {
let avatarDefault = this.state.profile.avatarDefault ? "data:image/png;base64," + this.state.profile.avatarDefault : null;
let avatarDefault = this.state.profile.avatarDefault
? "data:image/png;base64," + this.state.profile.avatarDefault
: null;
let _this = this;
if (avatarDefault !== null) {
let img = new Image();
img.onload = function () {
img.onload = function() {
_this.setState({
avatar: {
defaultWidth: img.width,
defaultHeight: img.height
}
})
});
};
img.src = avatarDefault;
}
this.setState({
visibleAvatarEditor: true,
visibleAvatarEditor: true
});
}
};
onLoadFileAvatar = file => {
let data = new FormData();
@ -109,64 +120,72 @@ class SectionHeaderContent extends React.PureComponent {
data.append("file", file);
data.append("Autosave", false);
loadAvatar(this.state.profile.id, data)
.then((response) => {
.then(response => {
var img = new Image();
img.onload = function () {
img.onload = function() {
var stateCopy = Object.assign({}, _this.state);
stateCopy.avatar = {
tmpFile: response.data,
image: response.data,
defaultWidth: img.width,
defaultHeight: img.height
}
};
_this.setState(stateCopy);
};
img.src = response.data;
})
.catch((error) => toastr.error(error));
}
.catch(error => toastr.error(error));
};
onSaveAvatar = (isUpdate, result) => {
if (isUpdate) {
createThumbnailsAvatar(this.state.profile.id, {
x: Math.round(result.x * this.state.avatar.defaultWidth - result.width / 2),
y: Math.round(result.y * this.state.avatar.defaultHeight - result.height / 2),
x: Math.round(
result.x * this.state.avatar.defaultWidth - result.width / 2
),
y: Math.round(
result.y * this.state.avatar.defaultHeight - result.height / 2
),
width: result.width,
height: result.height,
tmpFile: this.state.avatar.tmpFile
})
.then((response) => {
.then(response => {
let stateCopy = Object.assign({}, this.state);
stateCopy.visibleAvatarEditor = false;
stateCopy.avatar.tmpFile = '';
stateCopy.profile.avatarMax = response.max + '?_=' + Math.floor(Math.random() * Math.floor(10000));
stateCopy.avatar.tmpFile = "";
stateCopy.profile.avatarMax =
response.max +
"?_=" +
Math.floor(Math.random() * Math.floor(10000));
toastr.success("Success");
this.setState(stateCopy);
})
.catch((error) => toastr.error(error))
.catch(error => toastr.error(error))
.then(() => this.props.fetchProfile(this.state.profile.id));
} else {
deleteAvatar(this.state.profile.id)
.then((response) => {
.then(response => {
let stateCopy = Object.assign({}, this.state);
stateCopy.visibleAvatarEditor = false;
stateCopy.profile.avatarMax = response.big;
toastr.success("Success");
this.setState(stateCopy);
})
.catch((error) => toastr.error(error));
}
.catch(error => toastr.error(error));
}
};
onCloseAvatarEditor = () => {
this.setState({
visibleAvatarEditor: false,
visibleAvatarEditor: false
});
}
};
onEmailChange = event => {
const emailRegex = /.+@.+\..+/;
const newEmail = (event && event.target.value) || this.state.dialog.newEmail;
const newEmail =
(event && event.target.value) || this.state.dialog.newEmail;
const hasError = !emailRegex.test(newEmail);
const dialog = {
@ -174,7 +193,9 @@ class SectionHeaderContent extends React.PureComponent {
header: "Change email",
body: (
<Text.Body>
<span style={{ display: "block", marginBottom: "8px" }}>The activation instructions will be sent to the entered email</span>
<span style={{ display: "block", marginBottom: "8px" }}>
The activation instructions will be sent to the entered email
</span>
<TextInput
id="new-email"
scale={true}
@ -197,17 +218,20 @@ class SectionHeaderContent extends React.PureComponent {
],
newEmail: newEmail
};
this.setState({ dialog: dialog })
}
this.setState({ dialog: dialog });
};
onSendEmailChangeInstructions = () => {
sendInstructionsToChangeEmail(this.state.profile.id, this.state.dialog.newEmail)
.then((res) => {
sendInstructionsToChangeEmail(
this.state.profile.id,
this.state.dialog.newEmail
)
.then(res => {
toastr.success(res);
})
.catch((error) => toastr.error(error))
.catch(error => toastr.error(error))
.finally(this.onDialogClose);
}
};
onPasswordChange = () => {
const dialog = {
@ -215,7 +239,11 @@ class SectionHeaderContent extends React.PureComponent {
header: "Change password",
body: (
<Text.Body>
Send the password change instructions to the <a href={`mailto:${this.state.profile.email}`}>{this.state.profile.email}</a> email address
Send the password change instructions to the{" "}
<a href={`mailto:${this.state.profile.email}`}>
{this.state.profile.email}
</a>{" "}
email address
</Text.Body>
),
buttons: [
@ -228,47 +256,50 @@ class SectionHeaderContent extends React.PureComponent {
/>
]
};
this.setState({ dialog: dialog })
}
this.setState({ dialog: dialog });
};
onSendPasswordChangeInstructions = () => {
sendInstructionsToChangePassword(this.state.profile.email)
.then((res) => {
.then(res => {
toastr.success(res);
})
.catch((error) => toastr.error(error))
.catch(error => toastr.error(error))
.finally(this.onDialogClose);
}
};
onDialogClose = () => {
const dialog = { visible: false, newEmail: this.state.profile.email };
this.setState({ dialog: dialog })
}
this.setState({ dialog: dialog });
};
onEditClick = () => {
const { history, settings } = this.props;
history.push(`${settings.homepage}/edit/${this.state.profile.userName}`);
}
};
onUpdateUserStatus = (status, userId) => {
const { fetchProfile, updateUserStatus } = this.props;
updateUserStatus(status, new Array(userId))
.then(() => fetchProfile(userId));
}
updateUserStatus(status, new Array(userId)).then(() =>
fetchProfile(userId)
);
};
onDisableClick = () => this.onUpdateUserStatus(EmployeeStatus.Disabled, this.state.profile.id);
onDisableClick = () =>
this.onUpdateUserStatus(EmployeeStatus.Disabled, this.state.profile.id);
onEnableClick = () => this.onUpdateUserStatus(EmployeeStatus.Active, this.state.profile.id);
onEnableClick = () =>
this.onUpdateUserStatus(EmployeeStatus.Active, this.state.profile.id);
onReassignDataClick = user => {
const { history, settings } = this.props;
history.push(`${settings.homepage}/reassign/${user.userName}`);
}
};
onDeletePersonalDataClick = () => {
toastr.success("Context action: Delete personal data");
}
};
onDeleteProfileClick = user => {
this.setState({
@ -330,7 +361,7 @@ class SectionHeaderContent extends React.PureComponent {
]
}
});
}
};
onDeleteSelfProfileClick = email => {
this.setState({
@ -376,18 +407,20 @@ class SectionHeaderContent extends React.PureComponent {
]
}
});
}
};
onInviteAgainClick = () => {
resendUserInvites(new Array(this.state.profile.id))
.then(() => toastr.success(
.then(() =>
toastr.success(
<Text.Body>
The email activation instructions have been sent to the{" "}
<b>{this.state.profile.email}</b> email address
</Text.Body>
))
)
)
.catch(error => toastr.error(error));
}
};
getUserContextOptions = (user, viewer) => {
let status = "";
@ -403,22 +436,22 @@ class SectionHeaderContent extends React.PureComponent {
return [
{
key: "edit",
label: t('EditUserDialogTitle'),
label: t("EditUserDialogTitle"),
onClick: this.onEditClick
},
{
key: "change-password",
label: t('PasswordChangeButton'),
label: t("PasswordChangeButton"),
onClick: this.onPasswordChange
},
{
key: "change-email",
label: t('EmailChangeButton'),
label: t("EmailChangeButton"),
onClick: this.onEmailChange
},
{
key: "edit-photo",
label: t('EditPhoto'),
label: t("EditPhoto"),
onClick: this.openAvatarEditor
},
isMe(user, viewer.userName)
@ -439,27 +472,27 @@ class SectionHeaderContent extends React.PureComponent {
return [
{
key: "enable",
label: t('EnableUserButton'),
label: t("EnableUserButton"),
onClick: this.onEnableClick
},
{
key: "edit-photo",
label: t('EditPhoto'),
label: t("EditPhoto"),
onClick: this.openAvatarEditor
},
{
key: "reassign-data",
label: t('ReassignData'),
label: t("ReassignData"),
onClick: this.onReassignDataClick.bind(this, user)
},
{
key: "delete-personal-data",
label: t('RemoveData'),
label: t("RemoveData"),
onClick: this.onDeletePersonalDataClick
},
{
key: "delete-profile",
label: t('DeleteSelfProfile'),
label: t("DeleteSelfProfile"),
onClick: this.onDeleteProfileClick.bind(this, user)
}
];
@ -467,17 +500,17 @@ class SectionHeaderContent extends React.PureComponent {
return [
{
key: "edit",
label: t('EditButton'),
label: t("EditButton"),
onClick: this.onEditClick
},
{
key: "invite-again",
label: t('InviteAgainLbl'),
label: t("InviteAgainLbl"),
onClick: this.onInviteAgainClick
},
{
key: "edit-photo",
label: t('EditPhoto'),
label: t("EditPhoto"),
onClick: this.openAvatarEditor
},
!isMe(user, viewer.userName) &&
@ -501,11 +534,11 @@ class SectionHeaderContent extends React.PureComponent {
default:
return [];
}
}
};
goBack = () => {
this.props.history.goBack();
}
};
render() {
const { profile, isAdmin, viewer, t } = this.props;
@ -525,12 +558,12 @@ class SectionHeaderContent extends React.PureComponent {
</div>
<Header truncate={true}>
{profile.displayName}
{profile.isLDAP && ` (${t('LDAPLbl')})`}
{profile.isLDAP && ` (${t("LDAPLbl")})`}
</Header>
{((isAdmin && !profile.isOwner) || isMe(viewer, profile.userName)) && (
<ContextMenuButton
directionX="right"
title={t('Actions')}
title={t("Actions")}
iconName="VerticalDotsIcon"
size={16}
color="#A3A9AE"
@ -562,7 +595,7 @@ class SectionHeaderContent extends React.PureComponent {
}
}
const mapStateToProps = (state) => {
const mapStateToProps = state => {
return {
settings: state.auth.settings,
profile: state.profile.targetUser,
@ -570,6 +603,9 @@ const mapStateToProps = (state) => {
isAdmin: isAdmin(state.auth.user),
filter: state.people.filter
};
}
};
export default connect(mapStateToProps, { updateUserStatus, fetchProfile, fetchPeople })(withRouter(withTranslation()(SectionHeaderContent)));
export default connect(
mapStateToProps,
{ updateUserStatus, fetchProfile, fetchPeople }
)(withRouter(withTranslation()(SectionHeaderContent)));

View File

@ -14,7 +14,11 @@ import DepartmentField from './FormFields/DepartmentField'
import ContactsField from './FormFields/ContactsField'
import InfoFieldContainer from './FormFields/InfoFieldContainer'
import { departments, department, position, employedSinceDate } from '../../../../../helpers/customNames';
import { createThumbnailsAvatar, loadAvatar } from "../../../../../store/services/api";
import { api } from "asc-web-common";
const {
createThumbnailsAvatar,
loadAvatar
} = api.people;
class CreateUserForm extends React.Component {
@ -369,10 +373,10 @@ class CreateUserForm extends React.Component {
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Text.Body fontSize={13}>
<Text.Body fontSize={13} as="div">
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications.
<p className="tooltip_email" style={{marginTop: "1rem", marginBottom: "1rem"}} >
<p className="tooltip_email" style={{margin: "1rem 0"}} >
You can create a new mail on the domain as the primary.
In this case, you must set a one-time password so that the user can log in to the portal for the first time.
</p>

View File

@ -5,7 +5,6 @@ import { Avatar, Button, Textarea, Text, toastr, ModalDialog, TextInput, AvatarE
import { withTranslation, Trans } from 'react-i18next';
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts, mapGroupsToGroupSelectorOptions, mapGroupSelectorOptionsToGroups, filterGroupSelectorOptions } from "../../../../../store/people/selectors";
import { updateProfile } from '../../../../../store/profile/actions';
import { sendInstructionsToChangePassword, sendInstructionsToChangeEmail } from "../../../../../store/services/api";
import { MainContainer, AvatarContainer, MainFieldsContainer } from './FormFields/Form'
import TextField from './FormFields/TextField'
import TextChangeField from './FormFields/TextChangeField'
@ -15,8 +14,9 @@ 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 { createThumbnailsAvatar, loadAvatar, deleteAvatar } from "../../../../../store/services/api";
import styled from "styled-components";
import { api } from "asc-web-common";
const {sendInstructionsToChangePassword, sendInstructionsToChangeEmail, createThumbnailsAvatar, loadAvatar, deleteAvatar} = api.people;
const Table = styled.table`
width: 100%;
@ -449,7 +449,7 @@ class UpdateUserForm extends React.Component {
{t("ProfileTypePopupHelper")}
</Text.Body>
<Text.Body fontSize={12}>
<Text.Body fontSize={12} as="div">
<Table>
<tbody>
<tr>
@ -554,10 +554,10 @@ class UpdateUserForm extends React.Component {
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Text.Body fontSize={13}>
<Text.Body fontSize={13} as="div">
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications.
<p style={{marginTop: "1rem"/*, height: "0", visibility: "hidden"*/}}>
<p style={{margin: "1rem 0"/*, height: "0", visibility: "hidden"*/}}>
You can create a new mail on the domain as the primary.
In this case, you must set a one-time password so that the user can log in to the portal for the first time.
</p>

View File

@ -53,7 +53,7 @@
"maxSizeFileError": "Maximum file size exceeded",
"unknownError": "Error",
"editAvatar": "Edit photo",
"CalendarSelectDate": "Select Date:",
"CalendarSelectDate": "Select date:",
"Employee": "Employee",
"Calendar": "Calendar"
}

View File

@ -1,4 +1,3 @@
export const AUTH_KEY = "asc_auth_key";
export const GUID_EMPTY = "00000000-0000-0000-0000-000000000000";
export const EMPLOYEE_STATUS = "employeestatus";
export const ACTIVATION_STATUS = "activationstatus";
@ -9,32 +8,3 @@ export const SORT_BY = "sortby";
export const SORT_ORDER = "sortorder";
export const PAGE = "page";
export const PAGE_COUNT = "pagecount";
/**
* Enum for employee activation status.
* @readonly
*/
export const EmployeeActivationStatus = Object.freeze({
NotActivated: 0,
Activated: 1,
Pending: 2,
AutoGenerated: 4
});
/**
* Enum for employee status.
* @readonly
*/
export const EmployeeStatus = Object.freeze({
Active: 1,
Disabled: 2
});
/**
* Enum for employee type.
* @readonly
*/
export const EmployeeType = Object.freeze({
User: 1,
Guest: 2
});

View File

@ -9,23 +9,9 @@ import {
PAGE,
PAGE_COUNT
} from "./constants";
import Filter from "../store/people/filter";
export function getObjectByLocation(location) {
if (!location.search || !location.search.length) return null;
const searchUrl = location.search.substring(1);
const object = JSON.parse(
'{"' +
decodeURI(searchUrl)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
);
return object;
}
import { api, utils } from "asc-web-common";
const { Filter } = api;
const { getObjectByLocation } = utils;
export function getFilterByLocation(location) {
const urlFilter = getObjectByLocation(location);

View File

@ -1,18 +0,0 @@
import React, { Component } from "react";
export class ExternalRedirect extends Component {
constructor( props ){
super();
this.state = {
location: props.to
};
}
componentWillMount(){
window.location.replace(this.state.location);
}
render(){
return (<></>);
}
}
export default ExternalRedirect;

View File

@ -1,53 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { Route } from "react-router-dom";
import ExternalRedirect from '../helpers/externalRedirect';
import { PageLayout, Loader } from "asc-web-components";
import { isAdmin, isMe } from "../store/auth/selectors";
import { Error404 } from "../components/pages/Error";
const PrivateRoute = ({ component: Component, ...rest }) => {
const { isAuthenticated, isLoaded, isAdmin, restricted, allowForMe, currentUser, computedMatch } = rest;
const { userId } = computedMatch.params;
console.log("PrivateRoute render", rest);
return (
<Route
{...rest}
render={props =>
!isLoaded ? (
<PageLayout
sectionBodyContent={
<Loader className="pageLoader" type="rombs" size={40} />
}
/>
) : isAuthenticated ? (
restricted
? (isAdmin || (allowForMe && userId && isMe(currentUser, userId))
? <Component {...props} />
: <Error404 />
)
: <Component {...props} />
) : (
<ExternalRedirect to="/login" />
)
}
/>
);
};
PrivateRoute.propTypes = {
isAuthenticated: PropTypes.bool.isRequired
};
function mapStateToProps(state) {
return {
isAuthenticated: state.auth.isAuthenticated,
isLoaded: state.auth.isLoaded,
isAdmin: isAdmin(state.auth.user),
currentUser: state.auth.user
};
}
export default connect(mapStateToProps)(PrivateRoute);

View File

@ -1,22 +1,46 @@
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { AUTH_KEY } from "./helpers/constants";
import store from "./store/store";
import { fetchGroups, fetchPeople } from "./store/people/actions";
import config from "../package.json";
import "./custom.scss";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { setIsLoaded, getUserInfo } from "./store/auth/actions";
import { store as commonStore, constants } from "asc-web-common";
import { getFilterByLocation } from "./helpers/converters";
const { setIsLoaded, getUserInfo, setCurrentProductId, setCurrentProductHomePage, getPortalPasswordSettings, getPortalCultures, getPortalInviteLinks } = commonStore.auth.actions;
const { AUTH_KEY } = constants;
const token = localStorage.getItem(AUTH_KEY);
if (token) {
store.dispatch(getUserInfo);
getUserInfo(store.dispatch)
.then(() => getPortalPasswordSettings(store.dispatch))
.then(() => getPortalCultures(store.dispatch))
.then(() => store.dispatch(getPortalInviteLinks()))
.then(() => fetchGroups(store.dispatch))
.then(() => {
var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm");
const match = window.location.pathname.match(re);
if (match && match.length > 0) {
const newFilter = getFilterByLocation(window.location);
return fetchPeople(newFilter, store.dispatch);
}
return Promise.resolve();
})
.then(() => {
store.dispatch(setCurrentProductHomePage(config.homepage));
store.dispatch(setCurrentProductId("f4d98afd-d336-4332-8778-3c6945c81ea0"));
store.dispatch(setIsLoaded(true));
});
}
else {
store.dispatch(setIsLoaded(true));
}
};
ReactDOM.render(
<Provider store={store}>

View File

@ -1,79 +0,0 @@
import * as api from "../services/api";
import { fetchGroups, fetchPeople } from "../people/actions";
import { setAuthorizationToken } from "../../store/services/client";
import { getFilterByLocation } from "../../helpers/converters";
import config from "../../../package.json";
export const LOGIN_POST = "LOGIN_POST";
export const SET_CURRENT_USER = "SET_CURRENT_USER";
export const SET_MODULES = "SET_MODULES";
export const SET_SETTINGS = "SET_SETTINGS";
export const SET_IS_LOADED = "SET_IS_LOADED";
export const LOGOUT = "LOGOUT";
export function setCurrentUser(user) {
return {
type: SET_CURRENT_USER,
user
};
}
export function setModules(modules) {
return {
type: SET_MODULES,
modules
};
}
export function setSettings(settings) {
return {
type: SET_SETTINGS,
settings
};
}
export function setIsLoaded(isLoaded) {
return {
type: SET_IS_LOADED,
isLoaded
};
}
export function setLogout() {
return {
type: LOGOUT
};
}
export async function getUserInfo(dispatch) {
const { user, modules, settings } = await api.getInitInfo();
let newSettings = settings;
if (user.isAdmin) {
const inviteLinks = await api.getInvitationLinks();
newSettings = Object.assign(newSettings, inviteLinks);
}
dispatch(setCurrentUser(user));
dispatch(setModules(modules));
dispatch(setSettings(newSettings));
await fetchGroups(dispatch);
var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm");
const match = window.location.pathname.match(re);
if (match && match.length > 0)
{
const newFilter = getFilterByLocation(window.location);
await fetchPeople(newFilter, dispatch);
}
return dispatch(setIsLoaded(true));
}
export function logout() {
return dispatch => {
return api.logout()
.then(() => dispatch(setLogout()));
}
};

View File

@ -1,59 +0,0 @@
import { SET_CURRENT_USER, SET_MODULES, SET_IS_LOADED, LOGOUT, SET_SETTINGS } from './actions';
import isEmpty from 'lodash/isEmpty';
import config from "../../../package.json";
const initialState = {
isAuthenticated: false,
isLoaded: false,
user: {},
modules: [],
settings: {
currentProductId: "f4d98afd-d336-4332-8778-3c6945c81ea0",
culture: "en-US",
trustedDomains: [],
trustedDomainsType: 1,
timezone: "UTC",
utcOffset: "00:00:00",
utcHoursOffset: 0,
homepage: config.homepage,
datePattern: "M/d/yyyy",
datePatternJQ: "00/00/0000",
dateTimePattern: "dddd, MMMM d, yyyy h:mm:ss tt",
datepicker: {
datePattern: "mm/dd/yy",
dateTimePattern: "DD, mm dd, yy h:mm:ss tt",
timePattern: "h:mm tt"
},
hasShortenService: false
}
};
const authReducer = (state = initialState, action) => {
switch (action.type) {
case SET_CURRENT_USER:
return Object.assign({}, state, {
isAuthenticated: !isEmpty(action.user),
user: action.user
});
case SET_MODULES:
return Object.assign({}, state, {
modules: action.modules
});
case SET_SETTINGS:
return Object.assign({}, state, {
settings: { ...state.settings, ...action.settings }
});
case SET_IS_LOADED:
return Object.assign({}, state, {
isLoaded: action.isLoaded
});
case LOGOUT:
return Object.assign({}, initialState, {
isLoaded: true
});
default:
return state;
}
}
export default authReducer;

View File

@ -1,6 +1,5 @@
import * as api from "../../store/services/api";
import { setGroups, fetchPeople } from "../people/actions";
import history from "../../history";
import { api, history } from "asc-web-common";
export const SET_GROUP = "SET_GROUP";
export const CLEAN_GROUP = "CLEAN_GROUP";
@ -20,7 +19,7 @@ export function resetGroup() {
export function fetchGroup(groupId) {
return dispatch => {
api.getGroup(groupId)
api.groups.getGroup(groupId)
.then(group => dispatch(setGroup(group || null)));
};
}
@ -30,7 +29,7 @@ export function createGroup(groupName, groupManager, members) {
const { people } = getState();
const { groups } = people;
return api
return api.groups
.createGroup(groupName, groupManager, members)
.then(newGroup => {
history.goBack();
@ -46,7 +45,7 @@ export function updateGroup(id, groupName, groupManager, members) {
const { people } = getState();
const { groups } = people;
return api
return api.groups
.updateGroup(id, groupName, groupManager, members)
.then(newGroup => {
history.goBack();
@ -65,7 +64,7 @@ export function deleteGroup(id) {
const { people } = getState();
const { groups, filter } = people;
return api
return api.groups
.deleteGroup(id)
.then(res => {
return dispatch(setGroups(groups.filter(g => g.id !== id)));

View File

@ -1,6 +1,4 @@
import * as api from "../services/api";
import Filter from "./filter";
import history from "../../history";
import { api, history, constants } from "asc-web-common";
import config from "../../../package.json";
import {
EMPLOYEE_STATUS,
@ -11,10 +9,11 @@ import {
SORT_BY,
SORT_ORDER,
PAGE,
PAGE_COUNT,
EmployeeStatus
PAGE_COUNT
} from "../../helpers/constants";
import unionBy from 'lodash/unionBy';
const { EmployeeStatus } = constants;
const { Filter } = api;
export const SET_GROUPS = "SET_GROUPS";
export const SET_USERS = "SET_USERS";
@ -142,7 +141,7 @@ export function setSelectorUsers(users) {
export function fetchSelectorUsers() {
return dispatch => {
api.getSelectorUserList().then(data => {
api.people.getSelectorUserList().then(data => {
const users = data.items;
return dispatch(setSelectorUsers(users));
});
@ -150,7 +149,7 @@ export function fetchSelectorUsers() {
}
export function fetchGroups(dispatchFunc = null) {
return api.getGroupList().then(groups => {
return api.groups.getGroupList().then(groups => {
return dispatchFunc
? dispatchFunc(setGroups(groups))
: Promise.resolve(dispatch => dispatch(setGroups(groups)));
@ -179,7 +178,7 @@ function fetchPeopleByFilter(dispatch, filter) {
filterData.employeeStatus = EmployeeStatus.Active;
}
return api.getUserList(filterData).then(data => {
return api.people.getUserList(filterData).then(data => {
filterData.total = data.total;
dispatch(setFilter(filterData));
dispatch({
@ -192,7 +191,7 @@ function fetchPeopleByFilter(dispatch, filter) {
export function updateUserStatus(status, userIds) {
return (dispatch, getState) => {
return api.updateUserStatus(status, userIds).then(users => {
return api.people.updateUserStatus(status, userIds).then(users => {
const { people } = getState();
const { users: currentUsers } = people;
@ -205,7 +204,7 @@ export function updateUserStatus(status, userIds) {
export function updateUserType(type, userIds) {
return dispatch => {
return api.updateUserType(type, userIds).then(users => {
return api.people.updateUserType(type, userIds).then(users => {
users.forEach(user => {
dispatch(setUser(user));
});

View File

@ -1,142 +0,0 @@
import { toUrlParams } from "../services/converter";
const DEFAULT_PAGE = 0;
const DEFAULT_PAGE_COUNT = 25;
const DEFAULT_TOTAL = 0;
const DEFAULT_SORT_BY = "firstname";
const DEFAULT_SORT_ORDER = "ascending";
const DEFAULT_EMPLOYEE_STATUS = null;
const DEFAULT_ACTIVATION_STATUS = null;
const DEFAULT_ROLE = null;
const DEFAULT_SEARCH = null;
const DEFAULT_GROUP = null;
class Filter {
static getDefault(total = DEFAULT_TOTAL) {
return new Filter(DEFAULT_PAGE, DEFAULT_PAGE_COUNT, total);
}
constructor(
page = DEFAULT_PAGE,
pageCount = DEFAULT_PAGE_COUNT,
total = DEFAULT_TOTAL,
sortBy = DEFAULT_SORT_BY,
sortOrder = DEFAULT_SORT_ORDER,
employeeStatus = DEFAULT_EMPLOYEE_STATUS,
activationStatus = DEFAULT_ACTIVATION_STATUS,
role = DEFAULT_ROLE,
search = DEFAULT_SEARCH,
group = DEFAULT_GROUP
) {
this.page = page;
this.pageCount = pageCount;
this.sortBy = sortBy;
this.sortOrder = sortOrder;
this.employeeStatus = employeeStatus;
this.activationStatus = activationStatus;
this.role = role;
this.search = search;
this.total = total;
this.group = group;
}
getStartIndex = () => {
return this.page * this.pageCount;
};
hasNext = () => {
return this.total - this.getStartIndex() > this.pageCount;
};
hasPrev = () => {
return this.page > 0;
};
toDto = () => {
const {
pageCount,
sortBy,
sortOrder,
employeeStatus,
activationStatus,
role,
search,
group
} = this;
let dtoFilter = {
StartIndex: this.getStartIndex(),
Count: pageCount,
sortby: sortBy,
sortorder: sortOrder,
employeestatus: employeeStatus,
activationstatus: activationStatus,
filtervalue: search,
groupId: group
//fields: "id,status,isAdmin,isOwner,isVisitor,activationStatus,userName,email,displayName,avatarSmall,
//listAdminModules,birthday,title,location,isLDAP,isSSO"
};
switch (role) {
case "admin":
dtoFilter.isadministrator = true;
break;
case "user":
dtoFilter.employeeType = 1;
break;
case "guest":
dtoFilter.employeeType = 2;
break;
default:
break;
}
return dtoFilter;
};
toUrlParams = () => {
const dtoFilter = this.toDto();
const str = toUrlParams(dtoFilter, true);
return str;
};
clone(onlySorting) {
return onlySorting
? new Filter(
DEFAULT_PAGE,
DEFAULT_PAGE_COUNT,
DEFAULT_TOTAL,
this.sortBy,
this.sortOrder
)
: new Filter(
this.page,
this.pageCount,
this.total,
this.sortBy,
this.sortOrder,
this.employeeStatus,
this.activationStatus,
this.role,
this.search,
this.group
);
}
equals(filter) {
const equals =
this.employeeStatus === filter.employeeStatus &&
this.activationStatus === filter.activationStatus &&
this.role === filter.role &&
this.group === filter.group &&
this.search === filter.search &&
this.sortBy === filter.sortBy &&
this.sortOrder === filter.sortOrder &&
this.page === filter.page &&
this.pageCount === filter.pageCount;
return equals;
}
}
export default Filter;

View File

@ -11,7 +11,8 @@ import {
SET_SELECTOR_USERS
} from "./actions";
import { isUserSelected, skipUser, getUsersBySelected } from "./selectors";
import Filter from "./filter";
import { api } from "asc-web-common";
const { Filter } = api;
const initialState = {
users: [],

View File

@ -1,6 +1,7 @@
import { find, filter, cloneDeep } from "lodash";
import { EmployeeActivationStatus, EmployeeStatus } from "../../helpers/constants";
import { isAdmin } from "../auth/selectors";
import { store, constants } from 'asc-web-common';
const { isAdmin } = store.auth.selectors;
const { EmployeeActivationStatus, EmployeeStatus} = constants;
export function getSelectedUser(selection, userId) {
return find(selection, function (obj) {

View File

@ -1,8 +1,9 @@
import * as api from "../../store/services/api";
import { isMe } from '../auth/selectors';
import { getUserByUserName } from '../people/selectors';
import { fetchPeople } from "../people/actions";
import { setCurrentUser } from "../auth/actions";
import { store, api } from 'asc-web-common';
const { setCurrentUser } = store.auth.actions;
const { isMe } = store.auth.selectors;
export const SET_PROFILE = 'SET_PROFILE';
export const CLEAN_PROFILE = 'CLEAN_PROFILE';
@ -37,7 +38,7 @@ export function fetchProfile(userName) {
} else {
const user = getUserByUserName(people.users, userName);
if (!user) {
api.getUser(userName).then(user => {
api.people.getUser(userName).then(user => {
dispatch(setProfile(user));
});
} else {
@ -54,7 +55,7 @@ export function createProfile(profile) {
const member = employeeWrapperToMemberModel(profile);
let result;
return api.createUser(member).then(user => {
return api.people.createUser(member).then(user => {
result = user;
return dispatch(setProfile(user));
}).then(() => {
@ -72,7 +73,7 @@ export function updateProfile(profile) {
const member = employeeWrapperToMemberModel(profile);
let result;
return api.updateUser(member).then(user => {
return api.people.updateUser(member).then(user => {
result = user;
return Promise.resolve(dispatch(setProfile(user)));
}).then(() => {
@ -85,7 +86,7 @@ export function updateProfile(profile) {
export function updateProfileCulture(id, culture) {
return (dispatch) => {
return api.updateUserCulture(id, culture).then(user => {
return api.people.updateUserCulture(id, culture).then(user => {
dispatch(setCurrentUser(user));
return dispatch(setProfile(user));
});
@ -94,6 +95,6 @@ export function updateProfileCulture(id, culture) {
export function getInvitationLink(isGuest = false) {
return dispatch => {
return api.getInvitationLink(isGuest);
return api.portal.getInvitationLink(isGuest);
}
};

View File

@ -1,8 +1,9 @@
import { combineReducers } from 'redux';
import authReducer from './auth/reducers';
import peopleReducer from './people/reducers';
import profileReducer from './profile/reducers';
import groupReducer from './group/reducers';
import { store } from 'asc-web-common';
const { reducer: authReducer } = store.auth;
const rootReducer = combineReducers({
auth: authReducer,

View File

@ -1,305 +0,0 @@
import { request, setAuthorizationToken } from "./client";
import axios from "axios";
import Filter from "../people/filter";
export function getModulesList() {
return request({
method: "get",
url: "/modules"
}).then(modules => {
return axios.all(
modules.map(m =>
request({
method: "get",
url: `${window.location.origin}/${m}`
})
)
);
});
}
export function getSettings() {
return request({
method: "get",
url: "/settings.json"
});
}
export function getPortalCultures() {
return request({
method: "get",
url: "/settings/cultures.json"
});
}
export function getPortalPasswordSettings() {
return request({
method: "get",
url: "/settings/security/password"
});
}
export function getUser(userId) {
return request({
method: "get",
url: `/people/${userId || "@self"}.json`
});
}
export function getSelectorUserList() {
return request({
method: "get",
url: "/people/filter.json?fields=id,displayName,groups"
});
}
export function getUserList(filter = Filter.getDefault()) {
const params =
filter && filter instanceof Filter
? `/filter.json?${filter.toUrlParams()}`
: "";
return request({
method: "get",
url: `/people${params}`
});
}
export function getGroupList() {
return request({
method: "get",
url: "/group"
});
}
export function createUser(data) {
return request({
method: "post",
url: "/people",
data
});
}
export function updateUser(data) {
return request({
method: "put",
url: `/people/${data.id}`,
data
});
}
export function updateUserCulture(id, cultureName) {
return request({
method: "put",
url: `/people/${id}/culture`,
data: { cultureName }
});
}
export function loadAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo`,
data
});
}
export function createThumbnailsAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo/thumbnails.json`,
data
});
}
export function deleteAvatar(profileId) {
return request({
method: "delete",
url: `/people/${profileId}/photo`
});
}
export function getInitInfo() {
return axios
.all([
getUser(),
getModulesList(),
getSettings(),
getPortalPasswordSettings(),
getPortalCultures()
])
.then(
axios.spread((user, modules, settings, passwordSettings, cultures) => {
const info = {
user,
modules,
settings
};
info.settings.passwordSettings = passwordSettings;
info.settings.cultures = cultures || [];
return Promise.resolve(info);
})
);
}
export function getInvitationLinks() {
const isGuest = true;
return axios.all([getInvitationLink(), getInvitationLink(isGuest)]).then(
axios.spread((userInvitationLinkResp, guestInvitationLinkResp) => {
const links = {
inviteLinks: {
userLink: userInvitationLinkResp,
guestLink: guestInvitationLinkResp
}
};
return Promise.resolve(links);
})
);
}
export function updateUserStatus(status, userIds) {
return request({
method: "put",
url: `/people/status/${status}`,
data: { userIds }
});
}
export function updateUserType(type, userIds) {
return request({
method: "put",
url: `/people/type/${type}`,
data: { userIds }
});
}
export function resendUserInvites(userIds) {
return request({
method: "put",
url: "/people/invite",
data: { userIds }
});
}
export function sendInstructionsToDelete() {
return request({
method: "put",
url: "/people/self/delete.json"
});
}
export function sendInstructionsToChangePassword(email) {
return request({
method: "post",
url: "/people/password.json",
data: { email }
});
}
export function sendInstructionsToChangeEmail(userId, email) {
return request({
method: "post",
url: "/people/email.json",
data: { userId, email }
});
}
export function deleteUser(userId) {
return request({
method: "delete",
url: `/people/${userId}.json`
});
}
export function deleteUsers(userIds) {
return request({
method: "put",
url: "/people/delete.json",
data: { userIds }
});
}
export function getGroup(groupId) {
return request({
method: "get",
url: `/group/${groupId}.json`
});
}
const GUEST_INVITE_LINK = "guestInvitationLink";
const USER_INVITE_LINK = "userInvitationLink";
const INVITE_LINK_TTL = "localStorageLinkTtl";
const LINKS_TTL = 6 * 3600 * 1000;
export function getInvitationLink(isGuest) {
const curLinksTtl = localStorage.getItem(INVITE_LINK_TTL);
const now = +new Date();
if (!curLinksTtl) {
localStorage.setItem(INVITE_LINK_TTL, now);
} else if (now - curLinksTtl > LINKS_TTL) {
localStorage.removeItem(GUEST_INVITE_LINK);
localStorage.removeItem(USER_INVITE_LINK);
localStorage.setItem(INVITE_LINK_TTL, now);
}
const link = localStorage.getItem(
isGuest ? GUEST_INVITE_LINK : USER_INVITE_LINK
);
return link
? Promise.resolve(link)
: request({
method: "get",
url: `/portal/users/invite/${isGuest ? 2 : 1}.json`
}).then(link => {
localStorage.setItem(
isGuest ? GUEST_INVITE_LINK : USER_INVITE_LINK,
link
);
return Promise.resolve(link);
});
}
export function getShortenedLink(link) {
return request({
method: "put",
url: "/portal/getshortenlink.json",
data: link
});
}
export function createGroup(groupName, groupManager, members) {
const data = { groupName, groupManager, members };
return request({
method: "post",
url: "/group.json",
data
});
}
export function updateGroup(id, groupName, groupManager, members) {
const data = { groupId: id, groupName, groupManager, members };
return request({
method: "put",
url: `/group/${id}.json`,
data
});
}
export function deleteGroup(id) {
return request({
method: "delete",
url: `/group/${id}.json`
});
}
export function logout() {
return request({
method: "post",
url: "/authentication/logout"
}).then((data) => {
setAuthorizationToken();
return Promise.resolve();
});
}

View File

@ -1,96 +0,0 @@
import axios from "axios";
import { AUTH_KEY } from "../../helpers/constants.js";
//import { toastr } from "asc-web-components";
const PREFIX = "api";
const VERSION = "2.0";
const baseURL = `${window.location.origin}/${PREFIX}/${VERSION}`;
/**
* @description axios instance for ajax requests
*/
const client = axios.create({
baseURL: baseURL,
responseType: "json",
timeout: 30000 // default is `0` (no timeout)
});
setAuthorizationToken(localStorage.getItem(AUTH_KEY));
client.interceptors.response.use(
response => {
return response;
},
error => {
if(error.isAxiosError)
return error;
if (error.response.status === 401) {
window.location.href = "/login/error=unauthorized";
}
if (error.response.status === 502) {
window.location.href = `/error/${error.response.status}`;
}
return error;
}
);
export function setAuthorizationToken(token) {
client.defaults.withCredentials = true;
if (token) {
localStorage.setItem(AUTH_KEY, true);
} else {
localStorage.clear();
}
}
const checkResponseError = res => {
if(!res) return;
if (res.data && res.data.error) {
console.error(res.data.error);
throw new Error(res.data.error.message);
}
if(res.isAxiosError && res.message) {
console.error(res.message);
//toastr.error(res.message);
throw new Error(res.message);
}
};
/**
* @description wrapper for making ajax requests
* @param {object} object with method,url,data etc.
*/
export const request = function(options) {
const onSuccess = function(response) {
checkResponseError(response);
if(!response || !response.data || response.isAxiosError)
return null;
if(response.data.hasOwnProperty("total"))
return { total: +response.data.total, items: response.data.response };
return response.data.response;
};
const onError = function(error) {
console.error("Request Failed:", error.config);
if (error.response) {
console.error("Status:", error.response.status);
console.error("Data:", error.response.data);
console.error("Headers:", error.response.headers);
} else {
console.error("Error Message:", error.message);
}
return Promise.reject(error.response || error.message);
};
return client(options)
.then(onSuccess)
.catch(onError);
};

View File

@ -1,14 +0,0 @@
export const toUrlParams = (obj, skipNull) => {
let str = "";
for (var key in obj) {
if (skipNull && !obj[key]) continue;
if (str !== "") {
str += "&";
}
str += key + "=" + encodeURIComponent(obj[key]);
}
return str;
};

View File

@ -1,573 +0,0 @@
import axios from "axios";
const BASE_URL = "http://localhost";
const PORT = "8080";
const PREFIX = "api";
const VERSION = "2.0";
const API_URL = `${BASE_URL}:${PORT}/${PREFIX}/${VERSION}`;
function fakeResponse(data) {
return Promise.resolve({
data: {
response: data
}
});
}
export function login(data) {
return axios.post(`${API_URL}/authentication`, data);
}
export function getModulesList() {
const data = [
{
title: "Documents",
link: "/products/files/",
imageUrl: "images/documents240.png",
description:
"Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed.",
isPrimary: true
},
{
title: "People",
link: "/products/people/",
imageUrl: "images/people.svg",
isPrimary: false
}
];
return fakeResponse(data);
}
export function getSettings() {
const data = {
timezone:
"Russian Standard Time;180;(UTC+03:00) Moscow, St. Petersburg;Russia TZ 2 Standard Time;Russia TZ 2 Daylight Time;[01:01:0001;12:31:2010;60;[0;02:00:00;3;5;0;];[0;03:00:00;10;5;0;];][01:01:2011;12:31:2011;60;[0;02:00:00;3;5;0;];[0;00:00:00;1;1;6;];][01:01:2012;12:31:2012;0;[1;00:00:00;1;1;];[1;00:00:00.001;1;1;];60;][01:01:2013;12:31:2013;0;[1;00:00:00;1;1;];[1;00:00:00.001;1;1;];60;][01:01:2014;12:31:2014;60;[0;00:00:00;1;1;3;];[0;02:00:00;10;5;0;];];",
trustedDomains: [],
trustedDomainsType: 1,
culture: "ru-RU",
utcOffset: "03:00:00",
utcHoursOffset: 3
};
return fakeResponse(data);
}
export function getPortalPasswordSettings() {
const data = {
minLength: 6,
upperCase: false,
digits: false,
specSymbols: false
};
return fakeResponse(data);
}
export function getUser() {
const data = {
index: "a",
type: "person",
id: "2881e6c6-7c9a-11e9-81fb-0242ac120002",
timestamp: null,
crtdate: null,
displayCrtdate: "NaN:NaN PM NaN/NaN/NaN",
displayDateCrtdate: "NaN/NaN/NaN",
displayTimeCrtdate: "NaN:NaN PM",
trtdate: null,
displayTrtdate: "",
displayDateTrtdate: "",
displayTimeTrtdate: "",
birthday: null,
userName: "",
firstName: "",
lastName: "",
displayName: "Administrator ",
email: "paul.bannov@gmail.com",
tel: "",
contacts: {
mailboxes: [
{
type: 0,
name: "mail",
title: "paul.bannov@gmail.com",
label: "Email",
istop: false,
val: "paul.bannov@gmail.com"
}
],
telephones: [],
links: []
},
avatar: "/skins/default/images/default_user_photo_size_32-32.png",
avatarBig: "/skins/default/images/default_user_photo_size_82-82.png",
avatarSmall: "/skins/default/images/default_user_photo_size_32-32.png",
groups: [],
status: 0,
activationStatus: 0,
isActivated: false,
isPending: false,
isTerminated: false,
isMe: true,
isManager: false,
isPortalOwner: true,
isAdmin: true,
listAdminModules: [],
isVisitor: false,
isOutsider: false,
sex: "",
location: "",
title: "",
notes: "",
culture: "",
profileUrl: "/products/people/profile.aspx?user=administrator",
isLDAP: false,
isSSO: false
};
return fakeResponse(data);
}
export function getUsers() {
const data = [
{
id: "657d1d0e-c9f3-4c9d-bd48-07525e539fd6",
userName: "Alexey.Safronov",
isVisitor: false,
firstName: "Alexey",
lastName: "Safronov",
email: "Alexey.Safronov@avsmedia.net",
status: 1,
activationStatus: 1,
terminated: null,
department: "",
workFrom: "2017-07-11T20:22:53.0000000+03:00",
displayName: "Safronov Alexey",
contacts: [
{
type: "mail",
value: "alexey.safronov@onlyoffice.com"
}
],
avatarMedium: "/images/default_user_photo_size_48-48.png?_=-48038267",
avatar: "/images/default_user_photo_size_82-82.png?_=-48038267",
isAdmin: false,
isLDAP: true,
isOwner: false,
isSSO: false,
avatarSmall: "/images/default_user_photo_size_32-32.png?_=-48038267",
profileUrl:
"http://localhost:8092/products/people/profile.aspx?user=alexey.safronov"
},
{
id: "646a6cff-df57-4b83-8ffe-91a24910328c",
userName: "Nikolay.Ivanov",
isVisitor: false,
firstName: "Nikolay",
lastName: "Ivanov",
email: "profi.troll@outlook.com",
birthday: "1983-09-15T04:00:00.0000000+04:00",
sex: "male",
status: 1,
activationStatus: 1,
terminated: null,
department: "",
workFrom: "2007-10-03T04:00:00.0000000+04:00",
displayName: "Ivanov Nikolay",
mobilePhone: "79081612979",
avatarMedium: "/images/default_user_photo_size_48-48.png?_=-562774739",
avatar: "/images/default_user_photo_size_82-82.png?_=-562774739",
isAdmin: true,
isLDAP: false,
listAdminModules: ["people"],
isOwner: true,
isSSO: false,
avatarSmall: "/images/default_user_photo_size_32-32.png?_=-562774739",
profileUrl:
"http://localhost:8092/products/people/profile.aspx?user=nikolay.ivanov"
}
];
return fakeResponse(data);
}
export function getGroups() {
const data = [
{
id: "0824d8a0-d860-46df-8142-9313bb298a5c",
name: "Отдел продвижения и рекламы",
manager: null
},
{
id: "098f3dac-92bd-455f-8138-966313c098da",
name: "Группа интернет-рекламы",
manager: "galina.medvedeva"
},
{
id: "1518cad6-c2b9-4644-bcb9-3fc816714ecc",
name: "Отдел тестирования",
manager: null
},
{
id: "1d42a4fb-755e-44ab-bcf5-38482c9b2415",
name: "Отдел программирования форматов",
manager: "Sergey.Kirillov"
},
{
id: "36800583-b347-4646-b303-65d969fabec1",
name: "Рига",
manager: "Kate.Osipova"
},
{
id: "3b99e536-7b68-44c4-8d44-6e745fe48348",
name: "Группа проектирования",
manager: "Anna.Medvedeva"
},
{
id: "40e2b7b4-bdb8-43f8-a012-5ab382754dba",
name: "Группа технической поддержки клиентов",
manager: "Alexey.Micheev"
},
{
id: "5fd54d7e-aff8-435f-85d0-af0e2129be85",
name: "Группа PR и продвижения",
manager: "Alexander.Galkin"
},
{
id: "613fc896-3ddd-4de1-a567-edbbc6cf1fc8",
name: "Администрация",
manager: "Lev.Bannov"
},
{
id: "70fe34d0-e589-4810-bcf8-f3a791db20bf",
name: "Отдел технической документации",
manager: "Alexander.Vnuchkov"
},
{
id: "72940d26-57f7-4994-aff9-e505e48558d4",
name: "Даллас",
manager: null
},
{
id: "cc8eea30-1260-427e-83c4-ff9e9680edba",
name: "Отдел интернет-приложений",
manager: "Alex.Bannov"
},
{
id: "f34754ff-ba4f-4f5e-bc35-1ea2921b2c44",
name: "Отдел продаж",
manager: "galina.goduhina"
},
{
id: "ff00f2ea-2960-4fe9-b477-6a5a6ab14187",
name: "Группа по работе с клиентами",
manager: "Evgenia.Olshanskaja"
}
];
return fakeResponse(data);
}
export function createUser(data) {
data.id = "00000000-0000-0000-0000-000000000000";
return fakeResponse(data);
}
export function updateUser(data) {
return fakeResponse(data);
}
export function loadAvatar(data) {
return fakeResponse(data);
}
export function createThumbnailsAvatar(data) {
return fakeResponse(data);
}
export function deleteAvatar(data) {
return fakeResponse(data);
}
export function updateUserStatus(status, userIds) {
return fakeResponse([
{
id: userIds[0],
userName: "Mike.Zanyatski",
isVisitor: false,
firstName: "Mike",
lastName: "Zanyatski",
email: "my@gmail.com",
birthday: "2019-08-19T01:39:25.3240031Z",
sex: "male",
status: status,
activationStatus: 0,
terminated: "2019-08-19T01:39:25.3240031Z",
department: "Marketing",
workFrom: "2019-08-19T01:39:25.3240031Z",
location: "Palo Alto",
notes: "Notes to worker",
displayName: null,
title: "Manager",
contacts: [
{
type: "GTalk",
value: "my@gmail.com"
}
],
groups: [
{
id: "00000000-0000-0000-0000-000000000000",
name: "Group Name",
manager: "Jake.Zazhitski"
}
],
avatarMedium: "url to medium avatar",
avatar: "url to big avatar",
isAdmin: false,
isLDAP: false,
listAdminModules: ["projects", "crm"],
isOwner: false,
cultureName: "en-EN",
isSSO: false,
avatarSmall: "url to small avatar",
profileUrl: ""
}
]);
}
export function updateUserType(type, userIds) {
return fakeResponse([
{
id: userIds[0],
userName: "Mike.Zanyatski",
isVisitor: type === 1 ? false : true,
firstName: "Mike",
lastName: "Zanyatski",
email: "my@gmail.com",
birthday: "2019-08-19T01:39:25.3240031Z",
sex: "male",
status: 1,
activationStatus: 0,
terminated: "2019-08-19T01:39:25.3240031Z",
department: "Marketing",
workFrom: "2019-08-19T01:39:25.3240031Z",
location: "Palo Alto",
notes: "Notes to worker",
displayName: null,
title: "Manager",
contacts: [
{
type: "GTalk",
value: "my@gmail.com"
}
],
groups: [
{
id: "00000000-0000-0000-0000-000000000000",
name: "Group Name",
manager: "Jake.Zazhitski"
}
],
avatarMedium: "url to medium avatar",
avatar: "url to big avatar",
isAdmin: false,
isLDAP: false,
listAdminModules: ["projects", "crm"],
isOwner: false,
cultureName: "en-EN",
isSSO: false,
avatarSmall: "url to small avatar",
profileUrl: ""
}
]);
}
export function resendUserInvites(userIds) {
return fakeResponse([
{
id: userIds[0],
userName: "Mike.Zanyatski",
isVisitor: false,
firstName: "Mike",
lastName: "Zanyatski",
email: "my@gmail.com",
birthday: "2019-08-19T01:39:25.3240031Z",
sex: "male",
status: 1,
activationStatus: 0,
terminated: "2019-08-19T01:39:25.3240031Z",
department: "Marketing",
workFrom: "2019-08-19T01:39:25.3240031Z",
location: "Palo Alto",
notes: "Notes to worker",
displayName: null,
title: "Manager",
contacts: [
{
type: "GTalk",
value: "my@gmail.com"
}
],
groups: [
{
id: "00000000-0000-0000-0000-000000000000",
name: "Group Name",
manager: "Jake.Zazhitski"
}
],
avatarMedium: "url to medium avatar",
avatar: "url to big avatar",
isAdmin: false,
isLDAP: false,
listAdminModules: ["projects", "crm"],
isOwner: false,
cultureName: "en-EN",
isSSO: false,
avatarSmall: "url to small avatar",
profileUrl: ""
}
]);
}
export function deleteUser(userId) {
return fakeResponse([
{
id: userId,
userName: "Mike.Zanyatski",
isVisitor: false,
firstName: "Mike",
lastName: "Zanyatski",
email: "my@gmail.com",
birthday: "2019-08-19T01:39:25.3240031Z",
sex: "male",
status: 1,
activationStatus: 0,
terminated: "2019-08-19T01:39:25.3240031Z",
department: "Marketing",
workFrom: "2019-08-19T01:39:25.3240031Z",
location: "Palo Alto",
notes: "Notes to worker",
displayName: null,
title: "Manager",
contacts: [
{
type: "GTalk",
value: "my@gmail.com"
}
],
groups: [
{
id: "00000000-0000-0000-0000-000000000000",
name: "Group Name",
manager: "Jake.Zazhitski"
}
],
avatarMedium: "url to medium avatar",
avatar: "url to big avatar",
isAdmin: false,
isLDAP: false,
listAdminModules: ["projects", "crm"],
isOwner: false,
cultureName: "en-EN",
isSSO: false,
avatarSmall: "url to small avatar",
profileUrl: ""
}
]);
}
export function deleteUsers(userIds) {
return fakeResponse([
{
id: userIds[0],
userName: "Mike.Zanyatski",
isVisitor: false,
firstName: "Mike",
lastName: "Zanyatski",
email: "my@gmail.com",
birthday: "2019-08-19T01:39:25.3240031Z",
sex: "male",
status: 1,
activationStatus: 0,
terminated: "2019-08-19T01:39:25.3240031Z",
department: "Marketing",
workFrom: "2019-08-19T01:39:25.3240031Z",
location: "Palo Alto",
notes: "Notes to worker",
displayName: null,
title: "Manager",
contacts: [
{
type: "GTalk",
value: "my@gmail.com"
}
],
groups: [
{
id: "00000000-0000-0000-0000-000000000000",
name: "Group Name",
manager: "Jake.Zazhitski"
}
],
avatarMedium: "url to medium avatar",
avatar: "url to big avatar",
isAdmin: false,
isLDAP: false,
listAdminModules: ["projects", "crm"],
isOwner: false,
cultureName: "en-EN",
isSSO: false,
avatarSmall: "url to small avatar",
profileUrl: ""
}
]);
}
export function sendInstructionsToDelete() {
return fakeResponse("Instruction has been sent successfully");
}
export function sendInstructionsToChangePassword() {
return fakeResponse("Instruction has been sent successfully");
}
export function sendInstructionsToChangeEmail() {
return fakeResponse("Instruction has been sent successfully");
}
export function getGroup(groupId) {
return fakeResponse({
id: "06448c0a-7f10-4c6d-9ad4-f94de2235778",
name: "All domain users",
category: "00000000-0000-0000-0000-000000000000",
parent: "00000000-0000-0000-0000-000000000000",
description: null,
manager: {
id: "646a6cff-df57-4b83-8ffe-91a24910328c",
displayName: "Alexey Safronov",
avatarSmall:
"/storage/userPhotos/root/646a6cff-df57-4b83-8ffe-91a24910328c_size_32-32.png?_=1787432031",
profileUrl:
"http://localhost:8092/products/people/profile.aspx?user=alexey.safronov1"
},
members: [
{
id: "646a6cff-df57-4b83-8ffe-91a24910328c",
displayName: "Alexey Safronov",
avatarSmall:
"/storage/userPhotos/root/646a6cff-df57-4b83-8ffe-91a24910328c_size_32-32.png?_=1787432031",
profileUrl:
"http://localhost:8092/products/people/profile.aspx?user=alexey.safronov1"
}
]
});
}
export function getInvitationLink(isGuest) {
return fakeResponse(isGuest
? "guest invitation link"
: "user invitation link");
}
export function getShortenedLink(link) {
return fakeResponse("SHORT LINK: " + link);
}

View File

@ -1791,10 +1791,17 @@ asap@~2.0.6:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
"asc-web-common@file:../../../packages/asc-web-common":
version "1.0.1"
dependencies:
axios "^0.19.0"
history "4.9.0"
"asc-web-components@file:../../../packages/asc-web-components":
version "1.0.168"
version "1.0.197"
dependencies:
email-addresses "^3.0.3"
html-to-react "^1.4.2"
moment "^2.24.0"
prop-types "^15.7.2"
punycode "^2.1.1"
@ -3628,6 +3635,14 @@ dom-serializer@0:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
dependencies:
domelementtype "^2.0.1"
entities "^2.0.0"
domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@ -3657,6 +3672,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domhandler@^3.0, domhandler@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9"
integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==
dependencies:
domelementtype "^2.0.1"
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@ -3673,6 +3695,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
domutils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08"
integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==
dependencies:
dom-serializer "^0.2.1"
domelementtype "^2.0.1"
domhandler "^3.0.0"
dot-prop@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
@ -4976,6 +5007,16 @@ html-parse-stringify2@2.0.1:
dependencies:
void-elements "^2.0.1"
html-to-react@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.2.tgz#7b628ab56cd63a52f2d0b79d0fa838a51f088a57"
integrity sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g==
dependencies:
domhandler "^3.0"
htmlparser2 "^4.0"
lodash.camelcase "^4.3.0"
ramda "^0.26"
html-webpack-plugin@4.0.0-beta.5:
version "4.0.0-beta.5"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz#2c53083c1151bfec20479b1f8aaf0039e77b5513"
@ -5000,6 +5041,16 @@ htmlparser2@^3.3.0:
inherits "^2.0.1"
readable-stream "^3.1.1"
htmlparser2@^4.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8"
integrity sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
domutils "^2.0.0"
entities "^2.0.0"
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@ -6452,6 +6503,11 @@ lodash._reinterpolate@^3.0.0:
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@ -8637,6 +8693,11 @@ raf@3.4.1, raf@^3.1.0, raf@^3.4.0:
dependencies:
performance-now "^2.1.0"
ramda@^0.26:
version "0.26.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"

View File

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"asc-web-components": "file:../../packages/asc-web-components",
"asc-web-common": "file:../../packages/asc-web-common",
"axios": "^0.19.0",
"bootstrap": "4.3.1",
"connected-react-router": "6.5.2",

View File

@ -2,11 +2,7 @@ import React, { Suspense, lazy } from "react";
import { Router, Route, Switch } from "react-router-dom";
import { Loader } from "asc-web-components";
import StudioLayout from "./components/Layout/index";
import Login from "./components/pages/Login";
import PrivateRoute from "./helpers/privateRoute";
import PublicRoute from "./helpers/publicRoute";
import { Error404 } from "./components/pages/Error";
import history from './history';
import { history, PrivateRoute, PublicRoute, Login, Error404 } from "asc-web-common";
const Home = lazy(() => import("./components/pages/Home"));
const About = lazy(() => import("./components/pages/About"));

View File

@ -3,11 +3,11 @@ import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { Layout, Toast } from "asc-web-components";
import { logout } from "../../store/auth/actions";
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from "./i18n";
import isEqual from "lodash/isEqual";
//import { isAdmin } from "../../store/auth/selectors";
import { store } from 'asc-web-common';
const { logout } = store.auth.actions;
class PureStudioLayout extends React.Component {
shouldComponentUpdate(nextProps, nextState) {

View File

@ -5,6 +5,7 @@ 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";
const ActivateUserForm = lazy(() => import("./sub-components/activateUser"));
const CreateUserForm = lazy(() => import("./sub-components/createUser"));
@ -14,7 +15,6 @@ const ChangeEmailForm = lazy(() => import("./sub-components/changeEmail"));
const ChangePhoneForm = lazy(() => import("./sub-components/changePhone"));
const ProfileRemoveForm = lazy(() => import("./sub-components/profileRemove"));
const ChangeOwnerForm = lazy(() => import("./sub-components/changeOwner"));
const Error404 = lazy(() => import("../Error"));
const Confirm = ({ match, language }) => {

View File

@ -3,9 +3,9 @@ import { withRouter } from "react-router";
import { withTranslation } from 'react-i18next';
import { PageLayout, Loader } from 'asc-web-components';
import { connect } from 'react-redux';
import { logout, changeEmail } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
import { store } from 'asc-web-common';
const { logout, changeEmail } = store.auth.actions;
class ActivateEmail extends React.PureComponent {

View File

@ -5,9 +5,11 @@ import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } fr
import styled from 'styled-components';
import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import { EmployeeActivationStatus } from './../../../../helpers/constants';
import { getConfirmationInfo, activateConfirmUser } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
import { store, constants } from 'asc-web-common';
const { getConfirmationInfo, activateConfirmUser } = store.auth.actions;
const { EmployeeActivationStatus } = constants;
const inputWidth = '400px';

View File

@ -3,9 +3,9 @@ import { withRouter } from "react-router";
import { withTranslation } from 'react-i18next';
import { PageLayout, Loader } from 'asc-web-components';
import { connect } from 'react-redux';
import { changeEmail } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
import { store } from 'asc-web-common';
const { changeEmail } = store.auth.actions;
class ChangeEmail extends React.PureComponent {
componentDidMount() {

View File

@ -13,11 +13,8 @@ import {
Loader,
toastr
} from "asc-web-components";
import {
changePassword,
getConfirmationInfo,
logout
} from "../../../../../src/store/auth/actions";
import { store } from 'asc-web-common';
const { changePassword, getConfirmationInfo, logout } = store.auth.actions;
const BodyStyle = styled(Container)`
margin-top: 70px;

View File

@ -4,8 +4,6 @@ import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next';
import styled from "styled-components";
import { Button, TextInput, PageLayout, Text } from "asc-web-components";
//import { useTranslation } from "react-i18next";
//import { login } from '../../../../../src/store/auth/actions';
const BodyStyle = styled.div`
margin: 70px auto 0 auto;

View File

@ -5,8 +5,9 @@ import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } fr
import styled from 'styled-components';
import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import { getConfirmationInfo, createConfirmUser, logout, login } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
import { store } from 'asc-web-common';
const { getConfirmationInfo, createConfirmUser, logout, login } = store.auth.actions;
const inputWidth = '400px';

View File

@ -5,8 +5,9 @@ import { Button, PageLayout, Text } from 'asc-web-components';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import { deleteSelf } from './../../../../store/services/api';
import { logout } from '../../../../store/auth/actions';
import { store, api } from 'asc-web-common';
const { logout } = store.auth.actions;
const { deleteSelf } = api.people;
const ProfileRemoveContainer = styled.div`
display: flex;
@ -46,7 +47,6 @@ class ProfileRemove extends React.PureComponent {
isLoading: false,
isProfileDeleted: true
});
//setAuthorizationToken();
console.log('success delete', res)
return logout()
})

View File

@ -1,60 +0,0 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: '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: {
useSuspense: true
},
backend: {
loadPath: `/locales/Error/{{lng}}/{{ns}}.json`
}
});
} else if (process.env.NODE_ENV === "development") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
},
ru: {
translation: require("./locales/ru/translation.json")
}
};
newInstance.init({
resources: resources,
lng: '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: {
useSuspense: true
}
});
}
export default newInstance;

View File

@ -1,3 +0,0 @@
{
"Error404Text": "Sorry, the resource cannot be found."
}

View File

@ -1,3 +0,0 @@
{
"Error404Text": "Извините, страница не найдена."
}

View File

@ -1,60 +0,0 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: '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: {
useSuspense: false
},
backend: {
loadPath: `/locales/Login/{{lng}}/{{ns}}.json`
}
});
} else if (process.env.NODE_ENV === "development") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
},
ru: {
translation: require("./locales/ru/translation.json")
}
};
newInstance.init({
resources: resources,
lng: '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: {
useSuspense: false
}
});
}
export default newInstance;

View File

@ -8,14 +8,15 @@ import {
ArticleBodyContent
} from "./Article";
import { SectionHeaderContent } from './Section';
import { setCurrentProductId } from '../../../../store/auth/actions';
import { store } from 'asc-web-common';
const { setCurrentProductId } = store.auth.actions;
const Layout = ({ currentProductId, setCurrentProductId, language, children }) => {
useEffect(() => {
currentProductId !== 'settings' && setCurrentProductId('settings');
i18n.changeLanguage(language);
}, [currentProductId, language, setCurrentProductId]);
}, [language, currentProductId, setCurrentProductId]);
return (
<I18nextProvider i18n={i18n}>

View File

@ -2,9 +2,10 @@ import React from "react";
import { connect } from "react-redux";
import { withTranslation } from 'react-i18next';
import { FieldContainer, Text, ComboBox, Loader, Button, toastr, Link, TextInput } from "asc-web-components";
import { getCultures, setLanguageAndTime, getPortalTimezones, setGreetingTitle, restoreGreetingTitle } from '../../../../../store/auth/actions';
import styled from 'styled-components';
import { Trans } from 'react-i18next';
import { store } from 'asc-web-common';
const { getPortalCultures, setLanguageAndTime, getPortalTimezones, setGreetingTitle, restoreGreetingTitle } = store.auth.actions;
const mapCulturesToArray = (cultures, t) => {
return cultures.map((culture) => {
@ -71,12 +72,12 @@ class Customization extends React.Component {
componentDidMount() {
const { getCultures, portalLanguage, portalTimeZoneId, t, getPortalTimezones } = this.props;
const { getPortalCultures, portalLanguage, portalTimeZoneId, t, getPortalTimezones } = this.props;
const { timezones, languages } = this.state;
if (!timezones.length && !languages.length) {
let languages;
getCultures()
getPortalCultures()
.then(() => {
languages = mapCulturesToArray(this.props.rawCultures, t);
})
@ -278,6 +279,6 @@ function mapStateToProps(state) {
}
export default connect(mapStateToProps, {
getCultures, setLanguageAndTime, getPortalTimezones,
getPortalCultures, setLanguageAndTime, getPortalTimezones,
setGreetingTitle, restoreGreetingTitle
})(withTranslation()(Customization));

View File

@ -1,12 +1,13 @@
import React from "react";
import { Route } from "react-router-dom";
import { ValidationResult } from "./../helpers/constants";
import { getObjectByLocation } from "./../helpers/converters";
import { PageLayout, Loader } from "asc-web-components";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import { AUTH_KEY } from "./constants";
import { checkConfirmLink } from "../store/services/api";
import { api, constants, utils } from "asc-web-common";
const { checkConfirmLink } = api.user;
const { AUTH_KEY } = constants;
const { getObjectByLocation } = utils;
class ConfirmRoute extends React.Component {
constructor(props) {

View File

@ -1,16 +1,3 @@
export const AUTH_KEY = 'asc_auth_key';
/**
* Enum for employee activation status.
* @readonly
*/
export const EmployeeActivationStatus = Object.freeze({
NotActivated: 0,
Activated: 1,
Pending: 2,
AutoGenerated: 4
});
/**
* Enum for type of confirm link.
* @readonly
@ -44,12 +31,3 @@ export const ValidationResult = Object.freeze({
Invalid: 1,
Expired: 2
});
/**
* Enum for employee status.
* @readonly
*/
export const EmployeeStatus = Object.freeze({
Active: 1,
Disabled: 2
});

View File

@ -1,15 +0,0 @@
export function getObjectByLocation(location) {
if (!location.search || !location.search.length) return null;
const searchUrl = location.search.substring(1);
const object = JSON.parse(
'{"' +
decodeURIComponent(searchUrl)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
);
return object;
}

View File

@ -1,61 +0,0 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { AUTH_KEY } from './constants';
import { connect } from "react-redux";
import { isAdmin } from "../store/auth/selectors";
import { Error404 } from "../components/pages/Error";
import { PageLayout, Loader } from "asc-web-components";
const PrivateRoute = ({ component: Component, ...rest }) => {
const { isAuthenticated, isLoaded, isAdmin, restricted } = rest;
const token = localStorage.getItem(AUTH_KEY);
// console.log("PrivateRoute render", rest);
return (
<Route
{...rest}
render={props =>
token ? (
!isLoaded ? (
<PageLayout
sectionBodyContent={
<Loader className="pageLoader" type="rombs" size={40} />
}
/>
) : isAuthenticated ? (
restricted
? (isAdmin
? <Component {...props} />
: <Error404 />
)
: <Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
};
function mapStateToProps(state) {
return {
isAuthenticated: state.auth.isAuthenticated,
isLoaded: state.auth.isLoaded,
isAdmin: isAdmin(state.auth.user)
};
}
export default connect(mapStateToProps)(PrivateRoute);

View File

@ -1,3 +0,0 @@
import { createBrowserHistory } from 'history';
export default createBrowserHistory();

View File

@ -1,27 +1,36 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { AUTH_KEY } from './helpers/constants';
import store from './store/store';
import './custom.scss';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { getUserInfo, getPortalSettings } from './store/auth/actions';
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store/store";
import "./custom.scss";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { store as commonStore, constants, history } from "asc-web-common";
const {
getUserInfo,
getPortalSettings,
setIsLoaded
} = commonStore.auth.actions;
const { AUTH_KEY } = constants;
const token = localStorage.getItem(AUTH_KEY);
if(!token) {
store.dispatch(getPortalSettings);
}
else if (!window.location.pathname.includes("confirm/EmailActivation")) {
store.dispatch(getUserInfo);
if (!token) {
getPortalSettings(store.dispatch)
.then(() => store.dispatch(setIsLoaded(true)))
.catch(e => history.push(`/login/error=${e}`));
} else if (!window.location.pathname.includes("confirm/EmailActivation")) {
getUserInfo(store.dispatch)
.then(() => store.dispatch(setIsLoaded(true)))
.catch(e => history.push(`/login/error=${e}`));
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.

View File

@ -1,255 +0,0 @@
import * as api from '../services/api';
import { setAuthorizationToken } from '../services/client';
export const LOGIN_POST = 'LOGIN_POST';
export const SET_CURRENT_USER = 'SET_CURRENT_USER';
export const SET_MODULES = 'SET_MODULES';
export const SET_SETTINGS = 'SET_SETTINGS';
export const SET_IS_LOADED = 'SET_IS_LOADED';
export const LOGOUT = 'LOGOUT';
export const SET_PASSWORD_SETTINGS = 'SET_PASSWORD_SETTINGS';
export const SET_IS_CONFIRM_LOADED = 'SET_IS_CONFIRM_LOADED';
export const SET_NEW_EMAIL = 'SET_NEW_EMAIL';
export const GET_PORTAL_CULTURES = 'GET_PORTAL_CULTURES';
export const SET_PORTAL_LANGUAGE_AND_TIME = 'SET_PORTAL_LANGUAGE_AND_TIME';
export const GET_TIMEZONES = 'GET_TIMEZONES';
export const SET_CURRENT_PRODUCT_ID = 'SET_CURRENT_PRODUCT_ID';
export const SET_GREETING_SETTINGS = "SET_GREETING_SETTINGS";
export function setCurrentUser(user) {
return {
type: SET_CURRENT_USER,
user
};
};
export function setModules(modules) {
return {
type: SET_MODULES,
modules
};
};
export function setSettings(settings) {
return {
type: SET_SETTINGS,
settings
};
};
export function setIsLoaded(isLoaded) {
return {
type: SET_IS_LOADED,
isLoaded
};
};
export function setIsConfirmLoaded(isConfirmLoaded) {
return {
type: SET_IS_CONFIRM_LOADED,
isConfirmLoaded
};
};
export function setLogout() {
return {
type: LOGOUT
};
};
export function setPasswordSettings(passwordSettings) {
return {
type: SET_PASSWORD_SETTINGS,
passwordSettings
};
};
export function setNewEmail(email) {
return {
type: SET_NEW_EMAIL,
email
};
};
export function getPortalCultures(cultures) {
return {
type: GET_PORTAL_CULTURES,
cultures
};
};
export function setPortalLanguageAndTime(newSettings) {
return {
type: SET_PORTAL_LANGUAGE_AND_TIME,
newSettings
};
};
export function getTimezones(timezones) {
return {
type: GET_TIMEZONES,
timezones
};
};
export function setCurrentProductId(currentProductId) {
return {
type: SET_CURRENT_PRODUCT_ID,
currentProductId
};
};
export function setGreetingSettings(title) {
return {
type: SET_GREETING_SETTINGS,
title
};
};
export function getUser(dispatch) {
return api.getUser()
.then(user => dispatch(setCurrentUser(user)));
}
export function getPortalSettings(dispatch) {
return api.getSettings()
.then(settings => dispatch(setSettings(settings)));
}
export function getModules(dispatch) {
return api.getModulesList()
.then(modules => dispatch(setModules(modules)));
}
const loadInitInfo = (dispatch) => {
return getPortalSettings(dispatch)
.then(getModules.bind(this, dispatch))
.then(() => dispatch(setIsLoaded(true)));
}
export function getUserInfo(dispatch) {
return getUser(dispatch)
.then(loadInitInfo.bind(this, dispatch));
};
export function login(user, pass) {
return dispatch => {
return api.login(user, pass)
.then(() => getUserInfo(dispatch));
}
};
export function logout() {
return dispatch => {
return api.logout()
.then(() => dispatch(setLogout()));
}
};
export function getConfirmationInfo(token, type) {
return dispatch => {
return api.getPasswordSettings(token)
.then((settings) => dispatch(setPasswordSettings(settings)))
.then(() => dispatch(setIsConfirmLoaded(true)));
}
};
export function createConfirmUser(registerData, loginData, key) {
const data = Object.assign({}, registerData, loginData);
return (dispatch) => {
return api.createUser(data, key)
.then(user => dispatch(setCurrentUser(user)))
.then(() => api.login(loginData.userName, loginData.password))
.then(loadInitInfo.bind(this, dispatch));
};
};
export function changePassword(userId, password, key) {
return () => api.changePassword(userId, password, key)
}
export function changeEmail(userId, email, key) {
return dispatch => {
return api.changeEmail(userId, email, key)
.then(user => dispatch(setNewEmail(user.email)));
}
}
export function activateConfirmUser(personalData, loginData, key, userId, activationStatus) {
const changedData = {
id: userId,
FirstName: personalData.firstname,
LastName: personalData.lastname
}
return dispatch => {
return api.changePassword(userId, loginData.password, key)
.then(data => {
console.log('set password success:', data);
return api.updateActivationStatus(activationStatus, userId, key);
})
.then(data => {
console.log("activation success, result:", data);
return dispatch(login(loginData.userName, loginData.password));
})
.then(data => {
console.log("log in, result:", data);
return api.updateUser(changedData);
})
.then(user => dispatch(setCurrentUser(user)));
};
};
export function getCultures() {
return dispatch => {
return api.getPortalCultures()
.then(cultures => {
dispatch(getPortalCultures(cultures));
}
);
};
}
export function setLanguageAndTime(lng, timeZoneID) {
return dispatch => {
return api.setLanguageAndTime(lng, timeZoneID)
.then(() => dispatch(setPortalLanguageAndTime({ lng, timeZoneID })));
};
}
export function getPortalTimezones() {
return dispatch => {
return api.getPortalTimezones()
.then((timezones) => {
dispatch(getTimezones(timezones))
});
};
};
/* export function getGreetingTitle() {
return dispatch => {
return api.getGreetingSettings()
.then(greetingTitle => dispatch(setGreetingSettings(greetingTitle)));
};
} */
export function setGreetingTitle(greetingTitle) {
return dispatch => {
return api.setGreetingSettings(greetingTitle)
.then((res) => {
dispatch(setGreetingSettings(greetingTitle))
});
};
}
export function restoreGreetingTitle() {
return dispatch => {
return api.restoreGreetingSettings()
.then((res) => {
dispatch(setGreetingSettings(res.companyName))
});
};
}

View File

@ -1,99 +0,0 @@
import {
SET_CURRENT_USER, SET_MODULES, SET_SETTINGS, SET_IS_LOADED, LOGOUT, SET_PASSWORD_SETTINGS, SET_IS_CONFIRM_LOADED, SET_NEW_EMAIL,
GET_PORTAL_CULTURES, SET_PORTAL_LANGUAGE_AND_TIME, GET_TIMEZONES, SET_CURRENT_PRODUCT_ID, SET_GREETING_SETTINGS
} from './actions';
import isEmpty from 'lodash/isEmpty';
import config from "../../../package.json";
const initialState = {
isAuthenticated: false,
isLoaded: false,
isConfirmLoaded: false,
user: {},
modules: [],
settings: {
currentProductId: "home",
culture: "en-US",
cultures: [],
trustedDomains: [],
trustedDomainsType: 1,
timezone: "UTC",
timezones: [],
utcOffset: "00:00:00",
utcHoursOffset: 0,
homepage: config.homepage,
datePattern: "M/d/yyyy",
datePatternJQ: "00/00/0000",
dateTimePattern: "dddd, MMMM d, yyyy h:mm:ss tt",
datepicker: {
datePattern: "mm/dd/yy",
dateTimePattern: "DD, mm dd, yy h:mm:ss tt",
timePattern: "h:mm tt"
},
greetingSettings: 'Web Office Applications'
}/*,
password: null*/
}
const authReducer = (state = initialState, action) => {
switch (action.type) {
case SET_CURRENT_USER:
return Object.assign({}, state, {
isAuthenticated: !isEmpty(action.user),
user: action.user
});
case SET_MODULES:
return Object.assign({}, state, {
modules: action.modules
});
case SET_SETTINGS:
return Object.assign({}, state, {
settings: { ...state.settings, ...action.settings }
});
case GET_PORTAL_CULTURES:
return Object.assign({}, state, {
settings: { ...state.settings, cultures: action.cultures }
});
case SET_PASSWORD_SETTINGS:
return Object.assign({}, state, {
settings: { ...state.settings, passwordSettings: action.passwordSettings }
});
case SET_IS_LOADED:
return Object.assign({}, state, {
isLoaded: action.isLoaded
});
case SET_IS_CONFIRM_LOADED:
return Object.assign({}, state, {
isConfirmLoaded: action.isConfirmLoaded
});
case SET_NEW_EMAIL:
return Object.assign({}, state, {
user: { ...state.user, email: action.email }
});
case SET_PORTAL_LANGUAGE_AND_TIME:
return Object.assign({}, state, {
settings: { ...state.settings, culture: action.newSettings.lng, timezone: action.newSettings.timeZoneID }
});
case GET_TIMEZONES:
return Object.assign({}, state, {
settings: { ...state.settings, timezones: action.timezones }
});
case SET_CURRENT_PRODUCT_ID:
return Object.assign({}, state, {
settings: { ...state.settings, currentProductId: action.currentProductId }
});
case SET_GREETING_SETTINGS:
return Object.assign({}, state, {
settings: { ...state.settings, greetingSettings: action.title }
});
case LOGOUT:
return Object.assign({}, initialState, {
settings: { greetingSettings: state.settings.greetingSettings, culture: state.settings.culture }
});
default:
return state;
}
}
export default authReducer;

View File

@ -1,3 +0,0 @@
export function isAdmin(user) {
return user.isAdmin || user.isOwner;
};

View File

@ -1,6 +1,7 @@
import { combineReducers } from 'redux';
import authReducer from './auth/reducer';
import settingsReducer from './settings/reducer';
import { store } from 'asc-web-common';
const { reducer: authReducer } = store.auth;
const rootReducer = combineReducers({
auth: authReducer,

View File

@ -1,239 +0,0 @@
import { request, setAuthorizationToken } from "./client";
import axios from "axios";
import Filter from "../settings/filter";
export function login(userName, password) {
const data = {
userName,
password
};
return request({
method: "post",
url: "/authentication.json",
data
}).then(tokenData => {
setAuthorizationToken(tokenData.token);
return Promise.resolve(tokenData);
});
}
export function logout() {
return request({
method: "post",
url: "/authentication/logout"
}).then((data) => {
setAuthorizationToken();
return Promise.resolve();
});
}
export function getModulesList() {
return request({
method: "get",
url: "/modules"
}).then(modules => {
return modules && axios.all(
modules.map(m =>
request({
method: "get",
url: `${window.location.origin}/${m}`
})
)
);
});
}
export function getUser() {
return request({
method: "get",
url: "/people/@self.json"
});
}
export function getSettings() {
return request({
method: "get",
url: "/settings.json"
});
}
export function getPasswordSettings(key) {
return request({
method: "get",
url: "/settings/security/password",
headers: {
confirm: key
}
});
}
export function createUser(data, key) {
return request({
method: "post",
url: "/people",
data: data,
headers: { confirm: key }
});
}
export function changePassword(userId, password, key) {
const data = { password };
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
export function changeEmail(userId, email, key) {
const data = { email };
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
export function updateActivationStatus(activationStatus, userId, key) {
return request({
method: "put",
url: `/people/activationstatus/${activationStatus}.json`,
data: { userIds: [userId] },
headers: { confirm: key }
});
}
export function updateUser(data) {
return request({
method: "put",
url: `/people/${data.id}`,
data
});
}
export function checkConfirmLink(data) {
return request({
method: "post",
url: "/authentication/confirm.json",
data
});
}
export function deleteSelf(key) {
return request({
method: "delete",
url: "/people/@self",
headers: { confirm: key }
});
}
export function sendInstructionsToChangePassword(email) {
return request({
method: "post",
url: "/people/password.json",
data: { email }
});
}
export function getPortalCultures() {
return request({
method: "get",
url: "/settings/cultures.json"
});
}
export function setLanguageAndTime(lng, timeZoneID) {
return request({
method: "put",
url: "/settings/timeandlanguage.json",
data: { lng, timeZoneID }
});
}
export function getPortalTimezones() {
return request({
method: "get",
url: "/settings/timezones.json"
});
}
export function getUserList() {
return request({
method: "get",
url: `/people`
});
}
export function getListAdmins(filter = Filter.getDefault()) {
const filterParams = filter.toUrlParams();
const params =
"fields=id,displayName,groups,name,avatar,avatarSmall,isOwner,isAdmin,profileUrl,listAdminModules";
return request({
method: "get",
url: `/people/filter.json?${filterParams}&${params}`
});
}
export function changeProductAdmin(userId, productId, administrator) {
return request({
method: "put",
url: "/settings/security/administrator",
data: {
productId,
userId,
administrator
}
});
}
export function getUserById(userId) {
return request({
method: "get",
url: `/people/${userId}`
});
}
/* export function getGreetingSettings() {
return request({
method: "get",
url: `/settings/greetingsettings.json`,
});
} */
export function setGreetingSettings(title) {
return request({
method: "post",
url: `/settings/greetingsettings.json`,
data: { title }
});
}
export function restoreGreetingSettings() {
return request({
method: "post",
url: `/settings/greetingsettings/restore.json`
});
}
export function getLogoText() {
return request({
method: "get",
url: `/settings/whitelabel/logotext.json`
});
}
export function getLogoSizes() {
return request({
method: "get",
url: `/settings/whitelabel/sizes.json`
});
}
export function getLogoUrls() {
return request({
method: "get",
url: `/settings/whitelabel/logos.json`
});
}

View File

@ -1,14 +0,0 @@
export const toUrlParams = (obj, skipNull) => {
let str = "";
for (var key in obj) {
if (skipNull && !obj[key]) continue;
if (str !== "") {
str += "&";
}
str += key + "=" + encodeURIComponent(obj[key]);
}
return str;
};

View File

@ -1,167 +0,0 @@
import axios from "axios";
const BASE_URL = "http://localhost";
const PORT = "8080";
const PREFIX = "api";
const VERSION = "2.0";
const API_URL = `${BASE_URL}:${PORT}/${PREFIX}/${VERSION}`;
function fakeResponse(data) {
return Promise.resolve({
data: {
response: data
}
});
}
export function login(data) {
return axios.post(`${API_URL}/authentication`, data);
}
export function getModulesList() {
let data = [
{
title: "Documents",
link: "/products/files/",
imageUrl: "images/documents240.png",
description:
"Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed.",
isPrimary: true
},
{
title: "People",
link: "/products/people/",
imageUrl: "images/people.svg",
isPrimary: false
}
];
return fakeResponse(data);
}
export function getUser() {
let data = {
index: "a",
type: "person",
id: "2881e6c6-7c9a-11e9-81fb-0242ac120002",
timestamp: null,
crtdate: null,
displayCrtdate: "NaN:NaN PM NaN/NaN/NaN",
displayDateCrtdate: "NaN/NaN/NaN",
displayTimeCrtdate: "NaN:NaN PM",
trtdate: null,
displayTrtdate: "",
displayDateTrtdate: "",
displayTimeTrtdate: "",
birthday: null,
userName: "",
firstName: "",
lastName: "",
displayName: "Administrator ",
email: "paul.bannov@gmail.com",
tel: "",
contacts: {
mailboxes: [
{
type: 0,
name: "mail",
title: "paul.bannov@gmail.com",
label: "Email",
istop: false,
val: "paul.bannov@gmail.com"
}
],
telephones: [],
links: []
},
avatar: "/skins/default/images/default_user_photo_size_32-32.png",
avatarBig: "/skins/default/images/default_user_photo_size_82-82.png",
avatarSmall: "/skins/default/images/default_user_photo_size_32-32.png",
groups: [],
status: 0,
activationStatus: 0,
isActivated: false,
isPending: false,
isTerminated: false,
isMe: true,
isManager: false,
isPortalOwner: true,
isAdmin: true,
listAdminModules: [],
isVisitor: false,
isOutsider: false,
sex: "",
location: "",
title: "",
notes: "",
culture: "",
profileUrl: "/products/people/profile.aspx?user=administrator",
isLDAP: false,
isSSO: false
};
return fakeResponse(data);
}
export function getSettings() {
const data = {
timezone:
"Russian Standard Time;180;(UTC+03:00) Moscow, St. Petersburg;Russia TZ 2 Standard Time;Russia TZ 2 Daylight Time;[01:01:0001;12:31:2010;60;[0;02:00:00;3;5;0;];[0;03:00:00;10;5;0;];][01:01:2011;12:31:2011;60;[0;02:00:00;3;5;0;];[0;00:00:00;1;1;6;];][01:01:2012;12:31:2012;0;[1;00:00:00;1;1;];[1;00:00:00.001;1;1;];60;][01:01:2013;12:31:2013;0;[1;00:00:00;1;1;];[1;00:00:00.001;1;1;];60;][01:01:2014;12:31:2014;60;[0;00:00:00;1;1;3;];[0;02:00:00;10;5;0;];];",
trustedDomains: [],
trustedDomainsType: 1,
culture: "ru-RU",
utcOffset: "03:00:00",
utcHoursOffset: 3
};
return fakeResponse(data);
}
export function getPasswordSettings() {
const data = {
minLength: 12,
upperCase: true,
digits: true,
specSymbols: true
};
return fakeResponse(data);
}
export function createUser() {
const data = {
id: "00000000-0000-0000-0000-000000000000"
};
return fakeResponse(data);
}
export function changePassword() {
const data = { password: "password" };
return fakeResponse(data);
}
export function updateActivationStatus() {
return fakeResponse();
}
export function updateUser(data) {
return fakeResponse(data);
}
export function checkConfirmLink(data) {
return fakeResponse(data);
}
export function deleteUser(data) {
return fakeResponse(data);
}
export function updateUserStatus(data) {
return fakeResponse(data);
}
export function sendInstructionsToChangePassword() {
return fakeResponse("Instruction has been sent successfully");
}

View File

@ -1,7 +1,7 @@
import * as api from "../services/api";
import { api } from "asc-web-common";
import axios from "axios";
import { getSelectorOptions, getUserOptions } from "./selectors";
import Filter from "./filter";
const { Filter } = api;
export const SET_USERS = "SET_USERS";
export const SET_ADMINS = "SET_ADMINS";
@ -77,11 +77,11 @@ export function changeAdmins(userIds, productId, isAdmin, filter) {
return axios
.all(
userIds.map(userId =>
api.changeProductAdmin(userId, productId, isAdmin)
api.people.changeProductAdmin(userId, productId, isAdmin)
)
)
.then(() =>
axios.all([api.getUserList(filterData), api.getListAdmins(filterData)])
axios.all([api.people.getUserList(filterData), api.people.getListAdmins(filterData)])
)
.then(
axios.spread((users, admins) => {
@ -99,7 +99,7 @@ export function changeAdmins(userIds, productId, isAdmin, filter) {
export function getPortalOwner(userId) {
return dispatch => {
return api.getUserById(userId).then(owner => dispatch(setOwner(owner)));
return api.people.getUserById(userId).then(owner => dispatch(setOwner(owner)));
};
}
@ -110,7 +110,9 @@ export function fetchPeople(filter) {
}
return dispatch => {
return axios.all([api.getUserList(), api.getListAdmins(filterData)]).then(
return axios
.all([api.people.getUserList(filterData), api.people.getListAdmins(filterData)])
.then(
axios.spread((users, admins) => {
const options = getUserOptions(users.items, admins.items);
const newOptions = getSelectorOptions(options);
@ -143,7 +145,7 @@ export function getUpdateListAdmin(filter) {
export function getUsersOptions() {
return dispatch => {
return api.getUserList().then(users => {
return api.people.getUserList().then(users => {
const usersOptions = getSelectorOptions(users.items);
dispatch(setUsers(usersOptions));
});
@ -152,7 +154,8 @@ export function getUsersOptions() {
export function getWhiteLabelLogoText() {
return dispatch => {
return api.getLogoText().then(res => {
return api.settings.getLogoText()
.then(res => {
dispatch(setLogoText(res));
});
};
@ -160,7 +163,8 @@ export function getWhiteLabelLogoText() {
export function getWhiteLabelLogoSizes() {
return dispatch => {
return api.getLogoSizes().then(res => {
return api.settings.getLogoSizes()
.then(res => {
dispatch(setLogoSizes(res));
});
};
@ -168,7 +172,8 @@ export function getWhiteLabelLogoSizes() {
export function getWhiteLabelLogoUrls() {
return dispatch => {
return api.getLogoUrls().then(res => {
return api.settings.getLogoUrls()
.then(res => {
dispatch(setLogoUrls(Object.values(res)));
});
};

View File

@ -1,6 +1,7 @@
import { SET_USERS, SET_ADMINS, SET_OWNER, SET_OPTIONS, SET_FILTER, SET_LOGO_TEXT, SET_LOGO_SIZES, SET_LOGO_URLS } from "./actions";
import Filter from "./filter";
import { api } from "asc-web-common";
const { Filter } = api;
const initialState = {
common: {

View File

@ -1791,10 +1791,17 @@ asap@~2.0.6:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
"asc-web-common@file:../../packages/asc-web-common":
version "1.0.1"
dependencies:
axios "^0.19.0"
history "4.9.0"
"asc-web-components@file:../../packages/asc-web-components":
version "1.0.168"
version "1.0.197"
dependencies:
email-addresses "^3.0.3"
html-to-react "^1.4.2"
moment "^2.24.0"
prop-types "^15.7.2"
punycode "^2.1.1"
@ -3621,6 +3628,14 @@ dom-serializer@0:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
dependencies:
domelementtype "^2.0.1"
entities "^2.0.0"
domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@ -3650,6 +3665,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domhandler@^3.0, domhandler@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9"
integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==
dependencies:
domelementtype "^2.0.1"
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@ -3666,6 +3688,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
domutils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08"
integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==
dependencies:
dom-serializer "^0.2.1"
domelementtype "^2.0.1"
domhandler "^3.0.0"
dot-prop@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
@ -4969,6 +5000,16 @@ html-parse-stringify2@2.0.1:
dependencies:
void-elements "^2.0.1"
html-to-react@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.2.tgz#7b628ab56cd63a52f2d0b79d0fa838a51f088a57"
integrity sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g==
dependencies:
domhandler "^3.0"
htmlparser2 "^4.0"
lodash.camelcase "^4.3.0"
ramda "^0.26"
html-webpack-plugin@4.0.0-beta.5:
version "4.0.0-beta.5"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz#2c53083c1151bfec20479b1f8aaf0039e77b5513"
@ -4993,6 +5034,16 @@ htmlparser2@^3.3.0:
inherits "^2.0.1"
readable-stream "^3.1.1"
htmlparser2@^4.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8"
integrity sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
domutils "^2.0.0"
entities "^2.0.0"
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@ -6445,6 +6496,11 @@ lodash._reinterpolate@^3.0.0:
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@ -8630,6 +8686,11 @@ raf@3.4.1, raf@^3.1.0, raf@^3.4.0:
dependencies:
performance-now "^2.1.0"
ramda@^0.26:
version "0.26.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -0,0 +1 @@
dist/

View File

@ -0,0 +1,13 @@
module.exports = {
parser: 'babel-eslint',
extends: ['eslint:recommended', 'plugin:react/recommended'],
settings: {
react: {
version: 'detect',
},
},
env: {
browser: true,
node: true,
},
};

14
web/ASC.Web.Common/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
**/obj/
**/bin/
/packages/
*.suo
*.user
.vs/
.vscode/
*-lock.json
*.lock
**/node_modules/
**/storybook-static/
/web/ASC.Web.Components/dist
/build/deploy
/coverage

View File

@ -0,0 +1,7 @@
{
"presets": [
"@babel/preset-env",
"react-app"
],
"plugins": [["babel-plugin-styled-components", { "diplayName": true }]]
}

View File

@ -0,0 +1,9 @@
import '@storybook/addons';
import 'storybook-readme/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-options/register';
import '@storybook/addon-links/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-a11y/register';

View File

@ -0,0 +1,52 @@
import { configure, addDecorator, addParameters } from '@storybook/react';
import { withA11y } from '@storybook/addon-a11y';
import { addReadme } from 'storybook-readme';
import { withConsole } from '@storybook/addon-console';
import '!style-loader!css-loader!./styles.scss';
//import 'bootstrap/dist/css/bootstrap.css';
//import 'react-toastify/dist/ReactToastify.min.css';
/*
This is a package to make Story panel load and decode all files stories
Also, because of some internal usage, we cannot use the default babel config (for the library)
with this package.
In order to solve that, it's necessary a custom/simple .babelrc inside .storybook/ folder
*/
//import requireContext from 'require-context.macro';
/* Add A11y panel */
addDecorator(withA11y);
/* Enable README for all stories */
addDecorator(addReadme);
/* General options for storybook */
addParameters({
options:
{
name: 'ASC Web Common Storybook',
sortStoriesByKind: true,
showAddonPanel: true,
addonPanelInRight: true
},
/* Options for storybook-readme plugin */
readme: {
codeTheme: 'github',
StoryPreview: ({ children }) => children,
},
});
/* automatically import all files ending in *.stories.js inside src folder */
//const req = requireContext('../src', true, /\.stories\.js$/);
//const req = require.context('../src', true, /\.stories\.js$/);
const srcStories = require.context('../src', true, /\.stories\.js$/);
function loadStories() {
//req.keys().forEach(filename => req(filename));
srcStories.keys().forEach(filename => srcStories(filename));
}
addDecorator((storyFn, context) => withConsole()(storyFn)(context));
configure(loadStories, module);

View File

@ -0,0 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
const sectionStyles = {
padding: 16,
};
const Section = props => <div style={sectionStyles}>{props.children}</div>;
Section.propTypes = { children: PropTypes.node.isRequired };
export default Section;

View File

@ -0,0 +1 @@
<script>document.title = "ASC Web Common Storybook";</script>

View File

@ -0,0 +1,13 @@
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i' rel='stylesheet' type='text/css'></link>
<style>
html,
body {
margin: 0;
padding: 0;
font-size: 13px;
}
body {
overflow: auto;
}
</style>

View File

@ -0,0 +1,8 @@
/*
Any global style for all stories
It can also be an scss file, however,
you have to go to `webpack.config.js` file
and enable the options in there
*/
@import '../node_modules/bootstrap/dist/css/bootstrap.css';
@import '../node_modules/react-toastify/dist/ReactToastify.min.css';

View File

@ -0,0 +1,102 @@
const path = require('path');
const sourceFolders = [
path.resolve(__dirname),
path.resolve(__dirname, '../src'),
];
module.exports = ({ config }) => {
if (process.env.NODE_ENV === 'production') {
// remove progress plugin
// progress plugin outputs a lot of console logs, which makes
// netlify build realllllllllllllllly slow.
config.plugins = config.plugins.filter(
plugin => plugin.constructor.name !== 'ProgressPlugin'
);
config.devtool = 'none'; // TODO: should we use something differen?
} else {
config.devtool = 'cheap-module-source-map'; // TODO: should we use something differen?
}
config.devtool = 'cheap-module-source-map'; // TODO: should we use something differen?
config.module.rules = [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// add story source
{
test: /\.stories\.js$/,
loaders: [require.resolve('@storybook/source-loader')],
enforce: 'pre',
},
// Process JS with Babel.
{
test: /\.js$/,
include: sourceFolders,
use: [
{
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
compact: false,
presets: [require.resolve('../scripts/get-babel-preset')],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
highlightCode: true,
},
},
],
},
// For svg icons, we want to get them transformed into React components
// when we import them.
{
test: /\.react\.svg$/,
include: sourceFolders,
use: [
{
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
presets: [require.resolve('../scripts/get-babel-preset')],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
highlightCode: true,
},
},
{
loader: require.resolve('@svgr/webpack'),
options: {
// NOTE: disable this and manually add `removeViewBox: false` in the SVGO plugins list
// See related PR: https://github.com/smooth-code/svgr/pull/137
icon: false,
svgoConfig: {
plugins: [
{ removeViewBox: false },
// Keeps ID's of svgs so they can be targeted with CSS
{ cleanupIDs: false },
],
},
},
},
],
},
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
// Storybook uses a plugin to load and render markdown files.
{
test: /\.md$/,
use: [
{ loader: require.resolve('html-loader') },
{ loader: require.resolve('markdown-loader') },
],
}
];
return config;
};

View File

@ -0,0 +1,11 @@
# asc-web-common
# Common components and solutions to build your product
>
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## License
AGPL-3.0 © Ascensio System SIA

View File

@ -0,0 +1,21 @@
const presets = [
[
'@babel/preset-env',
{
modules: false,
},
],
'@babel/preset-react',
];
const plugins = ['@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-export-namespace-from', 'babel-plugin-styled-components'];
module.exports = {
presets,
plugins,
env: {
test: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
};

View File

@ -0,0 +1,4 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

View File

@ -0,0 +1,19 @@
module.exports = {
setupFiles: [
'<rootDir>/test/setup-tests.js'
],
setupFilesAfterEnv: [
'<rootDir>/scripts/setup-test-framework.js'
],
transform: {
'^.+\\.js$': '<rootDir>/test/transform-babel-jest.js',
},
/* It solves css/less/scss import issues.
You might have similar issues with different file extensions (e.g. md).
Just search for "<file type> jest loader"
*/
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/test/transform-file.js'
},
};

View File

@ -0,0 +1,125 @@
{
"name": "asc-web-common",
"version": "1.0.1",
"description": "Ascensio System SIA common components and solutions library",
"license": "AGPL-3.0",
"files": [
"dist",
"README.md",
"LICENSE",
"package.json"
],
"main": "dist/asc-web-common.js",
"module": "dist/asc-web-common.esm.js",
"jsnext:main": "dist/asc-web-common.es.js",
"scripts": {
"build": "rimraf dist && cross-env NODE_ENV=production rollup -c",
"build:storybook": "build-storybook -c .storybook -o storybook-static -s public",
"bump": "yarn version --no-git-tag-version --patch && git add ./package.json && git commit -m \"web: common: bump version\"",
"lint": "eslint .",
"prepare": "yarn run build",
"start": "cross-env NODE_ENV=development rollup -c -w",
"storybook": "start-storybook -p 6007 -s ./public",
"test": "jest",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch"
},
"dependencies": {
"axios": "^0.19.0",
"history": "4.9.0",
"lodash": "4.17.15",
"lodash-es": "4.17.15",
"moment": "^2.24.0",
"universal-cookie": "^4.0.2"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-export-default-from": "^7.5.2",
"@babel/plugin-proposal-export-namespace-from": "^7.5.2",
"@babel/plugin-transform-runtime": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@emotion/babel-preset-css-prop": "^10.0.17",
"@storybook/addon-a11y": "^5.1.11",
"@storybook/addon-actions": "^5.2.5",
"@storybook/addon-console": "^1.2.1",
"@storybook/addon-knobs": "^5.2.5",
"@storybook/addon-links": "^5.2.5",
"@storybook/addon-options": "^5.2.5",
"@storybook/addon-storysource": "^5.2.5",
"@storybook/addon-viewport": "^5.2.5",
"@storybook/addons": "^5.2.5",
"@storybook/react": "^5.2.5",
"@svgr/rollup": "^4.3.3",
"@svgr/webpack": "^4.3.2",
"@testing-library/react": "^8.0.8",
"@types/jest": "^24.0.17",
"asc-web-components": "file:../../packages/asc-web-components",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.8.0",
"babel-loader": "^8.0.6",
"babel-plugin-inline-react-svg": "^1.1.0",
"babel-plugin-transform-dynamic-import": "^2.1.0",
"babel-plugin-transform-rename-import": "^2.3.0",
"bootstrap": "^4.3.1",
"cross-env": "^5.2.0",
"css-loader": "^3.2.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"eslint": "^6.3.0",
"eslint-plugin-react": "^7.14.3",
"i18next": "17.0.12",
"jest": "^24.8.0",
"jest-enzyme": "^7.1.0",
"jest-junit": "^8.0.0",
"postcss": "^7.0.17",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-i18next": "10.12.2",
"react-redux": "7.1.1",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-values": "^0.3.3",
"reactstrap": "8.0.1",
"rollup": "^1.21.1",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-cleanup": "^3.1.1",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.1.0",
"rollup-plugin-generate-package-json": "^3.1.3",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-postcss": "^2.0.3",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-url": "^2.2.2",
"storybook-readme": "^5.0.8",
"styled-components": "^4.3.2",
"svg-inline-loader": "^0.8.0"
},
"peerDependencies": {
"asc-web-components": "file:../../packages/asc-web-components",
"bootstrap": "^4.3.1",
"i18next": "17.0.12",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-i18next": "10.12.2",
"react-redux": "7.1.1",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-values": "^0.3.3",
"reactstrap": "8.0.1",
"styled-components": "^4.3.2"
},
"resolutions": {
"js-yaml": "3.13.1"
},
"engines": {
"node": ">=8",
"npm": ">=5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>ASC Web Common Storybook</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,34 @@
<svg width="142" height="23" viewBox="0 0 142 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M31 12.0026C31 9.62504 31.6829 7.83068 33.0943 6.66435C34.4602 5.45316 36.0993 4.87 37.966 4.87C39.8326 4.87 41.4262 5.45316 42.792 6.66435C44.1579 7.87554 44.8409 9.62504 44.8409 12.0474C44.8409 14.4249 44.1579 16.2193 42.792 17.3856C41.4262 18.5968 39.7871 19.18 37.966 19.18C36.0993 19.18 34.5057 18.5968 33.0943 17.3856C31.6829 16.1744 31 14.3801 31 12.0026ZM34.0049 12.0026C34.0049 13.6623 34.3236 14.8287 34.9155 15.5913C35.5529 16.3539 36.2358 16.8473 36.9643 17.0268C37.1464 17.0716 37.283 17.1165 37.4651 17.1165C37.6017 17.1165 37.7838 17.1613 37.9204 17.1613C38.1025 17.1613 38.2391 17.1613 38.4212 17.1165C38.6034 17.1165 38.74 17.0716 38.9221 17.0268C39.6505 16.8473 40.3335 16.3539 40.9253 15.5913C41.5172 14.8287 41.8359 13.6175 41.8359 12.0474C41.8359 10.4774 41.5172 9.26617 40.9253 8.50357C40.3335 7.74097 39.6505 7.24752 38.9221 7.06808C38.74 7.02322 38.5578 6.97836 38.4212 6.97836C38.2391 6.97836 38.1025 6.93351 37.9204 6.93351C37.7383 6.93351 37.6017 6.9335 37.4651 6.97836C37.3285 6.97836 37.1464 7.02322 36.9643 7.06808C36.2358 7.24752 35.5529 7.74097 34.9155 8.50357C34.3236 9.22131 34.0049 10.3876 34.0049 12.0026Z" fill="white"/>
<path d="M46.3433 5.00449H50.0767L54.9938 13.8417L55.7223 15.7258H55.7678L55.7223 13.2585V5.00449H58.5906V19.0005H54.8573L49.9401 9.84925L49.2116 8.27919H49.1661L49.2116 10.7464V19.0005H46.3433V5.00449Z" fill="white"/>
<path d="M61.0948 5.00449H63.9631V16.6229H69.6087V19.0005H61.0948V5.00449Z" fill="white"/>
<path d="M67.8331 5.00449H71.1567L74.0705 9.93897L74.5258 10.881H74.6169L75.0722 9.93897L78.0316 5.00449H81.082L75.9372 13.3034V19.0005H73.0689V13.3034L67.8331 5.00449Z" fill="white"/>
<path d="M81.1731 12.0026C81.1731 9.62504 81.856 7.83068 83.2674 6.66435C84.6333 5.45316 86.2723 4.87 88.139 4.87C90.0057 4.87 91.5993 5.45316 92.9651 6.66435C94.331 7.87554 95.0139 9.62504 95.0139 12.0474C95.0139 14.4249 94.331 16.2193 92.9651 17.3856C91.5993 18.5968 89.9602 19.18 88.139 19.18C86.2723 19.18 84.6788 18.5968 83.2674 17.3856C81.9016 16.1744 81.1731 14.3801 81.1731 12.0026ZM84.178 12.0026C84.178 13.6623 84.4967 14.8287 85.0886 15.5913C85.726 16.3539 86.3634 16.8473 87.1374 17.0268C87.3195 17.0716 87.5016 17.1165 87.6382 17.1165C87.7748 17.1165 87.9569 17.1613 88.0935 17.1613C88.2756 17.1613 88.4122 17.1613 88.5943 17.1165C88.7764 17.1165 88.913 17.0716 89.0952 17.0268C89.8236 16.8473 90.5066 16.3539 91.0984 15.5913C91.6903 14.8287 92.009 13.6175 92.009 12.0474C92.009 10.4774 91.6903 9.26617 91.0984 8.50357C90.5066 7.74097 89.8236 7.24752 89.0952 7.06808C88.913 7.02322 88.7309 6.97836 88.5943 6.97836C88.4122 6.97836 88.2756 6.93351 88.0935 6.93351C87.9114 6.93351 87.7748 6.9335 87.6382 6.97836C87.5016 6.97836 87.3195 7.02322 87.1374 7.06808C86.4089 7.24752 85.726 7.74097 85.0886 8.50357C84.4967 9.22131 84.178 10.3876 84.178 12.0026Z" fill="#D7E4EA"/>
<path d="M96.5619 5.00449H104.484V7.38201H99.4303V10.7464H104.256V13.1239H99.4303V19.0005H96.5619V5.00449Z" fill="#D7E4EA"/>
<path d="M106.169 5.00449H114.091V7.38201H109.037V10.7464H113.863V13.1239H109.037V19.0005H106.169V5.00449Z" fill="#D7E4EA"/>
<path d="M115.775 19.0005V5.00449H118.644V19.0005H115.775Z" fill="#D7E4EA"/>
<path d="M131.665 5.3185V7.74088C131.164 7.56144 130.663 7.42687 130.117 7.33715C129.571 7.24743 128.979 7.20257 128.341 7.20257C126.839 7.20257 125.701 7.65116 124.881 8.5932C124.062 9.49038 123.652 10.6567 123.652 12.0473C123.652 13.3931 124.016 14.5146 124.79 15.4118C125.564 16.3089 126.657 16.8024 128.068 16.8024C128.569 16.8024 129.07 16.7575 129.662 16.7127C130.253 16.6229 130.845 16.4884 131.483 16.2192L131.665 18.5967C131.574 18.6416 131.437 18.6865 131.301 18.7313C131.119 18.7762 130.936 18.821 130.709 18.8659C130.345 18.9556 129.889 19.0005 129.343 19.0902C128.797 19.135 128.25 19.1799 127.658 19.1799C127.567 19.1799 127.476 19.1799 127.431 19.1799C127.34 19.1799 127.249 19.1799 127.203 19.1799C125.564 19.0902 124.062 18.4622 122.696 17.3855C121.33 16.2641 120.647 14.5146 120.647 12.1819C120.647 9.89411 121.33 8.09975 122.65 6.8437C123.97 5.58765 125.792 4.95963 128.023 4.95963C128.614 4.95963 129.161 4.95963 129.616 5.00448C130.117 5.04934 130.572 5.13906 131.073 5.22878C131.164 5.27364 131.301 5.27364 131.392 5.3185C131.437 5.27364 131.528 5.3185 131.665 5.3185Z" fill="#D7E4EA"/>
<path d="M133.486 5.00449H142V7.20257H136.4V10.7016H141.454V12.8997H136.4V16.8024H142V19.0005H133.486V5.00449Z" fill="#D7E4EA"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9389 22.718L0.657327 17.9809C-0.218514 17.5673 -0.218514 16.9282 0.657327 16.5522L4.23685 14.898L10.9009 17.9809C11.7767 18.3944 13.1857 18.3944 14.0234 17.9809L20.6874 14.898L24.267 16.5522C25.1428 16.9658 25.1428 17.6049 24.267 17.9809L13.9853 22.718C13.1857 23.094 11.7767 23.094 10.9389 22.718Z" fill="url(#paint0_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9389 16.8906L0.657327 12.1535C-0.218514 11.7399 -0.218514 11.1008 0.657327 10.7248L4.16069 9.10815L10.9389 12.2287C11.8148 12.6422 13.2237 12.6422 14.0615 12.2287L20.8398 9.10815L24.3431 10.7248C25.219 11.1384 25.219 11.7775 24.3431 12.1535L14.0615 16.8906C13.1857 17.3042 11.7767 17.3042 10.9389 16.8906Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9385 11.2131L0.656881 6.47597C-0.21896 6.06241 -0.21896 5.42327 0.656881 5.04731L10.9385 0.31017C11.8143 -0.10339 13.2233 -0.10339 14.0611 0.31017L24.3427 5.04731C25.2185 5.46087 25.2185 6.10001 24.3427 6.47597L14.0611 11.2131C13.1852 11.5891 11.7763 11.5891 10.9385 11.2131Z" fill="url(#paint2_linear)"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="12.4914" y1="27.2083" x2="12.4914" y2="9.91349" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCC2B1"/>
<stop offset="0.8848" stop-color="#D9420B"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="12.4914" y1="19.7202" x2="12.4914" y2="8.34589" gradientUnits="userSpaceOnUse">
<stop stop-color="#DEEDC9"/>
<stop offset="0.6606" stop-color="#8BBA25"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="12.4909" y1="15.114" x2="12.4909" y2="-0.363895" gradientUnits="userSpaceOnUse">
<stop stop-color="#C2EBFA"/>
<stop offset="1" stop-color="#26A8DE"/>
</linearGradient>
<clipPath id="clip0">
<rect width="142" height="23" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,15 @@
{
"short_name": "ASC Web Common Storybook",
"name": "ASC Web Common Storybook",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show More