Merge remote-tracking branch 'remotes/origin/master' into feature/grid-component

This commit is contained in:
Andrey Savihin 2019-12-10 11:51:07 +03:00
commit 581c32e672
304 changed files with 12121 additions and 7906 deletions

View File

@ -63,7 +63,7 @@ RUN apt-get -y update && \
jq \
git \
yarn \
dotnet-sdk-3.0 \
dotnet-sdk-3.1 \
supervisor \
mysql-client \
mysql-server
@ -74,6 +74,14 @@ RUN cd /app/onlyoffice/src/ && \
yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build/ASC.Web.Components.log && \
npm run build --prefix web/ASC.Web.Components && \
yarn pack --cwd web/ASC.Web.Components
RUN cd /app/onlyoffice/src/ && \
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
yarn remove asc-web-components --cwd web/ASC.Web.Common --peer && \
yarn add file:../../$component --cwd web/ASC.Web.Common --cache-folder ../../yarn --peer && \
yarn install --cwd web/ASC.Web.Common --frozen-lockfile > build/ASC.Web.Common.log && \
npm run build --prefix web/ASC.Web.Common && \
yarn pack --cwd web/ASC.Web.Common
RUN cd /app/onlyoffice/src/ && \
npm run build:storybook --prefix web/ASC.Web.Components && \
@ -82,7 +90,10 @@ RUN cd /app/onlyoffice/src/ && \
RUN cd /app/onlyoffice/src/ && \
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
yarn remove asc-web-components asc-web-common --cwd web/ASC.Web.Client && \
yarn add ../../$component --cwd web/ASC.Web.Client --cache-folder ../../yarn && \
yarn add ../../$common --cwd web/ASC.Web.Client --cache-folder ../../yarn && \
yarn install --cwd web/ASC.Web.Client --frozen-lockfile || (cd web/ASC.Web.Client && npm i && cd ../../) && \
npm run build --prefix web/ASC.Web.Client && \
rm -rf /var/www/studio/client/* && \
@ -91,7 +102,10 @@ RUN cd /app/onlyoffice/src/ && \
RUN cd /app/onlyoffice/src/ && \
component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) && \
common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) && \
yarn remove asc-web-components asc-web-common --cwd products/ASC.People/Client && \
yarn add ../../../$component --cwd products/ASC.People/Client --cache-folder ../../../yarn && \
yarn add ../../../$common --cwd products/ASC.People/Client --cache-folder ../../../yarn && \
yarn install --cwd products/ASC.People/Client --frozen-lockfile || (cd products/ASC.People/Client && npm i && cd ../../../) && \
npm run build --prefix products/ASC.People/Client && \
mkdir -p /var/www/products/ASC.People/client && \

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Library</OutputType>
<StartupObject />
@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
</ItemGroup>
<ItemGroup>

View File

@ -32,8 +32,8 @@
<PackageReference Include="ARSoft.Tools.NetStandard.DXSdata" Version="1.0.0" />
<PackageReference Include="Autofac" Version="4.9.4" />
<PackageReference Include="Autofac.Configuration" Version="4.1.0" />
<PackageReference Include="Confluent.Kafka" Version="1.2.1" />
<PackageReference Include="Google.Protobuf" Version="3.10.1" />
<PackageReference Include="Confluent.Kafka" Version="1.3.0" />
<PackageReference Include="Google.Protobuf" Version="3.11.1" />
<PackageReference Include="Grpc" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0">
<PrivateAssets>all</PrivateAssets>
@ -42,11 +42,11 @@
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.0.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="3.1.0" />
<!-- <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -46,10 +46,10 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="AWSSDK.CloudFront" Version="3.3.101.64" />
<PackageReference Include="AWSSDK.Core" Version="3.3.103.62" />
<PackageReference Include="AWSSDK.S3" Version="3.3.106.4" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.3.101.68" />
<PackageReference Include="AWSSDK.CloudFront" Version="3.3.101.74" />
<PackageReference Include="AWSSDK.Core" Version="3.3.104" />
<PackageReference Include="AWSSDK.S3" Version="3.3.109" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.3.101.78" />
<PackageReference Include="Grpc.Tools" Version="2.25.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Library</OutputType>
<StartupObject />

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Library</OutputType>
<StartupObject />

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Library</OutputType>
<StartupObject />

View File

@ -36,6 +36,7 @@ using ASC.FederatedLogin.Profile;
using ASC.Security.Cryptography;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.FederatedLogin
{
@ -65,26 +66,44 @@ namespace ASC.FederatedLogin
return profiles;
}
}
public class AccountLinker
public class ConfigureAccountLinker : IConfigureNamedOptions<AccountLinker>
{
private readonly string dbid;
public Signature Signature { get; }
public InstanceCrypto InstanceCrypto { get; }
public DbOptionsManager DbOptions { get; }
public AccountLinkerStorage AccountLinkerStorage { get; }
public AccountLinker(string dbid, Signature signature, InstanceCrypto instanceCrypto, DbOptionsManager dbOptions, AccountLinkerStorage accountLinkerStorage)
public ConfigureAccountLinker(Signature signature, InstanceCrypto instanceCrypto, DbOptionsManager dbOptions, AccountLinkerStorage accountLinkerStorage)
{
this.dbid = dbid;
Signature = signature;
InstanceCrypto = instanceCrypto;
DbOptions = dbOptions;
AccountLinkerStorage = accountLinkerStorage;
}
public void Configure(string name, AccountLinker options)
{
options.DbId = name;
options.AccountLinkerStorage = AccountLinkerStorage;
options.DbOptions = DbOptions;
options.InstanceCrypto = InstanceCrypto;
options.Signature = Signature;
}
public void Configure(AccountLinker options)
{
Configure("default", options);
}
}
public class AccountLinker
{
public string DbId { get; set; }
public Signature Signature { get; set; }
public InstanceCrypto InstanceCrypto { get; set; }
public DbOptionsManager DbOptions { get; set; }
public AccountLinkerStorage AccountLinkerStorage { get; set; }
public IEnumerable<string> GetLinkedObjects(string id, string provider)
{
return GetLinkedObjects(new LoginProfile(Signature, InstanceCrypto) { Id = id, Provider = provider });
@ -97,7 +116,7 @@ namespace ASC.FederatedLogin
public IEnumerable<string> GetLinkedObjectsByHashId(string hashid)
{
var db = DbOptions.Get(dbid);
var db = DbOptions.Get(DbId);
var query = new SqlQuery("account_links")
.Select("id").Where("uid", hashid).Where(!Exp.Eq("provider", string.Empty));
return db.ExecuteList(query).ConvertAll(x => (string)x[0]);
@ -116,7 +135,7 @@ namespace ASC.FederatedLogin
private List<LoginProfile> GetLinkedProfilesFromDB(string obj)
{
//Retrieve by uinque id
var db = DbOptions.Get(dbid);
var db = DbOptions.Get(DbId);
var query = new SqlQuery("account_links")
.Select("profile").Where("id", obj);
return db.ExecuteList(query).ConvertAll(x => LoginProfile.CreateFromSerializedString(Signature, InstanceCrypto, (string)x[0]));
@ -124,7 +143,7 @@ namespace ASC.FederatedLogin
public void AddLink(string obj, LoginProfile profile)
{
var db = DbOptions.Get(dbid);
var db = DbOptions.Get(DbId);
db.ExecuteScalar<int>(
new SqlInsert("account_links", true)
.InColumnValue("id", obj)
@ -158,7 +177,7 @@ namespace ASC.FederatedLogin
if (!string.IsNullOrEmpty(provider)) sql.Where("provider", provider);
if (!string.IsNullOrEmpty(hashId)) sql.Where("uid", hashId);
var db = DbOptions.Get(dbid);
var db = DbOptions.Get(DbId);
db.ExecuteScalar<int>(sql);
AccountLinkerStorage.RemoveFromCache(obj);
@ -175,4 +194,19 @@ namespace ASC.FederatedLogin
return services;
}
}
public static class AccountLinkerExtension
{
public static IServiceCollection AddAccountLinker(this IServiceCollection services)
{
services.TryAddScoped<AccountLinker>();
services.TryAddScoped<IConfigureOptions<AccountLinker>, ConfigureAccountLinker>();
return services
.AddSignatureService()
.AddInstanceCryptoService()
.AddDbManagerService()
.AddAccountLinkerStorageService();
}
}
}

View File

@ -27,13 +27,13 @@
using System;
using System.Linq;
using System.Security;
using ASC.Common.Data;
using ASC.Common.Utils;
using ASC.Core;
using ASC.Core.Users;
using ASC.FederatedLogin;
using ASC.FederatedLogin.Profile;
using ASC.Security.Cryptography;
using Microsoft.Extensions.Options;
using SecurityContext = ASC.Core.SecurityContext;
namespace ASC.Web.Studio.Core
@ -47,8 +47,7 @@ namespace ASC.Web.Studio.Core
SecurityContext securityContext,
Signature signature,
InstanceCrypto instanceCrypto,
DbOptionsManager dbOptions,
AccountLinkerStorage accountLinkerStorage)
IOptionsSnapshot<AccountLinker> snapshot)
{
var tenant = tenantManager.GetCurrentTenant();
var user = userManager.GetUsers(securityContext.CurrentAccount.ID);
@ -59,7 +58,7 @@ namespace ASC.Web.Studio.Core
Provider = ProviderConstants.Blockchain,
};
var linker = new AccountLinker("webstudio", signature, instanceCrypto, dbOptions, accountLinkerStorage);
var linker = snapshot.Get("webstudio");
if (string.IsNullOrEmpty(account))
{
linker.RemoveLink(user.ID.ToString(), loginProfile);
@ -72,14 +71,14 @@ namespace ASC.Web.Studio.Core
}
public static string GetAddress(SecurityContext securityContext, Signature signature, InstanceCrypto instanceCrypto, DbOptionsManager dbOptions, AccountLinkerStorage accountLinkerStorage)
public static string GetAddress(SecurityContext securityContext, IOptionsSnapshot<AccountLinker> snapshot)
{
return GetAddress(securityContext.CurrentAccount.ID, signature, instanceCrypto, dbOptions, accountLinkerStorage);
return GetAddress(securityContext.CurrentAccount.ID, snapshot);
}
public static string GetAddress(Guid userId, Signature signature, InstanceCrypto instanceCrypto, DbOptionsManager dbOptions, AccountLinkerStorage accountLinkerStorage)
public static string GetAddress(Guid userId, IOptionsSnapshot<AccountLinker> snapshot)
{
var linker = new AccountLinker("webstudio", signature, instanceCrypto, dbOptions, accountLinkerStorage);
var linker = snapshot.Get("webstudio");
var profile = linker.GetLinkedProfiles(userId.ToString(), ProviderConstants.Blockchain).FirstOrDefault();
if (profile == null) return null;

View File

@ -27,11 +27,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Utils;
using ASC.FederatedLogin.Profile;
using ASC.Security.Cryptography;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.FederatedLogin
{
@ -54,12 +53,12 @@ namespace ASC.FederatedLogin
}
public MultiRegionAccountLinker(string databaseId, Signature signature, InstanceCrypto instanceCrypto, DbOptionsManager dbOptions, IConfiguration configuration, AccountLinkerStorage accountLinkerStorage)
public MultiRegionAccountLinker(string databaseId, IConfiguration configuration, IOptionsSnapshot<AccountLinker> snapshot)
{
foreach (var connection in configuration.GetConnectionStrings())
{
if (connection.Name.StartsWith(databaseId))
_accountLinkers.Add(connection.Name, new AccountLinker(connection.Name, signature, instanceCrypto, dbOptions, accountLinkerStorage));
_accountLinkers.Add(connection.Name, snapshot.Get(connection.Name));
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<ProductVersion>9.0.30729</ProductVersion>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyTitle>ASC.Notify.Textile</AssemblyTitle>
<Company>Ascensio System SIA</Company>
<Product>ASC.Notify.Textile</Product>

View File

@ -13,9 +13,9 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
</ItemGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
</PropertyGroup>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

File diff suppressed because it is too large Load Diff

View File

@ -4,44 +4,41 @@
"private": true,
"homepage": "/products/people",
"dependencies": {
"asc-web-components": "file:../../../packages/asc-web-components",
"asc-web-common": "file:../../../packages/asc-web-common",
"asc-web-components": "file:../../../packages/asc-web-components",
"axios": "^0.19.0",
"bootstrap": "4.3.1",
"connected-react-router": "6.5.2",
"connected-react-router": "6.6.1",
"copy-to-clipboard": "^3.2.0",
"history": "4.9.0",
"i18next": "17.0.12",
"i18next-browser-languagedetector": "3.0.3",
"i18next-xhr-backend": "3.1.2",
"jquery": "3.4.1",
"history": "4.10.1",
"i18next": "19.0.1",
"i18next-browser-languagedetector": "4.0.1",
"i18next-xhr-backend": "3.2.2",
"lodash": "4.17.15",
"lodash-es": "4.17.15",
"merge": "^1.2.1",
"node-sass": "^4.12.0",
"oidc-client": "^1.9.0",
"node-sass": "^4.13.0",
"oidc-client": "^1.9.1",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-device-detect": "^1.7.5",
"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": "^16.12.0",
"react-device-detect": "^1.11.14",
"react-dom": "^16.12.0",
"react-i18next": "11.2.5",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"reactstrap": "8.0.1",
"redux": "4.0.4",
"redux-thunk": "2.3.0",
"styled-components": "^4.3.2"
"styled-components": "^4.4.1"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.4",
"cross-env": "^5.2.0",
"react-app-rewired": "^2.1.3",
"react-scripts": "3.1.1",
"copy-webpack-plugin": "^5.0.5",
"cross-env": "^6.0.3",
"react-app-rewired": "^2.1.5",
"react-scripts": "3.3.0",
"redux-devtools-extension": "^2.13.8",
"rimraf": "2.6.3"
"rimraf": "3.0.0"
},
"eslintConfig": {
"extends": "react-app"

View File

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

View File

@ -93,11 +93,8 @@ class PureInviteDialog extends React.Component {
});
};
componentDidUpdate(prevProps) {
console.log("invitelink did UPDATE");
if (this.props.visible && !prevProps.visible) {
componentDidMount() {
this.onCopyLinkToClipboard();
}
}
onClickToCloseButton = () =>
@ -116,12 +113,12 @@ class PureInviteDialog extends React.Component {
headerContent={t("InviteLinkTitle")}
bodyContent={
<>
<Text.Body className="margin-text" as="p">
<Text className="margin-text" as="p">
{t("HelpAnswerLinkInviteSettings")}
</Text.Body>
<Text.Body className="margin-text" as="p">
</Text>
<Text className="margin-text" as="p">
{t("InviteLinkValidInterval", { count: 7 })}
</Text.Body>
</Text>
<div className="flex">
<div>
<Link
@ -187,8 +184,8 @@ class PureInviteDialog extends React.Component {
const mapStateToProps = state => {
return {
settings: state.auth.settings.hasShortenService,
userInvitationLink: state.auth.settings.inviteLinks.userLink,
guestInvitationLink: state.auth.settings.inviteLinks.guestLink
userInvitationLink: state.portal.inviteLinks.userLink,
guestInvitationLink: state.portal.inviteLinks.guestLink
};
};

View File

@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import PropTypes from "prop-types";
import { IconButton, Text } from "asc-web-components";
import { IconButton, Header } from "asc-web-components";
import { withTranslation } from "react-i18next";
import { department } from "./../../../../../helpers/customNames";
import { resetGroup } from "../../../../../store/group/actions";
@ -36,7 +36,7 @@ class SectionHeaderContent extends React.Component {
size="16"
onClick={this.onBackClick}
/>
<Text.ContentHeader style={textStyle}>{headerText}</Text.ContentHeader>
<Header type="content" style={textStyle}>{headerText}</Header>
</div>
);
}

View File

@ -72,13 +72,13 @@ class SectionBodyContent extends React.PureComponent {
visible: true,
header: "Password change",
body: (
<Text.Body>
<Text>
Send the password change instructions to the{" "}
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
{email}
</Link>{" "}
email address
</Text.Body>
</Text>
),
buttons: [
<Button
@ -92,10 +92,10 @@ class SectionBodyContent extends React.PureComponent {
sendInstructionsToChangePassword(email)
.then(() =>
toastr.success(
<Text.Body>
<Text>
The password change instructions have been sent to the{" "}
<b>{email}</b> email address
</Text.Body>
</Text>
)
)
.catch(error => toastr.error(error))
@ -133,9 +133,9 @@ class SectionBodyContent extends React.PureComponent {
this.setState({ newEmail: e.target.value });
}}
/>
<Text.Body style={{ marginTop: "16px" }}>
<Text style={{ marginTop: "16px" }}>
The activation instructions will be sent to the entered email
</Text.Body>
</Text>
</>
),
buttons: [
@ -200,18 +200,18 @@ class SectionBodyContent extends React.PureComponent {
header: "Confirmation",
body: (
<>
<Text.Body>
<Text>
User <b>{user.displayName}</b> will be deleted.
</Text.Body>
<Text.Body>Note: this action cannot be undone.</Text.Body>
<Text.Body color="#c30" fontSize="18" style={{ margin: "20px 0" }}>
</Text>
<Text>Note: this action cannot be undone.</Text>
<Text color="#c30" fontSize="18" style={{ margin: "20px 0" }}>
Warning!
</Text.Body>
<Text.Body>
</Text>
<Text>
User personal documents which are available to others will be
deleted. To avoid this, you must start the data reassign process
before deleting.
</Text.Body>
</Text>
</>
),
buttons: [
@ -263,12 +263,12 @@ class SectionBodyContent extends React.PureComponent {
visible: true,
header: "Delete profile dialog",
body: (
<Text.Body>
<Text>
Send the profile deletion instructions to the email address{" "}
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
{email}
</Link>
</Text.Body>
</Text>
),
buttons: [
<Button
@ -282,10 +282,10 @@ class SectionBodyContent extends React.PureComponent {
sendInstructionsToDelete()
.then(() =>
toastr.success(
<Text.Body>
<Text>
Instructions to delete your profile has been sent to{" "}
<b>{email}</b> email address
</Text.Body>
</Text>
)
)
.catch(error => toastr.error(error))
@ -312,10 +312,10 @@ class SectionBodyContent extends React.PureComponent {
resendUserInvites([user.id])
.then(() =>
toastr.success(
<Text.Body>
<Text>
The email activation instructions have been sent to the{" "}
<b>{user.email}</b> email address
</Text.Body>
</Text>
)
)
.catch(error => toastr.error(error))

View File

@ -87,7 +87,7 @@ const UserContent = ({ user, history, settings }) => {
</>
{title
?
<Text.Body
<Text
style={headDepartmentStyle}
as="span"
color={sideInfoColor}
@ -96,7 +96,7 @@ const UserContent = ({ user, history, settings }) => {
truncate={true}
>
{title}
</Text.Body>
</Text>
: <div style={headDepartmentStyle}></div>
}
{groups}

View File

@ -4,7 +4,7 @@ import { withRouter } from "react-router";
import {
GroupButtonsMenu,
DropDownItem,
Text,
Header,
toastr,
ContextMenuButton,
IconButton
@ -210,7 +210,7 @@ const SectionHeaderContent = props => {
<div className="header-container">
{group ? (
<>
<Text.ContentHeader truncate={true}>{group.name}</Text.ContentHeader>
<Header type="content" truncate={true}>{group.name}</Header>
{isAdmin && (
<ContextMenuButton
directionX="right"
@ -225,7 +225,7 @@ const SectionHeaderContent = props => {
</>
) : (
<>
<Text.ContentHeader>Departments</Text.ContentHeader>
<Header type="content">Departments</Header>
{isAdmin && (
<IconButton
className="add-group-button"

View File

@ -205,15 +205,15 @@ class SectionBodyContent extends React.Component {
return (
<>
<Text.Body fontSize={18} >
<Text fontSize={18} >
Functionality at development stage.
</Text.Body>
</Text>
<br />
<Text.Body fontSize={14} >
<Text fontSize={14} >
Files are formatted according to CSV RFC rules. <br />
Column Order: FirstName, LastName, Email. <br />
Comma delimiter, strings in unix format. <br />
</Text.Body>
</Text>
<SelectSourceWrapper>
<StyledFileInput>
<Button size='big' primary={true} scale={true} label="Upload CSV" isDisabled={true} />
@ -223,9 +223,9 @@ class SectionBodyContent extends React.Component {
</StyledFileInput>
</SelectSourceWrapper>
<br />
<Text.Body fontSize={14} >
<Text fontSize={14} >
Ready for import: {`${splittedLines.length} of ${splittedLines.length}`}
</Text.Body>
</Text>
<div style={{ position: 'relative', width: '100%', height: '30px' }}>
<StyledProgress completed={completion} />
</div>

View File

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { Text, IconButton } from "asc-web-components";
import { Header, IconButton } from "asc-web-components";
import { withRouter } from "react-router";
import { useTranslation } from 'react-i18next';
@ -28,9 +28,9 @@ const SectionHeaderContent = props => {
onClick={() => history.push(settings.homepage)}
/>
</div>
<Text.ContentHeader truncate={true} style={textStyle}>
<Header type="content" truncate={true} style={textStyle}>
Add users to the portal
</Text.ContentHeader>
</Header>
</div>
);
};

View File

@ -122,7 +122,7 @@ class ProfileInfo extends React.PureComponent {
visible: true,
header: "Change email",
body: (
<Text.Body>
<Text>
<span style={{ display: "block", marginBottom: "8px" }}>The activation instructions will be sent to the entered email</span>
<TextInput
id="new-email"
@ -132,7 +132,7 @@ class ProfileInfo extends React.PureComponent {
onChange={this.onEmailChange}
hasError={hasError}
/>
</Text.Body>
</Text>
),
buttons: [
<Button
@ -207,7 +207,7 @@ class ProfileInfo extends React.PureComponent {
const formatedDepartments = department && getFormattedDepartments(groups);
const supportEmail = "documentation@onlyoffice.com";
const tooltipLanguage =
<Text.Body fontSize={13}>
<Text fontSize={13}>
<Trans i18nKey="NotFoundLanguage" i18n={i18n}>
"In case you cannot find your language in the list of the
available ones, feel free to write to us at
@ -218,7 +218,7 @@ class ProfileInfo extends React.PureComponent {
</Trans>
{" "}
<Link isHovered={true} href="https://helpcenter.onlyoffice.com/ru/guides/become-translator.aspx">{t("LearnMore")}</Link>
</Text.Body>
</Text>
return (
<InfoContainer>

View File

@ -108,35 +108,35 @@ class SectionBodyContent extends React.PureComponent {
{(isSelf && false) && (
<ToggleWrapper isSelf={true} >
<ToggleContent label={t('Subscriptions')} isOpen={true} >
<Text.Body as="span">
<Text as="span">
<Button
size="big"
label={t('EditSubscriptionsBtn')}
primary={true}
onClick={this.onEditSubscriptionsClick}
/>
</Text.Body>
</Text>
</ToggleContent>
</ToggleWrapper>
)}
{profile.notes && (
<ToggleWrapper>
<ToggleContent label={t('Comments')} isOpen={true} >
<Text.Body as="span">{profile.notes}</Text.Body>
<Text as="span">{profile.notes}</Text>
</ToggleContent>
</ToggleWrapper>
)}
{profile.contacts && (
<ToggleWrapper isContacts={true} >
<ToggleContent label={t('ContactInformation')} isOpen={true} >
<Text.Body as="span">{infoContacts}</Text.Body>
<Text as="span">{infoContacts}</Text>
</ToggleContent>
</ToggleWrapper>
)}
{socialContacts && (
<ToggleWrapper isContacts={true} >
<ToggleContent label={t('SocialProfiles')} isOpen={true} >
<Text.Body as="span">{socialContacts}</Text.Body>
<Text as="span">{socialContacts}</Text>
</ToggleContent>
</ToggleWrapper>
)}

View File

@ -10,7 +10,8 @@ import {
TextInput,
Button,
ModalDialog,
AvatarEditor
AvatarEditor,
Header
} from "asc-web-components";
import { withRouter } from "react-router";
import {
@ -22,7 +23,7 @@ import {
updateUserStatus,
fetchPeople
} from "../../../../../store/people/actions";
import { fetchProfile } from "../../../../../store/profile/actions";
import { fetchProfile, getUserPhoto } from "../../../../../store/profile/actions";
import styled from "styled-components";
import { store, api, constants } from "asc-web-common";
const { isAdmin, isMe } = store.auth.selectors;
@ -43,7 +44,7 @@ const wrapperStyle = {
alignItems: "center"
};
const Header = styled(Text.ContentHeader)`
const HeaderContainer = styled(Header)`
margin-left: 16px;
margin-right: 16px;
max-width: calc(100vw - 430px);
@ -81,9 +82,7 @@ class SectionHeaderContent extends React.PureComponent {
},
avatar: {
tmpFile: "",
image: profile.avatarDefault
? "data:image/png;base64," + profile.avatarDefault
: null,
image: null,
defaultWidth: 0,
defaultHeight: 0
}
@ -93,24 +92,31 @@ class SectionHeaderContent extends React.PureComponent {
};
openAvatarEditor = () => {
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() {
_this.setState({
avatar: {
defaultWidth: img.width,
defaultHeight: img.height
}
});
};
img.src = avatarDefault;
}
this.setState({
visibleAvatarEditor: true
getUserPhoto(this.state.profile.id).then(userPhotoData => {
if(userPhotoData.original){
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2],
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
},
visibleAvatarEditor: true
});
}else{
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
defaultWidth: 0,
defaultHeight: 0,
image: null
},
visibleAvatarEditor: true
});
}
}
});
};
@ -192,7 +198,7 @@ class SectionHeaderContent extends React.PureComponent {
visible: true,
header: "Change email",
body: (
<Text.Body>
<Text>
<span style={{ display: "block", marginBottom: "8px" }}>
The activation instructions will be sent to the entered email
</span>
@ -204,7 +210,7 @@ class SectionHeaderContent extends React.PureComponent {
onChange={this.onEmailChange}
hasError={hasError}
/>
</Text.Body>
</Text>
),
buttons: [
<Button
@ -238,13 +244,13 @@ class SectionHeaderContent extends React.PureComponent {
visible: true,
header: "Change password",
body: (
<Text.Body>
<Text>
Send the password change instructions to the{" "}
<a href={`mailto:${this.state.profile.email}`}>
{this.state.profile.email}
</a>{" "}
email address
</Text.Body>
</Text>
),
buttons: [
<Button
@ -308,18 +314,18 @@ class SectionHeaderContent extends React.PureComponent {
header: "Confirmation",
body: (
<>
<Text.Body>
<Text>
User <b>{user.displayName}</b> will be deleted.
</Text.Body>
<Text.Body>Note: this action cannot be undone.</Text.Body>
<Text.Body color="#c30" fontSize="18" style={{ margin: "20px 0" }}>
</Text>
<Text>Note: this action cannot be undone.</Text>
<Text color="#c30" fontSize="18" style={{ margin: "20px 0" }}>
Warning!
</Text.Body>
<Text.Body>
</Text>
<Text>
User personal documents which are available to others will be
deleted. To avoid this, you must start the data reassign process
before deleting.
</Text.Body>
</Text>
</>
),
buttons: [
@ -369,12 +375,12 @@ class SectionHeaderContent extends React.PureComponent {
visible: true,
header: "Delete profile dialog",
body: (
<Text.Body>
<Text>
Send the profile deletion instructions to the email address{" "}
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
{email}
</Link>
</Text.Body>
</Text>
),
buttons: [
<Button
@ -386,10 +392,10 @@ class SectionHeaderContent extends React.PureComponent {
sendInstructionsToDelete()
.then(() =>
toastr.success(
<Text.Body>
<Text>
Instructions to delete your profile has been sent to{" "}
<b>{email}</b> email address
</Text.Body>
</Text>
)
)
.catch(error => toastr.error(error));
@ -413,10 +419,10 @@ class SectionHeaderContent extends React.PureComponent {
resendUserInvites(new Array(this.state.profile.id))
.then(() =>
toastr.success(
<Text.Body>
<Text>
The email activation instructions have been sent to the{" "}
<b>{this.state.profile.email}</b> email address
</Text.Body>
</Text>
)
)
.catch(error => toastr.error(error));
@ -556,10 +562,10 @@ class SectionHeaderContent extends React.PureComponent {
onClick={this.goBack}
/>
</div>
<Header truncate={true}>
<HeaderContainer type='content' truncate={true}>
{profile.displayName}
{profile.isLDAP && ` (${t("LDAPLbl")})`}
</Header>
</HeaderContainer>
{((isAdmin && !profile.isOwner) || isMe(viewer, profile.userName)) && (
<ContextMenuButton
directionX="right"

View File

@ -1,12 +1,12 @@
import React from "react";
import styled from "styled-components";
import { Text } from "asc-web-components";
import { Header } from "asc-web-components";
const Container = styled.div`
margin: 0 0 40px 0;
`;
const Header = styled(Text.ContentHeader)`
const StyledHeader = styled(Header)`
margin: 0 0 24px 0;
line-height: unset;
`;
@ -16,7 +16,7 @@ const InfoFieldContainer = React.memo(props => {
return (
<Container>
<Header>{headerText}</Header>
<StyledHeader type='content'>{headerText}</StyledHeader>
{children}
</Container>
);

View File

@ -52,6 +52,7 @@ class PasswordField extends React.Component {
isDisabled={radioIsDisabled}
onClick={radioOnChange}
className="radio-group"
spacing='33px'
/>
<PasswordInput
inputName={inputName}

View File

@ -40,6 +40,7 @@ class RadioField extends React.Component {
isDisabled={radioIsDisabled}
onClick={radioOnChange}
className="radio-group"
spacing='33px'
/>
</FieldContainer>
);

View File

@ -4,7 +4,7 @@ import { connect } from 'react-redux'
import { Avatar, Button, Textarea, toastr, AvatarEditor, Text } from 'asc-web-components'
import { withTranslation, Trans } from 'react-i18next';
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts, mapGroupsToGroupSelectorOptions, mapGroupSelectorOptionsToGroups, filterGroupSelectorOptions } from "../../../../../store/people/selectors";
import { createProfile } from '../../../../../store/profile/actions';
import { createProfile, getUserPhoto } from '../../../../../store/profile/actions';
import { MainContainer, AvatarContainer, MainFieldsContainer } from './FormFields/Form'
import TextField from './FormFields/TextField'
import PasswordField from './FormFields/PasswordField'
@ -69,19 +69,17 @@ class CreateUserForm extends React.Component {
}
openAvatarEditor(){
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 () {
_this.setState({
avatar:{
defaultWidth: img.width,
defaultHeight: img.height
}
})
};
img.src = avatarDefault;
let avatarDefault = this.state.avatar.image;
let avatarDefaultSizes = /_orig_(\d*)-(\d*)./g.exec(this.state.avatar.image);
if (avatarDefault !== null && avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
image: this.state.avatar.image,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2]
}
})
}
this.setState({
visibleAvatarEditor: true,
@ -149,7 +147,21 @@ class CreateUserForm extends React.Component {
});
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
getUserPhoto(profile.id).then(userPhotoData => {
if(userPhotoData.original){
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2],
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
}
});
}
}
});
return {
visibleAvatarEditor: false,
croppedAvatarImage: "",
@ -169,7 +181,7 @@ class CreateUserForm extends React.Component {
},
avatar: {
tmpFile:"",
image: profile.avatarDefault ? "data:image/png;base64," + profile.avatarDefault : null,
image: null,
defaultWidth: 0,
defaultHeight: 0,
x: 0,
@ -373,7 +385,7 @@ class CreateUserForm extends React.Component {
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Text.Body fontSize={13} as="div">
<Text 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={{margin: "1rem 0"}} >
@ -382,7 +394,7 @@ class CreateUserForm extends React.Component {
</p>
The main e-mail can be used as a login when logging in to the portal.
</Trans>
</Text.Body>
</Text>
}
/>
<PasswordField

View File

@ -4,7 +4,7 @@ import { connect } from 'react-redux'
import { Avatar, Button, Textarea, Text, toastr, ModalDialog, TextInput, AvatarEditor, Link } from 'asc-web-components'
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 { updateProfile, getUserPhoto } from '../../../../../store/profile/actions'
import { MainContainer, AvatarContainer, MainFieldsContainer } from './FormFields/Form'
import TextField from './FormFields/TextField'
import TextChangeField from './FormFields/TextChangeField'
@ -82,6 +82,22 @@ class UpdateUserForm extends React.Component {
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
getUserPhoto(profile.id).then(userPhotoData => {
if(userPhotoData.original){
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2],
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
}
});
}
}
});
const newState = {
isLoading: false,
errors: {
@ -105,7 +121,7 @@ class UpdateUserForm extends React.Component {
},
avatar: {
tmpFile:"",
image: profile.avatarDefault ? "data:image/png;base64," + profile.avatarDefault : null,
image: null,
defaultWidth: 0,
defaultHeight: 0
}
@ -193,7 +209,7 @@ class UpdateUserForm extends React.Component {
visible: true,
header: "Change email",
body: (
<Text.Body>
<Text>
<span style={{display: "block", marginBottom: "8px"}}>The activation instructions will be sent to the entered email</span>
<TextInput
id="new-email"
@ -203,7 +219,7 @@ class UpdateUserForm extends React.Component {
onChange={this.onEmailChange}
hasError={hasError}
/>
</Text.Body>
</Text>
),
buttons: [
<Button
@ -234,9 +250,9 @@ class UpdateUserForm extends React.Component {
visible: true,
header: "Change password",
body: (
<Text.Body>
<Text>
Send the password change instructions to the <a href={`mailto:${this.state.profile.email}`}>{this.state.profile.email}</a> email address
</Text.Body>
</Text>
),
buttons: [
<Button
@ -265,9 +281,9 @@ class UpdateUserForm extends React.Component {
visible: true,
header: "Change phone",
body: (
<Text.Body>
<Text>
The instructions on how to change the user mobile number will be sent to the user email address
</Text.Body>
</Text>
),
buttons: [
<Button
@ -323,19 +339,17 @@ class UpdateUserForm extends React.Component {
}
openAvatarEditor(){
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 () {
_this.setState({
avatar:{
defaultWidth: img.width,
defaultHeight: img.height
}
})
};
img.src = avatarDefault;
let avatarDefault = this.state.avatar.image;
let avatarDefaultSizes = /_orig_(\d*)-(\d*)./g.exec(this.state.avatar.image);
if (avatarDefault !== null && avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
this.setState({
avatar: {
tmpFile: this.state.avatar.tmpFile,
image: this.state.avatar.image,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2]
}
})
}
this.setState({
visibleAvatarEditor: true,
@ -443,30 +457,30 @@ class UpdateUserForm extends React.Component {
const contacts = getUserContacts(profile.contacts);
const tooltipTypeContent =
<>
<Text.Body
<Text
style={{paddingBottom: 17}}
fontSize={13}>
{t("ProfileTypePopupHelper")}
</Text.Body>
</Text>
<Text.Body fontSize={12} as="div">
<Text fontSize={12} as="div">
<Table>
<tbody>
<tr>
<Th>
<Text.Body isBold fontSize={13}>
<Text isBold fontSize={13}>
{t("ProductsAndInstruments_Products")}
</Text.Body>
</Text>
</Th>
<Th>
<Text.Body isBold fontSize={13}>
<Text isBold fontSize={13}>
{t("Employee")}
</Text.Body>
</Text>
</Th>
<Th>
<Text.Body isBold fontSize={13}>
<Text isBold fontSize={13}>
{t("GuestCaption")}
</Text.Body>
</Text>
</Th>
</tr>
<tr>
@ -506,7 +520,7 @@ class UpdateUserForm extends React.Component {
</tr>
</tbody>
</Table>
</Text.Body>
</Text>
<Link
color="#316DAA"
isHovered={true}
@ -554,7 +568,7 @@ class UpdateUserForm extends React.Component {
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Text.Body fontSize={13} as="div">
<Text 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={{margin: "1rem 0"/*, height: "0", visibility: "hidden"*/}}>
@ -563,7 +577,7 @@ class UpdateUserForm extends React.Component {
</p>
The main e-mail can be used as a login when logging in to the portal.
</Trans>
</Text.Body>
</Text>
}
/>
<TextChangeField

View File

@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import { withRouter } from "react-router";
import { IconButton, Text, utils } from 'asc-web-components';
import { IconButton, Header, utils } from 'asc-web-components';
import { useTranslation } from 'react-i18next';
import {typeUser, typeGuest } from './../../../../../helpers/customNames';
@ -11,7 +11,7 @@ const Wrapper = styled.div`
align-Items: center;
`;
const Header = styled(Text.ContentHeader)`
const HeaderContainer = styled(Header)`
margin-left: 16px;
max-width: calc(100vw - 430px);
@media ${utils.device.tablet} {
@ -39,7 +39,7 @@ const SectionHeaderContent = (props) => {
return (
<Wrapper>
<IconButton iconName={'ArrowPathIcon'} size="16" onClick={onClick}/>
<Header truncate={true}>{headerText}</Header>
<HeaderContainer type='content' truncate={true}>{headerText}</HeaderContainer>
</Wrapper>
);
};

View File

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { Text, IconButton } from "asc-web-components";
import { Header, IconButton } from "asc-web-components";
import { withRouter } from "react-router";
import { useTranslation } from 'react-i18next';
@ -28,12 +28,12 @@ const SectionHeaderContent = props => {
onClick={() => history.push(settings.homepage)}
/>
</div>
<Text.ContentHeader truncate={true} style={textStyle}>
<Header type="content" truncate={true} style={textStyle}>
{/* {profile.displayName}
{profile.isLDAP && ` (${t('LDAPLbl')})`}
- */}
{t('ReassignmentData')}
</Text.ContentHeader>
</Header>
</div>
);
};

View File

@ -1,9 +1,7 @@
// Override default variables before the import
$font-family-base: 'Open Sans', sans-serif;
// Import Bootstrap and its default variables
@import '~bootstrap/scss/bootstrap.scss';
@import '~react-toastify/dist/ReactToastify.min.css';
html, body {
height: 100%;
@ -18,4 +16,8 @@ html, body {
left: calc(50% - 32px);
top: 35%;
}
}
}
body {
margin: 0;
}

View File

@ -10,7 +10,8 @@ import App from "./App";
import * as serviceWorker from "./serviceWorker";
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;
import { getPortalInviteLinks } from './store/portal/actions';
const { setIsLoaded, getUserInfo, setCurrentProductId, setCurrentProductHomePage, getPortalPasswordSettings, getPortalCultures } = commonStore.auth.actions;
const { AUTH_KEY } = constants;
const token = localStorage.getItem(AUTH_KEY);

View File

@ -0,0 +1,24 @@
import { api } from 'asc-web-common';
export const SET_INVITE_LINKS = "SET_INVITE_LINKS";
export function setInviteLinks(userLink, guestLink) {
return {
type: SET_INVITE_LINKS,
payload: {
userLink,
guestLink
}
};
}
export function getPortalInviteLinks() {
return (dispatch, getState) => {
const { auth } = getState();
if (!auth.user.isAdmin) return Promise.resolve();
return api.portal.getInvitationLinks().then(data => {
dispatch(setInviteLinks(data.userLink, data.guestLink));
});
};
};

View File

@ -0,0 +1,18 @@
import { SET_INVITE_LINKS } from "./actions";
const initialState = {
inviteLinks: {}
};
const profileReducer = (state = initialState, action) => {
switch (action.type) {
case SET_INVITE_LINKS:
return Object.assign({}, state, {
inviteLinks: action.payload
});
default:
return state;
};
}
export default profileReducer;

View File

@ -93,8 +93,6 @@ export function updateProfileCulture(id, culture) {
};
};
export function getInvitationLink(isGuest = false) {
return dispatch => {
return api.portal.getInvitationLink(isGuest);
}
export function getUserPhoto(id) {
return api.people.getUserPhoto(id);
};

View File

@ -2,6 +2,7 @@ import { combineReducers } from 'redux';
import peopleReducer from './people/reducers';
import profileReducer from './profile/reducers';
import groupReducer from './group/reducers';
import portalReducer from './portal/reducers';
import { store } from 'asc-web-common';
const { reducer: authReducer } = store.auth;
@ -9,7 +10,8 @@ const rootReducer = combineReducers({
auth: authReducer,
people: peopleReducer,
profile: profileReducer,
group: groupReducer
group: groupReducer,
portal: portalReducer
});
export default rootReducer;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>

View File

@ -75,11 +75,10 @@ namespace ASC.Employee.Core.Controllers
public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
public Signature Signature { get; }
public InstanceCrypto InstanceCrypto { get; }
public DbOptionsManager DbOptions { get; }
public AccountLinkerStorage AccountLinkerStorage { get; }
public WebItemSecurityCache WebItemSecurityCache { get; }
public MessageTarget MessageTarget { get; }
public SettingsManager SettingsManager { get; }
public IOptionsSnapshot<AccountLinker> AccountLinker { get; }
public EmployeeWraperFullHelper EmployeeWraperFullHelper { get; }
public EmployeeWraperHelper EmployeeWraperHelper { get; }
public ILog Log { get; }
@ -109,12 +108,11 @@ namespace ASC.Employee.Core.Controllers
DisplayUserSettingsHelper displayUserSettingsHelper,
Signature signature,
InstanceCrypto instanceCrypto,
DbOptionsManager dbOptions,
AccountLinkerStorage accountLinkerStorage,
WebItemSecurityCache webItemSecurityCache,
MessageTarget messageTarget,
SettingsManager settingsManager,
IOptionsMonitor<ILog> option,
IOptionsSnapshot<AccountLinker> accountLinker,
EmployeeWraperFullHelper employeeWraperFullHelper,
EmployeeWraperHelper employeeWraperHelper)
{
@ -143,11 +141,10 @@ namespace ASC.Employee.Core.Controllers
DisplayUserSettingsHelper = displayUserSettingsHelper;
Signature = signature;
InstanceCrypto = instanceCrypto;
DbOptions = dbOptions;
AccountLinkerStorage = accountLinkerStorage;
WebItemSecurityCache = webItemSecurityCache;
MessageTarget = messageTarget;
SettingsManager = settingsManager;
AccountLinker = accountLinker;
EmployeeWraperFullHelper = employeeWraperFullHelper;
EmployeeWraperHelper = employeeWraperHelper;
}
@ -750,109 +747,7 @@ namespace ASC.Employee.Core.Controllers
return new ThumbnailsDataWrapper(user.ID, UserPhotoManager);
}
public FormFile Base64ToImage(string base64String, string fileName)
{
byte[] imageBytes = Convert.FromBase64String(base64String);
MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
ms.Write(imageBytes, 0, imageBytes.Length);
return new FormFile(ms, 0, ms.Length, fileName, fileName);
}
[Create("{userid}/photo/cropped")]
public People.Models.FileUploadResult UploadCroppedMemberPhoto(string userid, UploadCroppedPhotoModel model)
{
var result = new People.Models.FileUploadResult();
try
{
Guid userId;
try
{
userId = new Guid(userid);
}
catch
{
userId = SecurityContext.CurrentAccount.ID;
}
PermissionContext.DemandPermissions(new UserSecurityProvider(userId), Constants.Action_EditUser);
var userPhoto = Base64ToImage(model.base64CroppedImage, "userPhoto_" + userId.ToString());
var defaultUserPhoto = Base64ToImage(model.base64DefaultImage, "defaultPhoto" + userId.ToString());
if (userPhoto.Length > SetupInfo.MaxImageUploadSize)
{
result.Success = false;
result.Message = FileSizeComment.FileImageSizeExceptionString;
return result;
}
var data = new byte[userPhoto.Length];
using var inputStream = userPhoto.OpenReadStream();
var br = new BinaryReader(inputStream);
br.Read(data, 0, (int)userPhoto.Length);
br.Close();
var defaultData = new byte[defaultUserPhoto.Length];
using var defaultInputStream = defaultUserPhoto.OpenReadStream();
var defaultBr = new BinaryReader(defaultInputStream);
defaultBr.Read(defaultData, 0, (int)defaultUserPhoto.Length);
defaultBr.Close();
CheckImgFormat(data);
if (model.Autosave)
{
if (data.Length > SetupInfo.MaxImageUploadSize)
throw new ImageSizeLimitException();
var mainPhoto = UserPhotoManager.SaveOrUpdateCroppedPhoto(userId, data, defaultData);
result.Data =
new
{
main = mainPhoto,
retina = UserPhotoManager.GetRetinaPhotoURL(userId),
max = UserPhotoManager.GetMaxPhotoURL(userId),
big = UserPhotoManager.GetBigPhotoURL(userId),
medium = UserPhotoManager.GetMediumPhotoURL(userId),
small = UserPhotoManager.GetSmallPhotoURL(userId),
};
}
else
{
result.Data = UserPhotoManager.SaveTempPhoto(data, SetupInfo.MaxImageUploadSize, UserPhotoManager.OriginalFotoSize.Width, UserPhotoManager.OriginalFotoSize.Height);
}
result.Success = true;
}
catch (UnknownImageFormatException)
{
result.Success = false;
result.Message = PeopleResource.ErrorUnknownFileImageType;
}
catch (ImageWeightLimitException)
{
result.Success = false;
result.Message = PeopleResource.ErrorImageWeightLimit;
}
catch (ImageSizeLimitException)
{
result.Success = false;
result.Message = PeopleResource.ErrorImageSizetLimit;
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.Message.HtmlEncode();
}
return result;
}
[Create("{userid}/photo")]
public People.Models.FileUploadResult UploadMemberPhoto(string userid, IFormCollection model)
{
@ -1005,6 +900,7 @@ namespace ASC.Employee.Core.Controllers
var settings = new UserPhotoThumbnailSettings(thumbnailsModel.X, thumbnailsModel.Y, thumbnailsModel.Width, thumbnailsModel.Height);
SettingsManager.SaveForUser(settings, user.ID);
UserPhotoManager.RemovePhoto(user.ID);
UserPhotoManager.SaveOrUpdatePhoto(user.ID, data);
UserPhotoManager.RemoveTempPhoto(fileName);
}
@ -1338,7 +1234,7 @@ namespace ASC.Employee.Core.Controllers
private AccountLinker GetLinker()
{
return new AccountLinker("webstudio", Signature, InstanceCrypto, DbOptions, AccountLinkerStorage);
return AccountLinker.Get("webstudio");
}
private static string GetMeaningfulProviderName(string providerName)
@ -1579,6 +1475,7 @@ namespace ASC.Employee.Core.Controllers
public static IServiceCollection AddPeopleController(this IServiceCollection services)
{
return services
.AddAccountLinker()
.AddMessageTargetService()
.AddAccountLinkerStorageService()
.AddFileSizeCommentService()

View File

@ -89,9 +89,6 @@ namespace ASC.Web.Api.Models
[DataMember(Order = 20)]
public string AvatarMax { get; set; }
[DataMember(Order = 20)]
public string AvatarDefault { get; set; }
[DataMember(Order = 20)]
public string AvatarMedium { get; set; }
@ -252,11 +249,6 @@ namespace ASC.Web.Api.Models
var userInfoLM = userInfo.LastModified.GetHashCode();
if (Context.Check("avatarDefault"))
{
result.AvatarDefault = Convert.ToBase64String(UserManager.GetUserPhoto(userInfo.ID));
}
if (Context.Check("avatarMax"))
{
result.AvatarMax = UserPhotoManager.GetMaxPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");

View File

@ -8,12 +8,6 @@ namespace ASC.People.Models
public List<IFormFile> Files { get; set; }
public bool Autosave { get; set; }
}
public class UploadCroppedPhotoModel
{
public string base64CroppedImage { get; set; }
public string base64DefaultImage { get; set; }
public bool Autosave { get; set; }
}
public class FileUploadResult
{

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
@ -24,8 +24,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
</ItemGroup>
<ItemGroup>

View File

@ -3,40 +3,37 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"asc-web-components": "file:../../packages/asc-web-components",
"asc-web-common": "file:../../packages/asc-web-common",
"asc-web-components": "file:../../packages/asc-web-components",
"axios": "^0.19.0",
"bootstrap": "4.3.1",
"connected-react-router": "6.5.2",
"history": "4.9.0",
"i18next": "17.0.12",
"i18next-browser-languagedetector": "3.0.3",
"i18next-xhr-backend": "3.1.2",
"jquery": "3.4.1",
"connected-react-router": "6.6.1",
"history": "4.10.1",
"i18next": "19.0.1",
"i18next-browser-languagedetector": "4.0.1",
"i18next-xhr-backend": "3.2.2",
"lodash": "4.17.15",
"lodash-es": "4.17.15",
"merge": "^1.2.1",
"node-sass": "^4.12.0",
"oidc-client": "^1.9.0",
"node-sass": "^4.13.0",
"oidc-client": "^1.9.1",
"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",
"reactstrap": "8.0.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-i18next": "11.2.5",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"redux": "4.0.4",
"redux-thunk": "2.3.0",
"styled-components": "^4.3.2"
"styled-components": "^4.4.1"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.4",
"cross-env": "^5.2.0",
"react-app-rewired": "^2.1.3",
"react-scripts": "3.1.1",
"copy-webpack-plugin": "^5.0.5",
"cross-env": "^6.0.3",
"react-app-rewired": "^2.1.5",
"react-scripts": "3.3.0",
"redux-devtools-extension": "^2.13.8",
"rimraf": "2.6.3"
"rimraf": "3.0.0"
},
"scripts": {
"start": "react-app-rewired start",

View File

@ -74,33 +74,33 @@ const Body = ({language}) => {
</p>
<VersionStyle>
<Text.Body className="text_p" fontSize={14} color="#A3A9AE">
<Text className="text_p" fontSize={14} color="#A3A9AE">
{`${t("AboutCompanyVersion")}: ${version.version}`}
</Text.Body>
</Text>
</VersionStyle>
<Text.Body className="copyright-line" fontSize={14}>
<Text className="copyright-line" fontSize={14}>
{t("AboutCompanyLicensor")}
</Text.Body>
</Text>
<Text.Body className="text_p" fontSize={16} isBold={true}>
<Text className="text_p" fontSize={16} isBold={true}>
Ascensio System SIA
</Text.Body>
</Text>
<Style>
<Text.Body className="text_p" fontSize={12}>
<Text.Body
<Text className="text_p" fontSize={12}>
<Text
className="text_span"
fontSize={12}
as="span"
color="#A3A9AE"
>
{t("AboutCompanyAddressTitle")}:{" "}
</Text.Body>
</Text>
20A-12 Ernesta Birznieka-Upisha street, Riga, Latvia, EU, LV-1050
</Text.Body>
</Text>
<Text.Body
<Text
fontSize={12}
className="text_span"
as="span"
@ -110,20 +110,20 @@ const Body = ({language}) => {
<Link href="mailto:support@onlyoffice.com" fontSize={12}>
support@onlyoffice.com
</Link>
</Text.Body>
</Text>
<div style={{ marginTop: "4px" }}>
<Text.Body className="text_p" fontSize={12}>
<Text.Body
<Text className="text_p" fontSize={12}>
<Text
fontSize={12}
className="text_span"
as="span"
color="#A3A9AE"
>
{t("AboutCompanyTelTitle")}:{" "}
</Text.Body>
</Text>
+371 660-16425
</Text.Body>
</Text>
</div>
<Link href="http://www.onlyoffice.com" fontSize={12}>
@ -131,7 +131,7 @@ const Body = ({language}) => {
</Link>
<div style={{ marginTop: "20px" }}>
<Text.Body className="text_p" fontSize={12}>
<Text className="text_p" fontSize={12}>
{t("LicensedUnder", {license: "GNU GPL v.3"} )}:{" "}
<Link
href="https://www.gnu.org/licenses/gpl-3.0.html"
@ -140,9 +140,9 @@ const Body = ({language}) => {
>
GNU GPL v.3
</Link>{" "}
</Text.Body>
</Text>
<Text.Body className="text_p" fontSize={12}>
<Text className="text_p" fontSize={12}>
{t("SourceCode")}:{" "}
<Link
href="https://github.com/ONLYOFFICE/CommunityServer"
@ -151,7 +151,7 @@ const Body = ({language}) => {
>
GitHub
</Link>
</Text.Body>
</Text>
</div>
</Style>
</BodyStyle>

View File

@ -5,7 +5,8 @@ import { PageLayout, Loader } from 'asc-web-components';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { store } from 'asc-web-common';
const { logout, changeEmail } = store.auth.actions;
import { changeEmail } from '../../../../store/confirm/actions';
const { logout } = store.auth.actions;
class ActivateEmail extends React.PureComponent {
@ -18,13 +19,13 @@ class ActivateEmail extends React.PureComponent {
history.push(`/login/confirmed-email=${email}`);
})
.catch((e) => {
console.log('activate email error', e);
// console.log('activate email error', e);
history.push(`/login/error=${e}`);
});
}
render() {
console.log('Activate email render');
// console.log('Activate email render');
return (
<Loader className="pageLoader" type="rombs" size={40} />
);

View File

@ -3,11 +3,10 @@ import { withRouter } from "react-router";
import { withTranslation } from 'react-i18next';
import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } from 'asc-web-components';
import styled from 'styled-components';
import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { store, constants } from 'asc-web-common';
const { getConfirmationInfo, activateConfirmUser } = store.auth.actions;
import { constants } from 'asc-web-common';
import { getConfirmationInfo, activateConfirmUser } from '../../../../store/confirm/actions';
const { EmployeeActivationStatus } = constants;
@ -139,7 +138,7 @@ class Confirm extends React.PureComponent {
componentDidMount() {
const { getConfirmationInfo, history } = this.props;
getConfirmationInfo(this.state.key, this.state.linkType)
getConfirmationInfo(this.state.key)
.then(
function () {
console.log("get settings success");
@ -191,13 +190,13 @@ class Confirm extends React.PureComponent {
<ConfirmContainer>
<div className='start-basis'>
<div className='margin-left'>
<Text.Body className='confirm-row' as='p' fontSize={18}>{t('InviteTitle')}</Text.Body>
<Text className='confirm-row' as='p' fontSize={18}>{t('InviteTitle')}</Text>
<div className='confirm-row full-width break-word'>
<a href='/login'>
<img src="images/dark_general.png" alt="Logo" />
</a>
<Text.Body as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text.Body>
<Text as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text>
</div>
</div>
@ -294,14 +293,13 @@ class Confirm extends React.PureComponent {
{/* <Row className='confirm-row'>
<Text.Body as='p' fontSize={14}>{t('LoginWithAccount')}</Text.Body>
<Text as='p' fontSize={14}>{t('LoginWithAccount')}</Text>
</Row>
*/}
<Collapse className='confirm-row'
isOpen={!!this.state.errorText}>
<div className="alert alert-danger">{this.state.errorText}</div>
</Collapse>
<Text className="confirm-row" fontSize={14} color="#c30">
{this.state.errorText}
</Text>
</div>
</ConfirmContainer>
)
@ -321,7 +319,7 @@ const ActivateUserForm = (props) => (<PageLayout sectionBodyContent={<Confirm {.
function mapStateToProps(state) {
return {
isConfirmLoaded: state.auth.isConfirmLoaded,
isConfirmLoaded: state.confirm.isConfirmLoaded,
settings: state.auth.settings.passwordSettings,
greetingTitle: state.auth.settings.greetingSettings
};

View File

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

View File

@ -78,10 +78,10 @@ class Form extends React.PureComponent {
src="images/dark_general.png"
alt="Logo"
/>
<Text.Body className="owner-title">{greetingTitle}</Text.Body>
<Text.Body className="owner-confirm_text" fontSize={18}>
<Text className="owner-title">{greetingTitle}</Text>
<Text className="owner-confirm_text" fontSize={18}>
{t("ConfirmOwnerPortalTitle", { newOwner: "NEW OWNER" })}
</Text.Body>
</Text>
{this.state.showButtons ? (
<>
<Button
@ -103,9 +103,9 @@ class Form extends React.PureComponent {
/>
</>
) : (
<Text.Body className="owner-confirm-message" fontSize={12}>
<Text className="owner-confirm-message" fontSize={12}>
{t("ConfirmOwnerPortalSuccessMessage")}
</Text.Body>
</Text>
)}
</div>
</div>

View File

@ -4,43 +4,42 @@ import { withTranslation } from "react-i18next";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import styled from "styled-components";
import { Container, Row, Col, Card, CardTitle, CardImg } from "reactstrap";
import {
Button,
PageLayout,
Text,
PasswordInput,
Loader,
toastr
toastr,
Heading
} from "asc-web-components";
import { store } from 'asc-web-common';
const { changePassword, getConfirmationInfo, logout } = store.auth.actions;
import { store } from "asc-web-common";
import { getConfirmationInfo, changePassword } from '../../../../store/confirm/actions';
const { logout } = store.auth.actions;
const BodyStyle = styled(Container)`
margin-top: 70px;
p {
const BodyStyle = styled.form`
margin: 70px auto 0 auto;
max-width: 500px;
.password-header {
margin-bottom: 24px;
.password-logo {
max-width: 216px;
max-height: 35px;
}
.password-title {
margin: 8px 0;
}
}
.password-text {
margin-bottom: 5px;
}
.button-style {
margin-top: 20px;
}
.password-row {
margin: 23px 0 0;
.password-card {
border: none;
.card-img {
max-width: 216px;
max-height: 35px;
}
.card-title {
word-wrap: break-word;
margin: 8px 0;
text-align: left;
font-size: 24px;
color: #116d9d;
}
}
.password-button {
margin-top: 20px;
}
`;
@ -106,8 +105,7 @@ class Form extends React.PureComponent {
componentDidMount() {
const { getConfirmationInfo, history } = this.props;
getConfirmationInfo(this.state.key)
.catch(error => {
getConfirmationInfo(this.state.key).catch(error => {
toastr.error(this.props.t(`${error}`));
history.push("/");
});
@ -126,69 +124,65 @@ class Form extends React.PureComponent {
render() {
const { settings, isConfirmLoaded, t, greetingTitle } = this.props;
const { isLoading, password, passwordEmpty } = this.state;
const mdOptions = { size: 6, offset: 3 };
return !isConfirmLoaded ? (
<Loader className="pageLoader" type="rombs" size={40} />
) : (
<BodyStyle>
<Row className="password-row">
<Col sm="12" md={mdOptions}>
<Card className="password-card">
<CardImg
className="card-img"
src="images/dark_general.png"
alt="Logo"
top
/>
<CardTitle className="card-title">
{greetingTitle}
</CardTitle>
</Card>
<Text.Body fontSize={14}>{t("PassworResetTitle")}</Text.Body>
<PasswordInput
id="password"
name="password"
inputName="password"
inputValue={password}
size="huge"
scale={true}
type="password"
isDisabled={isLoading}
hasError={passwordEmpty}
onValidateInput={this.validatePassword}
generatorSpecial="!@#$%^&*"
tabIndex={1}
value={password}
onChange={this.onChange}
emailInputName="E-mail"
passwordSettings={settings}
tooltipPasswordTitle="Password must contain:"
tooltipPasswordLength={`${t("ErrorPasswordLength", {
fromNumber: 6,
toNumber: 30
})}:`}
placeholder={t("PasswordCustomMode")}
maxLength={30}
onKeyDown={this.onKeyPress}
isAutoFocussed={true}
inputWidth="490px"
/>
<Button
className="button-style"
primary
size="big"
tabIndex={2}
label={
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
}
isDisabled={isLoading}
isLoading={isLoading}
onClick={this.onSubmit}
/>
</Col>
</Row>
<div className="password-header">
<img
className="password-logo"
src="images/dark_general.png"
alt="Logo"
/>
<Heading className="password-title" color="#116d9d">
{greetingTitle}
</Heading>
</div>
<Text className="password-text" fontSize={14}>
{t("PassworResetTitle")}
</Text>
<PasswordInput
id="password"
name="password"
inputName="password"
inputValue={password}
size="huge"
scale={true}
type="password"
isDisabled={isLoading}
hasError={passwordEmpty}
onValidateInput={this.validatePassword}
generatorSpecial="!@#$%^&*"
tabIndex={1}
value={password}
onChange={this.onChange}
emailInputName="E-mail"
passwordSettings={settings}
tooltipPasswordTitle="Password must contain:"
tooltipPasswordLength={`${t("ErrorPasswordLength", {
fromNumber: 6,
toNumber: 30
})}:`}
placeholder={t("PasswordCustomMode")}
maxLength={30}
onKeyDown={this.onKeyPress}
isAutoFocussed={true}
inputWidth="490px"
/>
<Button
id="button"
className="password-button"
primary
size="big"
tabIndex={2}
label={
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
}
isDisabled={isLoading}
isLoading={isLoading}
onClick={this.onSubmit}
/>
</BodyStyle>
);
}
@ -212,14 +206,15 @@ const ChangePasswordForm = props => (
function mapStateToProps(state) {
return {
isValidConfirmLink: state.auth.isValidConfirmLink,
isConfirmLoaded: state.auth.isConfirmLoaded,
isConfirmLoaded: state.confirm.isConfirmLoaded,
settings: state.auth.settings.passwordSettings,
isAuthenticated: state.auth.isAuthenticated,
greetingTitle: state.auth.settings.greetingSettings,
greetingTitle: state.auth.settings.greetingSettings
};
}
export default connect(
mapStateToProps,
{ changePassword, getConfirmationInfo, logout }
)(withRouter(withTranslation()(ChangePasswordForm)));
export default connect(mapStateToProps, {
changePassword,
getConfirmationInfo,
logout
})(withRouter(withTranslation()(ChangePasswordForm)));

View File

@ -68,9 +68,9 @@ const PhoneForm = props => {
{greetingTitle}
</div>
</div>
<Text.Body className="edit-text" isBold fontSize={14}>{subTitleTranslation}</Text.Body>
<Text.Body fontSize={13}>{infoTranslation}: <b>+{currentPhone}</b></Text.Body>
<Text.Body className="edit-text" fontSize={13}>{subInfoTranslation}</Text.Body>
<Text className="edit-text" isBold fontSize={14}>{subTitleTranslation}</Text>
<Text fontSize={13}>{infoTranslation}: <b>+{currentPhone}</b></Text>
<Text className="edit-text" fontSize={13}>{subInfoTranslation}</Text>
<TextInput
id="phone"
name="phone"

View File

@ -3,11 +3,11 @@ import { withRouter } from "react-router";
import { withTranslation } from 'react-i18next';
import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } from 'asc-web-components';
import styled from 'styled-components';
import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { store } from 'asc-web-common';
const { getConfirmationInfo, createConfirmUser, logout, login } = store.auth.actions;
import { getConfirmationInfo, createConfirmUser } from '../../../../store/confirm/actions';
const { logout, login } = store.auth.actions;
const inputWidth = '400px';
@ -153,7 +153,7 @@ class Confirm extends React.PureComponent {
componentDidMount() {
const { getConfirmationInfo, history } = this.props;
getConfirmationInfo(this.state.key, this.state.linkType)
getConfirmationInfo(this.state.key)
.catch(e => {
console.error("get settings error", e);
history.push(`/login/error=${e}`);
@ -206,13 +206,13 @@ class Confirm extends React.PureComponent {
<ConfirmContainer>
<div className='start-basis'>
<div className='margin-left'>
<Text.Body className='confirm-row' as='p' fontSize={18}>{t('InviteTitle')}</Text.Body>
<Text className='confirm-row' as='p' fontSize={18}>{t('InviteTitle')}</Text>
<div className='confirm-row full-width break-word'>
<a href='/login'>
<img src="images/dark_general.png" alt="Logo" />
</a>
<Text.Body as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text.Body>
<Text as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text>
</div>
</div>
@ -314,14 +314,13 @@ class Confirm extends React.PureComponent {
{/* <Row className='confirm-row'>
<Text.Body as='p' fontSize={14}>{t('LoginWithAccount')}</Text.Body>
<Text as='p' fontSize={14}>{t('LoginWithAccount')}</Text>
</Row>
*/}
<Collapse className='confirm-row'
isOpen={!!this.state.errorText}>
<div className="alert alert-danger">{this.state.errorText}</div>
</Collapse>
<Text className='confirm-row' fontSize={14} color="#c30">
{this.state.errorText}
</Text>
</div>
</ConfirmContainer>
)
@ -341,7 +340,7 @@ const CreateUserForm = (props) => (<PageLayout sectionBodyContent={<Confirm {...
function mapStateToProps(state) {
return {
isConfirmLoaded: state.auth.isConfirmLoaded,
isConfirmLoaded: state.confirm.isConfirmLoaded,
isAuthenticated: state.auth.isAuthenticated,
settings: state.auth.settings.passwordSettings,
greetingTitle: state.auth.settings.greetingSettings

View File

@ -70,13 +70,13 @@ class ProfileRemove extends React.PureComponent {
<a href='/login'>
<img src="images/dark_general.png" alt="Logo" />
</a>
<Text.Body as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text.Body>
<Text as='p' fontSize={24} color='#116d9d'>{greetingTitle}</Text>
</div>
{!isProfileDeleted
? <>
<Text.Body className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileConfirmation')}</Text.Body>
<Text.Body className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileConfirmationInfo')}</Text.Body>
<Text className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileConfirmation')}</Text>
<Text className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileConfirmationInfo')}</Text>
<Button
className='confirm-row'
@ -89,8 +89,8 @@ class ProfileRemove extends React.PureComponent {
/>
</>
: <>
<Text.Body className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileSuccessMessage')}</Text.Body>
<Text.Body className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileSuccessMessageInfo')}</Text.Body>
<Text className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileSuccessMessage')}</Text>
<Text className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileSuccessMessageInfo')}</Text>
</>
}

View File

@ -2,25 +2,60 @@ import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from "react-router";
import { Container, Col, Row, Collapse } from 'reactstrap';
import { ModuleTile, Loader, PageLayout, toastr } from 'asc-web-components';
import { Loader, PageLayout, toastr, Text } from 'asc-web-components';
import { ModuleTile } from "asc-web-common";
import { useTranslation } from 'react-i18next';
import i18n from './i18n';
import styled from "styled-components";
const HomeContainer = styled.div`
padding: 62px 15px 0 15px;
margin: 0 auto;
max-width: 1140px;
width: 100%;
box-sizing: border-box;
/*justify-content: center;*/
.home-modules {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
.home-module {
flex-basis: 0;
flex-grow: 1;
max-width: 100%;
}
}
.home-error-text {
margin-top: 23px;
padding: 0 30px;
@media (min-width: 768px) {
margin-left: 25%;
flex: 0 0 50%;
max-width: 50%;
}
@media (min-width: 576px) {
flex: 0 0 100%;
max-width: 100%;
}
}
`;
const Tiles = ({ modules, isPrimary, history }) => {
let index = 0;
return (
<Row>
<div className="home-modules">
{
modules.filter(m => m.isPrimary === isPrimary).map(module => (
<Col key={++index}>
<div className="home-module" key={++index}>
<ModuleTile {...module} onClick={() => window.open(module.link, '_self')} />
</Col>
</div>
))
}
</Row>
</div>
);
};
@ -44,17 +79,16 @@ const Body = ({ modules, match, history, isLoaded }) => {
<Loader className="pageLoader" type="rombs" size={40} />
)
: (
<Container style={{ paddingTop: '62px' }}>
<HomeContainer>
<Tiles modules={modules} isPrimary={true} history={history} />
<Tiles modules={modules} isPrimary={false} history={history} />
<Collapse isOpen={!modules || !modules.length}>
<Row style={{ margin: "23px 0 0" }}>
<Col sm="12" md={{ size: 6, offset: 3 }}>
<div className="alert alert-danger">{t('NoOneModulesAvailable')}</div>
</Col>
</Row>
</Collapse>
</Container>
{!modules || !modules.length ? (
<Text className="home-error-text" fontSize={14} color="#c30">
{t('NoOneModulesAvailable')}
</Text>
) : null}
</HomeContainer>
)
);

View File

@ -14,10 +14,8 @@ import { getKeyByLink, settingsTree, getSelectedLinkByKey, selectKeyOfTreeElemen
const StyledTreeMenu = styled(TreeMenu)`
.inherit-title-link {
& > span {
font-size: inherit;
font-weight: inherit;
}
}
`;
@ -176,4 +174,4 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps)(withRouter(withTranslation()(ArticleBodyContent)));
export default connect(mapStateToProps)(withRouter(withTranslation()(ArticleBodyContent)));

View File

@ -1,10 +1,10 @@
import React from 'react';
import { Text } from 'asc-web-components';
import { Header } from 'asc-web-components';
import { useTranslation } from 'react-i18next';
const ArticleHeaderContent = () => {
const { t } = useTranslation();
return <Text.MenuHeader>{t('Settings')}</Text.MenuHeader>;
return <Header type="menu">{t('Settings')}</Header>;
}
export default ArticleHeaderContent;

View File

@ -1,11 +1,11 @@
import React from "react";
import { withRouter } from "react-router";
import { Text, utils } from 'asc-web-components';
import { Header, utils } from 'asc-web-components';
import styled from 'styled-components';
import { withTranslation } from 'react-i18next';
import { getKeyByLink, settingsTree, getTKeyByKey } from '../../../utils';
const Header = styled(Text.ContentHeader)`
const HeaderContainer = styled(Header)`
margin-right: 16px;
max-width: calc(100vw - 430px);
@media ${utils.device.tablet} {
@ -56,9 +56,9 @@ class SectionHeaderContent extends React.Component {
const { header } = this.state;
return (
<Header truncate={true}>
<HeaderContainer type='content' truncate={true}>
{t(header)}
</Header>
</HeaderContainer>
);
}
};

View File

@ -5,7 +5,8 @@ import { FieldContainer, Text, ComboBox, Loader, Button, toastr, Link, TextInput
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;
import { setLanguageAndTime, getPortalTimezones, setGreetingTitle, restoreGreetingTitle } from '../../../../../store/settings/actions';
const { getPortalCultures } = store.auth.actions;
const mapCulturesToArray = (cultures, t) => {
return cultures.map((culture) => {
@ -152,7 +153,7 @@ class Customization extends React.Component {
const { isLoadedData, languages, language, isLoading, timezones, timezone, greetingTitle, isLoadingGreetingSave, isLoadingGreetingRestore } = this.state;
const supportEmail = "documentation@onlyoffice.com";
const tooltipLanguage =
<Text.Body fontSize={13}>
<Text fontSize={13}>
<Trans i18nKey="NotFoundLanguage" i18n={i18n}>
"In case you cannot find your language in the list of the
available ones, feel free to write to us at
@ -163,7 +164,7 @@ class Customization extends React.Component {
</Trans>
{" "}
<Link isHovered={true} href="https://helpcenter.onlyoffice.com/ru/guides/become-translator.aspx">{t("LearnMore")}</Link>
</Text.Body>
</Text>
console.log("CustomizationSettings render");
return (
@ -172,7 +173,7 @@ class Customization extends React.Component {
: <>
<StyledComponent>
<div className='settings-block'>
<Text.Body fontSize={16}>{t('StudioTimeLanguageSettings')}</Text.Body>
<Text fontSize={16}>{t('StudioTimeLanguageSettings')}</Text>
<FieldContainer
id='fieldContainerLanguage'
className='margin-top field-container-width'
@ -223,7 +224,7 @@ class Customization extends React.Component {
</div>
<div className='settings-block'>
<Text.Body fontSize={16}>{t('GreetingSettingsTitle')}</Text.Body>
<Text fontSize={16}>{t('GreetingSettingsTitle')}</Text>
<FieldContainer
id='fieldContainerWelcomePage'
className='margin-top field-container-width'

View File

@ -200,14 +200,14 @@ class WhiteLabel extends React.Component {
: <>
<StyledComponent>
<div className='settings-block'>
<Text.Body fontSize={16}>{t('LogoSettings')}</Text.Body>
<Text.Body
<Text fontSize={16}>{t('LogoSettings')}</Text>
<Text
className='margin-top'
fontSize={14}>
<Trans i18nKey="LogoUploadRecommendation">
We recommended that you use images in <strong>PNG</strong> format with transparent background
</Trans>
</Text.Body>
</Text>
<FieldContainer
id='fieldContainerCompanyName'

View File

@ -59,6 +59,12 @@ const ToggleContentContainer = styled.div`
.filter_container {
margin-top: 16px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
`;
class PureAdminsSettings extends Component {
@ -444,11 +450,11 @@ class PureAdminsSettings extends Component {
</Link>
<div style={{ maxWidth: 120 }} />
<Text.Body>
<Text>
{user.isAdmin
? "Full access"
: "People module admin"}
</Text.Body>
</Text>
{!user.isOwner ? (
<IconButton

View File

@ -11,14 +11,6 @@ const ProjectsContainer = styled.div`
align-items: flex-start;
flex-direction: row;
flex-wrap: wrap;
.display-block {
display: block;
}
div label:not(:first-child) {
margin: 0;
}
`;
const RadioButtonContainer = styled.div`
@ -81,12 +73,12 @@ class PureModulesSettings extends Component {
>
<ProjectsContainer>
<RadioButtonContainer>
<Text.Body>
<Text>
{t("AccessRightsAccessToProduct", {
product: t("People")
})}
:
</Text.Body>
</Text>
<RadioButtonGroup
name="selectGroup"
selected="allUsers"
@ -104,18 +96,19 @@ class PureModulesSettings extends Component {
})
}
]}
className="display-block"
orientation='vertical'
spacing='10px'
/>
</RadioButtonContainer>
<ProjectsBody>
<Text.Body className="projects_margin" fontSize={12}>
<Text className="projects_margin" fontSize={12}>
{t("AccessRightsProductUsersCan", {
category: t("People")
})}
</Text.Body>
<Text.Body fontSize={12}>
</Text>
<Text fontSize={12}>
<li>{t("ViewProfilesAndGroups")}</li>
</Text.Body>
</Text>
</ProjectsBody>
</ProjectsContainer>
</ToggleContent>

View File

@ -182,7 +182,7 @@ class PureOwnerSettings extends Component {
className="page_loader"
/>
<HeaderContainer>
<Text.Body fontSize={18}>{t("PortalOwner")}</Text.Body>
<Text fontSize={18}>{t("PortalOwner")}</Text>
</HeaderContainer>
<BodyContainer>
@ -195,13 +195,13 @@ class PureOwnerSettings extends Component {
source={owner.avatar}
/>
<div className="avatar_body">
<Text.Body
<Text
className="avatar_text"
fontSize={16}
isBold={true}
>
{owner.displayName}
</Text.Body>
</Text>
{owner.groups &&
owner.groups.map(group => (
<Link
@ -215,20 +215,20 @@ class PureOwnerSettings extends Component {
</div>
</AvatarContainer>
<ProjectsBody>
<Text.Body className="portal_owner" fontSize={12}>
<Text className="portal_owner" fontSize={12}>
{t("AccessRightsOwnerCan")}:
</Text.Body>
<Text.Body fontSize={12}>
</Text>
<Text fontSize={12}>
{OwnerOpportunities.map((item, key) => (
<li key={key}>{item};</li>
))}
</Text.Body>
</Text>
</ProjectsBody>
</BodyContainer>
<Text.Body fontSize={12} className="text-body_wrapper">
<Text fontSize={12} className="text-body_wrapper">
{t("AccessRightsChangeOwnerText")}
</Text.Body>
</Text>
<Link
className="link_style"
@ -246,13 +246,13 @@ class PureOwnerSettings extends Component {
isDisabled={!isLoading ? selectedOwner === null : false}
onClick={this.onChangeOwner}
/>
<Text.Body
<Text
className="text-body_inline"
fontSize={12}
color="#A3A9AE"
>
{t("AccessRightsChangeOwnerConfirmText")}
</Text.Body>
</Text>
<div className="advanced-selector">
<AdvancedSelector

View File

@ -1,9 +1,7 @@
// Override default variables before the import
$font-family-base: 'Open Sans', sans-serif;
// Import Bootstrap and its default variables
@import '~bootstrap/scss/bootstrap.scss';
@import '~react-toastify/dist/ReactToastify.min.css';
html, body {
height: 100%;
@ -18,3 +16,6 @@ html, body {
top: 35%;
}
}
body {
margin: 0;
}

View File

@ -0,0 +1,74 @@
import { api, store } from "asc-web-common";
const { setCurrentUser, loadInitInfo, login, getPortalPasswordSettings, setNewEmail, logout } = store.auth.actions;
export const SET_IS_CONFIRM_LOADED = "SET_IS_CONFIRM_LOADED";
export function setIsConfirmLoaded(isConfirmLoaded) {
return {
type: SET_IS_CONFIRM_LOADED,
isConfirmLoaded
};
};
export function getConfirmationInfo(token) {
return dispatch => {
return getPortalPasswordSettings(dispatch, token)
.then(() => dispatch(setIsConfirmLoaded(true)));
};
};
export function createConfirmUser(registerData, loginData, key) {
const data = Object.assign({}, registerData, loginData);
return dispatch => {
return api.people
.createUser(data, key)
.then(user => dispatch(setCurrentUser(user)))
.then(() => api.user.login(loginData.userName, loginData.password))
.then(() => loadInitInfo(dispatch));
};
};
export function activateConfirmUser(
personalData,
loginData,
key,
userId,
activationStatus
) {
const changedData = {
id: userId,
FirstName: personalData.firstname,
LastName: personalData.lastname
};
return dispatch => {
return api.people
.changePassword(userId, loginData.password, key)
.then(data => {
return api.people.updateActivationStatus(activationStatus, userId, key);
})
.then(data => {
return dispatch(login(loginData.userName, loginData.password));
})
.then(data => {
return api.people.updateUser(changedData);
})
.then(user => dispatch(setCurrentUser(user)));
};
}
export function changeEmail(userId, email, key) {
return dispatch => {
return api.people
.changeEmail(userId, email, key)
.then(user => dispatch(setNewEmail(user.email)));
};
};
export function changePassword(userId, password, key) {
return dispatch => {
return api.people
.changePassword(userId, password, key)
.then(() => logout(dispatch));
};
};

View File

@ -0,0 +1,18 @@
import { SET_IS_CONFIRM_LOADED } from './actions';
const initialState = {
isConfirmLoaded: false,
};
const confirmReducer = (state = initialState, action) => {
switch (action.type) {
case SET_IS_CONFIRM_LOADED:
return Object.assign({}, state, {
isConfirmLoaded: action.isConfirmLoaded
});
default:
return state;
}
}
export default confirmReducer;

View File

@ -1,11 +1,13 @@
import { combineReducers } from 'redux';
import settingsReducer from './settings/reducer';
import confirmReducer from './confirm/reducer';
import { store } from 'asc-web-common';
const { reducer: authReducer } = store.auth;
const rootReducer = combineReducers({
auth: authReducer,
settings: settingsReducer
settings: settingsReducer,
confirm: confirmReducer
});
export default rootReducer;

View File

@ -1,7 +1,8 @@
import { api } from "asc-web-common";
import { api, store } from "asc-web-common";
import axios from "axios";
import { getSelectorOptions, getUserOptions } from "./selectors";
const { Filter } = api;
const { setPortalLanguageAndTime, setTimezones, setGreetingSettings } = store.auth.actions;
export const SET_USERS = "SET_USERS";
export const SET_ADMINS = "SET_ADMINS";
@ -178,3 +179,35 @@ export function getWhiteLabelLogoUrls() {
});
};
}
export function setLanguageAndTime(lng, timeZoneID) {
return dispatch => {
return api.settings
.setLanguageAndTime(lng, timeZoneID)
.then(() => dispatch(setPortalLanguageAndTime({ lng, timeZoneID })));
};
};
export function getPortalTimezones() {
return dispatch => {
return api.settings.getPortalTimezones().then(timezones => {
dispatch(setTimezones(timezones));
});
};
};
export function setGreetingTitle(greetingTitle) {
return dispatch => {
return api.settings.setGreetingSettings(greetingTitle).then(() => {
dispatch(setGreetingSettings(greetingTitle));
});
};
}
export function restoreGreetingTitle() {
return dispatch => {
return api.settings.restoreGreetingSettings().then(res => {
dispatch(setGreetingSettings(res.companyName));
});
};
}

File diff suppressed because it is too large Load Diff

View File

@ -4,5 +4,3 @@ 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

@ -19,6 +19,11 @@ module.exports = ({ config }) => {
config.devtool = 'cheap-module-source-map'; // TODO: should we use something differen?
}
config.resolve.alias = {
...config.resolve.alias,
"styled-components": path.resolve('./node_modules/styled-components'),
};
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.

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-common",
"version": "1.0.3",
"version": "1.0.14",
"description": "Ascensio System SIA common components and solutions library",
"license": "AGPL-3.0",
"files": [
@ -33,87 +33,84 @@
"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",
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-export-default-from": "^7.7.4",
"@babel/plugin-proposal-export-namespace-from": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.6",
"@babel/preset-react": "^7.7.4",
"@emotion/babel-preset-css-prop": "^10.0.23",
"@emotion/styled": "^10.0.23",
"@storybook/addon-a11y": "^5.2.8",
"@storybook/addon-actions": "^5.2.8",
"@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",
"@storybook/addon-knobs": "^5.2.8",
"@storybook/addon-links": "^5.2.8",
"@storybook/addon-options": "^5.2.8",
"@storybook/addon-storysource": "^5.2.8",
"@storybook/addon-viewport": "^5.2.8",
"@storybook/addons": "^5.2.8",
"@storybook/react": "^5.2.8",
"@svgr/rollup": "^4.3.3",
"@svgr/webpack": "^4.3.2",
"@testing-library/react": "^8.0.8",
"@types/jest": "^24.0.17",
"@svgr/webpack": "^4.3.3",
"@testing-library/react": "^9.3.2",
"@types/jest": "^24.0.23",
"asc-web-components": "file:../../packages/asc-web-components",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.8.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.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",
"cross-env": "^6.0.3",
"css-loader": "^3.2.1",
"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",
"enzyme-adapter-react-16": "^1.15.1",
"eslint": "^6.7.2",
"eslint-plugin-react": "^7.17.0",
"i18next": "19.0.1",
"jest": "^24.9.0",
"jest-enzyme": "^7.1.2",
"jest-junit": "^10.0.0",
"postcss": "^7.0.24",
"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": "^16.12.0",
"react-dom": "^16.12.0",
"react-i18next": "11.2.5",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-values": "^0.3.3",
"reactstrap": "8.0.1",
"rollup": "^1.21.1",
"rollup": "^1.27.9",
"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-generate-package-json": "^3.2.0",
"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",
"rollup-plugin-url": "^3.0.1",
"storybook-readme": "^5.0.8",
"styled-components": "^4.3.2",
"styled-components": "^4.4.1",
"svg-inline-loader": "^0.8.0"
},
"peerDependencies": {
"asc-web-components": "file:../../packages/asc-web-components",
"bootstrap": "^4.3.1",
"i18next": "17.0.12",
"i18next": "19.0.1",
"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": "^16.12.0",
"react-dom": "^16.12.0",
"react-i18next": "11.2.5",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-values": "^0.3.3",
"reactstrap": "8.0.1",
"styled-components": "^4.3.2"
"styled-components": "^4.4.1"
},
"resolutions": {
"js-yaml": "3.13.1"

View File

@ -21,6 +21,12 @@ export function getUserList(filter = Filter.getDefault()) {
url: `/people/${userName || '@self'}.json`
});
}
export function getUserPhoto(userId) {
return request({
method: "get",
url: `/people/${userId}/photo`
});
}
export function createUser(data, confirmKey = null) {
const options = {

View File

@ -1,4 +1,5 @@
export { default as PrivateRoute } from "./routing/privateRoute";
export { default as PublicRoute } from "./routing/publicRoute";
export { default as Login } from "./login";
export { default as StudioLayout } from "./layout";
export { default as StudioLayout } from "./layout";
export { default as ModuleTile } from "./module-tile";

View File

@ -1,12 +1,12 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { Collapse } from "reactstrap";
import {
Button,
TextInput,
PageLayout,
Text,
Heading,
Link,
toastr,
Checkbox,
@ -20,46 +20,49 @@ import SubModalDialog from "./sub-components/modal-dialog";
import { login, setIsLoaded } from "../../store/auth/actions";
import { sendInstructionsToChangePassword } from "../../api/people";
const FormContainer = styled.div`
const FormContainer = styled.form`
margin: 50px auto 0 auto;
max-width: 432px;
.login-header {
margin-bottom: 24px;
.login-logo {
max-width: 216px;
max-height: 35px;
}
.login-title {
word-wrap: break-word;
margin: 8px 0;
text-align: left;
font-size: 24px;
color: #116d9d;
}
margin-bottom: 24px;
}
.login-input {
margin-bottom: 24px;
}
.login-link {
float: right;
line-height: 16px;
}
.login-forgot-wrapper {
height: 36px;
.login-checkbox {
float: left;
span {
font-size: 12px;
.login-checkbox-wrapper {
position: absolute;
display: inline-flex;
.login-checkbox {
float: left;
span {
font-size: 12px;
}
}
.login-tooltip {
margin-left: 3px;
display: inline-flex;
}
}
.login-link {
float: right;
line-height: 16px;
}
}
.login-tooltip {
margin-left: 3px;
display: inline-flex;
}
.login-button {
@ -179,9 +182,10 @@ class Form extends Component {
componentDidMount() {
const { language, match } = this.props;
const { params } = match;
const { error, confirmedEmail } = match.params;
i18n.changeLanguage(language);
params.error && this.setState({ errorText: params.error });
error && this.setState({ errorText: error });
confirmedEmail && this.setState({ identifier: confirmedEmail });
window.addEventListener("keyup", this.onKeyPress);
}
@ -215,7 +219,9 @@ class Form extends Component {
src="images/dark_general.png"
alt="Logo"
/>
<div className="login-title">{greetingTitle}</div>
<Heading className="login-title" color="#116d9d">
{greetingTitle}
</Heading>
</div>
<TextInput
@ -244,7 +250,6 @@ class Form extends Component {
placeholder={t("Password")}
size="huge"
scale={true}
isAutoFocussed={true}
tabIndex={2}
isDisabled={isLoading}
autoComplete="current-password"
@ -253,8 +258,8 @@ class Form extends Component {
className="login-input"
/>
<div style={{ height: 30 }}>
<div style={{ position: "absolute", display: "inline-flex" }}>
<div className="login-forgot-wrapper">
<div className="login-checkbox-wrapper">
<Checkbox
className="login-checkbox"
isChecked={isChecked}
@ -265,7 +270,7 @@ class Form extends Component {
className="login-tooltip"
helpButtonHeaderContent={t("CookieSettingsTitle")}
tooltipContent={
<Text.Body fontSize={12}>{t("RememberHelper")}</Text.Body>
<Text fontSize={12}>{t("RememberHelper")}</Text>
}
/>
</div>
@ -294,6 +299,7 @@ class Form extends Component {
) : null}
<Button
id="button"
className="login-button"
primary
size="big"
@ -305,13 +311,13 @@ class Form extends Component {
/>
{params.confirmedEmail && (
<Text.Body isBold={true} fontSize={16}>
<Text isBold={true} fontSize={16}>
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
</Text.Body>
</Text>
)}
<Collapse isOpen={!!errorText}>
<div className="alert alert-danger">{errorText}</div>
</Collapse>
<Text fontSize={14} color="#c30">
{errorText}
</Text>
</FormContainer>
);
}

View File

@ -17,19 +17,19 @@ class SubModalDialog extends React.Component {
<ModalDialog
visible={openDialog}
headerContent={
<Text.Body isBold={false} fontSize={21}>
<Text isBold={false} fontSize={21}>
{t("PasswordRecoveryTitle")}
</Text.Body>
</Text>
}
bodyContent={[
<Text.Body
<Text
key="text-body"
className="text-body"
isBold={false}
fontSize={13}
>
{t("MessageSendPasswordRecoveryInstructionsOnEmail")}
</Text.Body>,
</Text>,
<TextInput
key="e-mail"
id="e-mail"

View File

@ -0,0 +1,34 @@
# ModuleTile
Module tile is used for navigation to module page
### Usage
```js
import { ModuleTile } from "asc-web-common";
```
```jsx
<ModuleTile
title="Documents"
imageUrl="./modules/documents240.png"
link="/products/files/"
description="Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed."
isPrimary={true}
onClick={action("onClick")}
/>
```
### Properties
| Props | Type | Required | Values | Default | Description |
| ------------- | :------------: | :------: | :----: | :-----: | --------------------------------------- |
| `className` | `string` | - | - | - | Accepts class |
| `description` | `string` | - | - | - | Description of primary tile |
| `id` | `string` | - | - | - | Accepts id |
| `imageUrl` | `string` | - | - | - | Image url/path |
| `isPrimary` | `bool` | - | - | - | Tells when the tile should be primary |
| `link` | `string` | - | - | - | Link to return on onClick |
| `onClick` | `func` | ✅ | - | - | What the tile will trigger when clicked |
| `style` | `obj`, `array` | - | - | - | Accepts css style |
| `title` | `string` | - | - | - | Title of tile |

View File

@ -0,0 +1,149 @@
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { Text } from "asc-web-components";
const TitleContainer = styled.div`
width: auto;
&:hover {
.selectable {
text-decoration: underline;
}
}
.title-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
.title-image-wrapper {
padding: 0 16px;
position: relative;
@media (min-width: 768px) {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.title-image {
border: none;
height: 241px;
width: 240px;
cursor: pointer;
}
}
.title-text-wrapper {
padding: 0 16px;
position: relative;
width: 100%;
@media (min-width: 768px) {
flex: 0 0 auto;
width: auto;
max-width: 50%;
}
}
.title-text {
flex: 1 1 auto;
padding: 1.25rem;
.title-text-header {
margin: 46px 0 11px 0;
cursor: pointer;
}
.title-text-description {
line-height: 20px;
}
}
}
.sub-title-content {
text-align: center;
flex: 1 1 auto;
padding: 1.25rem;
cursor: pointer;
.sub-title-image {
border: none;
height: 100px;
width: 100px;
}
.sub-title-text {
margin: 16px 0 16px 0;
text-align: center;
}
}
`;
const ModuleTile = props => {
// console.log("ModuleTile render", props);
const { title, imageUrl, link, description, isPrimary, onClick } = props;
const handleClick = (e, link) => onClick && onClick(e, link);
return (
<TitleContainer>
{isPrimary ? (
<div className="title-content">
<div className="title-image-wrapper">
<img
className="title-image selectable"
src={imageUrl}
onClick={handleClick.bind(link)}
/>
</div>
<div className="title-text-wrapper">
<div onClick={handleClick.bind(link)} className="title-text">
<Text fontSize={36} className="title-text-header selectable">
{title}
</Text>
<Text fontSize={12} className="title-text-description">
{description}
</Text>
</div>
</div>
</div>
) : (
<div className="sub-title-content selectable">
<div>
<img
className="sub-title-image"
src={imageUrl}
onClick={handleClick.bind(link)}
/>
</div>
<div>
<div>
<Text
fontSize={18}
className="sub-title-text"
onClick={handleClick.bind(link)}
>
{title}
</Text>
</div>
</div>
</div>
)}
</TitleContainer>
);
};
ModuleTile.propTypes = {
title: PropTypes.string.isRequired,
imageUrl: PropTypes.string.isRequired,
link: PropTypes.string.isRequired,
description: PropTypes.string,
isPrimary: PropTypes.bool,
onClick: PropTypes.func.isRequired
};
ModuleTile.defaultProps = {
isPrimary: false,
description: ""
};
export default ModuleTile;

View File

@ -0,0 +1,24 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Section from '../../../.storybook/decorators/section';
import ModuleTile from '../module-tile';
import withReadme from 'storybook-readme/with-readme';
import { withKnobs, boolean, text } from '@storybook/addon-knobs/react';
import Readme from './README.md';
storiesOf('Components|ModuleTile', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('base', () => (
<Section>
<ModuleTile
title={text("title", "Documents")}
imageUrl="./modules/documents240.png"
link="/products/files/"
description={text("description", "Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed.")}
isPrimary={boolean("isPrimary", true)}
onClick={action("onClick")}
/>
</Section>
));

View File

@ -0,0 +1,61 @@
import React from 'react';
import { mount } from 'enzyme';
import ModuleTile from './';
const baseProps = {
title: "Documents",
imageUrl: "./modules/documents240.png",
link: "/products/files/",
description: "Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed.",
isPrimary: true
}
describe('<ModuleTile />', () => {
it('renders without error', () => {
const wrapper = mount(<ModuleTile {...baseProps} />);
expect(wrapper).toExist();
});
it('accepts id', () => {
const wrapper = mount(
<ModuleTile {...baseProps} id="testId" />
);
expect(wrapper.prop('id')).toEqual('testId');
});
it('accepts className', () => {
const wrapper = mount(
<ModuleTile {...baseProps} className="test" />
);
expect(wrapper.prop('className')).toEqual('test');
});
it('accepts style', () => {
const wrapper = mount(
<ModuleTile {...baseProps} style={{ color: 'red' }} />
);
expect(wrapper.getDOMNode().style).toHaveProperty('color', 'red');
});
it('added onClick prop', () => {
const onClick = jest.fn();
const wrapper = mount(
<ModuleTile {...baseProps} onClick={onClick} />
);
expect(wrapper.prop('onClick')).toEqual(onClick);
});
it('not isPrimary prop', () => {
const wrapper = mount(
<ModuleTile {...baseProps} isPrimary={false} />
);
expect(wrapper.prop('isPrimary')).toEqual(false);
});
});

View File

@ -7,7 +7,6 @@ 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";
@ -15,7 +14,6 @@ export const GET_TIMEZONES = "GET_TIMEZONES";
export const SET_CURRENT_PRODUCT_ID = "SET_CURRENT_PRODUCT_ID";
export const SET_CURRENT_PRODUCT_HOME_PAGE = "SET_CURRENT_PRODUCT_HOME_PAGE";
export const SET_GREETING_SETTINGS = "SET_GREETING_SETTINGS";
export const SET_INVITE_LINKS = "SET_INVITE_LINKS";
export function setCurrentUser(user) {
return {
@ -45,12 +43,6 @@ export function setIsLoaded(isLoaded) {
};
}
export function setIsConfirmLoaded(isConfirmLoaded) {
return {
type: SET_IS_CONFIRM_LOADED,
isConfirmLoaded
};
}
export function setLogout() {
return {
@ -65,16 +57,6 @@ export function setPasswordSettings(passwordSettings) {
};
}
export function setInviteLinks(userLink, guestLink) {
return {
type: SET_INVITE_LINKS,
payload: {
userLink,
guestLink
}
};
}
export function setNewEmail(email) {
return {
type: SET_NEW_EMAIL,
@ -140,7 +122,7 @@ export function getModules(dispatch) {
.then(modules => dispatch(setModules(modules)));
}
const loadInitInfo = dispatch => {
export const loadInitInfo = dispatch => {
return getPortalSettings(dispatch).then(() => getModules(dispatch));
};
@ -160,74 +142,6 @@ export function logout() {
};
}
export function getConfirmationInfo(token, type) {
return dispatch => {
return api.settings
.getPortalPasswordSettings(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.people
.createUser(data, key)
.then(user => dispatch(setCurrentUser(user)))
.then(() => api.user.login(loginData.userName, loginData.password))
.then(() => loadInitInfo(dispatch));
};
}
export function changePassword(userId, password, key) {
return dispatch => {
return api.people
.changePassword(userId, password, key)
.then(() => logout(dispatch));
};
}
export function changeEmail(userId, email, key) {
return dispatch => {
return api.people
.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.people
.changePassword(userId, loginData.password, key)
.then(data => {
console.log("set password success:", data);
return api.people.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.people.updateUser(changedData);
})
.then(user => dispatch(setCurrentUser(user)));
};
}
export function getPortalCultures(dispatch = null) {
return dispatch
? api.settings.getPortalCultures().then(cultures => {
@ -240,51 +154,8 @@ export function getPortalCultures(dispatch = null) {
};
}
export function setLanguageAndTime(lng, timeZoneID) {
return dispatch => {
return api.settings
.setLanguageAndTime(lng, timeZoneID)
.then(() => dispatch(setPortalLanguageAndTime({ lng, timeZoneID })));
};
}
export function getPortalTimezones() {
return dispatch => {
return api.settings.getPortalTimezones().then(timezones => {
dispatch(setTimezones(timezones));
});
};
}
export function setGreetingTitle(greetingTitle) {
return dispatch => {
return api.settings.setGreetingSettings(greetingTitle).then(() => {
dispatch(setGreetingSettings(greetingTitle));
});
};
}
export function restoreGreetingTitle() {
return dispatch => {
return api.settings.restoreGreetingSettings().then(res => {
dispatch(setGreetingSettings(res.companyName));
});
};
}
export function getPortalPasswordSettings(dispatch, confirmKey = null) {
return api.settings.getPortalPasswordSettings(confirmKey).then(settings => {
dispatch(setPasswordSettings(settings));
});
}
export function getPortalInviteLinks() {
return (dispatch, getState) => {
const { auth } = getState();
if (!auth.user.isAdmin) return Promise.resolve();
return api.portal.getInvitationLinks().then(data => {
dispatch(setInviteLinks(data.userLink, data.guestLink));
});
};
}

View File

@ -1,14 +1,12 @@
import {
SET_CURRENT_USER, SET_MODULES, SET_SETTINGS, SET_IS_LOADED, LOGOUT, SET_PASSWORD_SETTINGS, SET_IS_CONFIRM_LOADED, SET_NEW_EMAIL,
SET_CURRENT_USER, SET_MODULES, SET_SETTINGS, SET_IS_LOADED, LOGOUT, SET_PASSWORD_SETTINGS, SET_NEW_EMAIL,
GET_PORTAL_CULTURES, SET_PORTAL_LANGUAGE_AND_TIME, GET_TIMEZONES, SET_CURRENT_PRODUCT_ID, SET_CURRENT_PRODUCT_HOME_PAGE, SET_GREETING_SETTINGS,
SET_INVITE_LINKS
} from './actions';
import isEmpty from "lodash/isEmpty";
const initialState = {
isAuthenticated: false,
isLoaded: false,
isConfirmLoaded: false,
user: {},
modules: [],
settings: {
@ -57,18 +55,10 @@ const authReducer = (state = initialState, action) => {
return Object.assign({}, state, {
settings: { ...state.settings, passwordSettings: action.passwordSettings }
});
case SET_INVITE_LINKS:
return Object.assign({}, state, {
settings: { ...state.settings, inviteLinks: action.payload }
});
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 }

File diff suppressed because it is too large Load Diff

View File

@ -4,5 +4,35 @@ 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';
body {
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
margin: 0;
font-size: 1rem;
text-align: left;
}
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
h5, .h5 {
font-size: 1.25rem;
}
.Toastify__toast-container--top-center {
top: 1em;
left: 50%;
margin-left: -160px;
}

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-components",
"version": "1.0.199",
"version": "1.0.224",
"description": "Ascensio System SIA component library",
"license": "AGPL-3.0",
"main": "dist/asc-web-components.js",
@ -36,86 +36,85 @@
"styled-components": "^4.3.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",
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-export-default-from": "^7.7.4",
"@babel/plugin-proposal-export-namespace-from": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.6",
"@babel/preset-react": "^7.7.4",
"@emotion/babel-preset-css-prop": "^10.0.23",
"@emotion/styled": "^10.0.23",
"@storybook/addon-a11y": "^5.2.8",
"@storybook/addon-actions": "^5.2.8",
"@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",
"@storybook/addon-knobs": "^5.2.8",
"@storybook/addon-links": "^5.2.8",
"@storybook/addon-options": "^5.2.8",
"@storybook/addon-storysource": "^5.2.8",
"@storybook/addon-viewport": "^5.2.8",
"@storybook/addons": "^5.2.8",
"@storybook/react": "^5.2.8",
"@svgr/rollup": "^4.3.3",
"@svgr/webpack": "^4.3.2",
"@testing-library/react": "^8.0.8",
"@types/jest": "^24.0.17",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.8.0",
"@svgr/webpack": "^4.3.3",
"@testing-library/react": "^9.3.2",
"@types/jest": "^24.0.23",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.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",
"cross-env": "^5.2.0",
"css-loader": "^3.2.0",
"cross-env": "^6.0.3",
"css-loader": "^3.2.1",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"eslint": "^6.3.0",
"eslint-plugin-react": "^7.14.3",
"jest": "^24.8.0",
"jest-enzyme": "^7.1.0",
"jest-junit": "^8.0.0",
"postcss": "^7.0.17",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"enzyme-adapter-react-16": "^1.15.1",
"eslint": "^6.7.2",
"eslint-plugin-react": "^7.17.0",
"jest": "^24.9.0",
"jest-enzyme": "^7.1.2",
"jest-junit": "^10.0.0",
"postcss": "^7.0.24",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-values": "^0.3.3",
"rollup": "^1.21.1",
"rollup": "^1.27.9",
"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-generate-package-json": "^3.2.0",
"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",
"rollup-plugin-url": "^3.0.1",
"storybook-readme": "^5.0.8",
"styled-components": "^4.3.2",
"styled-components": "^4.4.1",
"svg-inline-loader": "^0.8.0"
},
"dependencies": {
"bootstrap": "^4.3.1",
"email-addresses": "^3.0.3",
"email-addresses": "^3.1.0",
"html-to-react": "^1.4.2",
"lodash": "4.17.15",
"lodash-es": "4.17.15",
"moment": "^2.24.0",
"prop-types": "^15.7.2",
"punycode": "^2.1.1",
"rc-tree": "^2.1.2",
"rc-tree": "^2.1.3",
"react-autosize-textarea": "^7.0.0",
"react-avatar-edit": "^0.8.3",
"react-avatar-editor": "^11.0.7",
"react-custom-scrollbars": "^4.2.1",
"react-dropzone": "^10.1.8",
"react-dropzone": "^10.2.1",
"react-lifecycles-compat": "^3.0.4",
"react-text-mask": "^5.4.3",
"react-toastify": "^5.3.2",
"react-toastify": "^5.4.1",
"react-tooltip": "^3.11.1",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"reactstrap": "^8.0.1"
"react-window": "^1.8.5"
},
"resolutions": {
"js-yaml": "3.13.1"

View File

@ -9,7 +9,7 @@ import Checkbox from "../checkbox";
import Button from "../button";
import { Icons } from "../icons";
import ComboBox from "../combobox";
import { Text } from "../text";
import Text from "../text";
import findIndex from "lodash/findIndex";
import filter from "lodash/filter";
import isEqual from "lodash/isEqual";
@ -116,6 +116,7 @@ const StyledBodyContainer = styled(Container)`
}
.data_column_one {
box-sizing: border-box;
${props =>
props.displayType === "dropdown" &&
props.groups &&
@ -130,6 +131,7 @@ const StyledBodyContainer = styled(Container)`
margin-top: 4px;
margin-left: -8px;
.option {
box-sizing: border-box;
line-height: 32px;
padding-left: ${props => (props.isMultiSelect ? 8 : 0)}px;
cursor: pointer;
@ -150,6 +152,7 @@ const StyledBodyContainer = styled(Container)`
}
.data_column_two {
box-sizing: border-box;
${props =>
props.displayType === "dropdown" &&
props.groups &&
@ -171,6 +174,7 @@ const StyledBodyContainer = styled(Container)`
margin-left: 8px;
.option {
box-sizing: border-box;
line-height: 32px;
padding-left: 8px;
cursor: pointer;
@ -511,14 +515,14 @@ class AdvancedSelector extends React.Component {
groups &&
groups.length > 0 && (
<div className="data_column_two">
<Text.Body
<Text
as="p"
className="group_header"
fontSize={15}
isBold={true}
>
Groups
</Text.Body>
</Text>
<FixedSizeList
className="group_list"
height={listHeight}

View File

@ -0,0 +1,530 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { BooleanValue, StringValue } from "react-values";
import Section from "../../../.storybook/decorators/section";
import Avatar from "../avatar";
import Button from "../button";
//import HelpButton from "../help-button";
//import IconButton from "../icon-button";
import ToggleButton from "../toggle-button";
import Calendar from "../calendar";
import Checkbox from "../checkbox";
import ComboBox from "../combobox";
import InputBlock from "../input-block";
import RadioButtonGroup from "../radio-button-group";
import TextInput from "../text-input";
import Textarea from "../textarea";
import ContextMenuButton from "../context-menu-button";
import DatePicker from "../date-picker";
import FieldContainer from "../field-container";
import Header from "../header";
import Heading from "../heading";
import Link from "../link";
import Loader from "../loader";
import Row from "../row";
import Scrollbar from "../scrollbar";
import TabContainer from "../tabs-container";
import Text from "../text";
import Toast from "../toast";
import toastr from "../toast/toastr";
import ToggleContent from "../toggle-content";
import Tooltip from "../tooltip";
import { Icons } from "../icons";
const array_items = [
{
key: "0",
title: "Tab 1",
content: <div>Tab 1 content</div>
},
{
key: "1",
title: "Tab 2",
content: <div>Tab 2 content</div>
},
{
key: "2",
title: "Tab 3",
content: <div>Tab 3 content</div>
}
];
const options = [
{
key: 0,
icon: "CatalogEmployeeIcon", // optional item
label: "Option 1",
disabled: false, // optional item
onClick: () => {} // optional item
},
{
key: 1,
icon: "CatalogEmployeeIcon", // optional item
label: "Option 2",
disabled: false, // optional item
onClick: () => {} // optional item
},
{
key: 2,
icon: "CatalogEmployeeIcon", // optional item
label: "Option 3",
disabled: true, // optional item
onClick: () => {} // optional item
},
{
key: 3,
icon: "CatalogEmployeeIcon", // optional item
label: "Option 4",
disabled: false, // optional item
onClick: () => {} // optional item
}
];
const arrayUsers = [
{ key: "user_1", name: "Bob", email: "Bob@gmail.com", position: "developer" },
{
key: "user_2",
name: "John",
email: "John@gmail.com",
position: "developer"
},
{
key: "user_3",
name: "Kevin",
email: "Kevin@gmail.com",
position: "developer"
}
];
const element = "Icon";
const elementAvatar = (
<Avatar size="small" role="user" userName="Demo Avatar" />
);
const elementIcon = <Icons.CatalogFolderIcon size="big" />;
const elementComboBox = (
<ComboBox
options={[
{ key: 1, icon: "ItemActiveIcon", label: "Open" },
{ key: 2, icon: "CheckIcon", label: "Closed" }
]}
onSelect={option => console.log(option)}
selectedOption={{
key: 0,
icon: "ItemActiveIcon",
label: ""
}}
scaled={false}
size="content"
isDisabled={false}
/>
);
const checkedProps = { checked: false };
const getElementProps = element =>
element === "Avatar"
? { element: elementAvatar }
: element === "Icon"
? { element: elementIcon }
: element === "ComboBox"
? { element: elementComboBox }
: {};
const elementProps = getElementProps(element);
const rowContent = (
<>
<Row
key="1"
{...checkedProps}
{...elementProps}
contextOptions={[
{
key: "key1",
label: "Edit",
onClick: () => console.log("Context action: Edit")
},
{
key: "key2",
label: "Delete",
onClick: () => console.log("Context action: Delete")
}
]}
>
<Text truncate={true}>Sample text</Text>
</Row>
</>
);
let rowCount = 5;
const rowArray = [];
while (rowCount != 0) {
rowArray.push(rowContent);
rowCount--;
}
storiesOf("Components|All", module)
.addParameters({ options: { showAddonPanel: false } })
.add("all", () => (
<Section>
<div
style={{
display: "grid",
gridGap: 15,
//gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))"
gridTemplateColumns: "repeat(auto-fill, 300px)"
}}
>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<Heading level={1} title="Some title">
Heading text
</Heading>
</div>
<div style={{ padding: "8px 0" }}>
<Header type="content" title="Some title" isInline>
Header text
</Header>
</div>
<div style={{ padding: "8px 0" }}>
<Text as="p" title="Some title">
Text as "p"
</Text>
</div>
<div style={{ padding: "8px 0" }}>
<Link type="page" href="https://github.com">
Black page link
</Link>
<br />
<Link type="page" href="https://github.com" isHovered={true}>
Black hovered page link
</Link>
<br />
<Link type="action">Black action link</Link>
<br />
<Link type="action" isHovered={true}>
Black hovered action link
</Link>
</div>
<div style={{ padding: "24px 0 8px 0" }}>
<Link data-for="group" data-tip={0}>
Bob
</Link>
<br />
<Link data-for="group" data-tip={1}>
John
</Link>
<br />
<Link data-for="group" data-tip={2}>
Kevin
</Link>
<Tooltip
id="group"
offsetRight={90}
getContent={dataTip =>
dataTip ? (
<div>
<Text isBold={true} fontSize={16}>
{arrayUsers[dataTip].name}
</Text>
<Text color="#A3A9AE" fontSize={13}>
{arrayUsers[dataTip].email}
</Text>
<Text fontSize={13}>{arrayUsers[dataTip].position}</Text>
</div>
) : null
}
/>
</div>
<div style={{ padding: "8px 0" }}>
<div style={{ display: "flex" }}>
<div style={{ marginRight: 16 }}>
<Button
size="big"
isDisabled={false}
onClick={() => {}}
label="Button"
primary
/>
</div>
<Button
size="big"
isDisabled={false}
onClick={() => {}}
label="Button"
/>
</div>
</div>
<div style={{ padding: "8px 0" }}>
<Toast />
<Button
label="Show toastr"
onClick={() =>
toastr.success(
"Some text for toast",
"Some text for title",
true
)
}
/>
</div>
{/*
<div style={{ padding: "8px 0" }}>
<ContextMenuButton
iconName="VerticalDotsIcon"
size={16}
color="#A3A9AE"
isDisabled={false}
title="Actions"
getData={() => [
{
key: "key",
label: "label",
onClick: () => {}
}
]}
/>
</div>
*/}
{/*<div style={{ padding: "8px 0" }}>
<div style={{ display: "flex" }}>
<div style={{ marginRight: 16 }}>
<IconButton
size="25"
isDisabled={false}
onClick={() => {}}
iconName={"SearchIcon"}
isFill={true}
isClickable={false}
/>
</div>
<HelpButton tooltipContent="Paste you tooltip content here" />
</div>
</div>
*/}
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<ComboBox
options={options}
isDisabled={false}
selectedOption={{
key: 0,
label: "Select"
}}
dropDownMaxHeight={200}
noBorder={false}
scaledOptions={true}
size="content"
onSelect={option => console.log("selected", option)}
/>
</div>
<div style={{ padding: "8px 0" }}>
<StringValue>
{({ value, set }) => (
<TextInput
placeholder="Add input text"
value={value}
onChange={e => set(e.target.value)}
/>
)}
</StringValue>
</div>
<div style={{ padding: "8px 0" }}>
<StringValue>
{({ value, set }) => (
<InputBlock
placeholder="Add input text"
iconName={"SearchIcon"}
onIconClick={() => {}}
onChange={e => set(e.target.value)}
value={value}
>
<Icons.SettingsIcon size="medium" />
</InputBlock>
)}
</StringValue>
</div>
<div style={{ padding: "8px 0" }}>
<DatePicker
onChange={date => {
console.log("Selected date", date);
}}
selectedDate={new Date()}
minDate={new Date("1970/01/01")}
maxDate={new Date(new Date().getFullYear() + 1 + "/01/01")}
isDisabled={false}
isReadOnly={false}
hasError={false}
isOpen={false}
themeColor="#ED7309"
locale="en"
/>
</div>
<div style={{ padding: "8px 0" }}>
<StringValue>
{({ value, set }) => (
<Textarea
placeholder="Add comment"
onChange={event => set(event.target.value)}
value={value}
/>
)}
</StringValue>
</div>
<div style={{ padding: "8px 0" }}>
<StringValue>
{({ value, set }) => (
<FieldContainer
place="top"
isRequired
tooltipContent="Paste you tooltip content here"
labelText="Name:"
>
<TextInput
value={value}
onChange={e => set(e.target.value)}
/>
</FieldContainer>
)}
</StringValue>
</div>
<div style={{ padding: "8px 0" }}>
<RadioButtonGroup
name="fruits"
selected="banana"
options={[
{ value: "apple", label: "Sweet apple" },
{ value: "banana", label: "Banana" },
{ value: "Mandarin" }
]}
/>
</div>
<div style={{ padding: "8px 0" }}>
<BooleanValue>
{({ value, toggle }) => (
<div style={{ display: "flex" }}>
<div style={{ marginRight: 24 }}>
<Checkbox
id="id"
name="name"
value="value"
label="Checkbox"
isChecked={value}
isIndeterminate={false}
isDisabled={false}
onChange={e => toggle(e.target.checked)}
/>
</div>
<Checkbox
id="id"
name="name"
value="value"
label="Checkbox indeterminate"
isIndeterminate={true}
isDisabled={false}
onChange={() => {}}
/>
</div>
)}
</BooleanValue>
</div>
<div style={{ padding: "8px 0 24px 0" }}>
<BooleanValue>
{({ value, toggle }) => (
<ToggleButton
label="Toggle button"
onChange={e => toggle(e.target.checked)}
isChecked={value}
/>
)}
</BooleanValue>
</div>
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<Calendar
onChange={() => {}}
disabled={false}
themeColor="#ED7309"
selectedDate={new Date()}
openToDate={new Date()}
minDate={new Date("1970/01/01")}
maxDate={new Date("3000/01/01")}
locale="ru"
/>
</div>
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
{rowArray[0]}
{rowArray.map((item, index) => {
return <div key={index}>{item}</div>;
})}
</div>
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<Avatar
size="max"
role="admin"
source=""
userName=""
editing={false}
/>
</div>
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<Loader type="base" color="black" size={30} label="Loading..." />
</div>
<div style={{ padding: "8px 0", marginLeft: 45 }}>
<Loader type="oval" color="black" size={30} label="Loading" />
</div>
<div style={{ padding: "8px 0", marginLeft: 45 }}>
<Loader type="dual-ring" color="black" size={30} label="Loading" />
</div>
</div>
<div style={{ justifySelf: "center" }}>
<div style={{ padding: "8px 0" }}>
<Scrollbar stype="mediumBlack" style={{ width: 300, height: 200 }}>
================================================================
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
================================================================
</Scrollbar>
</div>
</div>
<div>
<div style={{ padding: "8px 0" }}>
<TabContainer>{array_items}</TabContainer>
</div>
<div style={{ padding: "16px 0" }}>
<ToggleContent label="ToggleContent">
<span>Toggle content text</span>
</ToggleContent>
</div>
</div>
</div>
</Section>
));

View File

@ -46,3 +46,6 @@ import { AvatarEditor } from "asc-web-components";
| `onDeleteImage` | `function` | - | - | - | Image deletion event |
| `onLoadFile` | `function` | - | - | - | Image upload event |
| `onImageChange` | `function` | - | - | - | Image change event |
| `className` | `string` | - | - | - | Accepts class |
| `id` | `string` | - | - | - | Accepts id |
| `style` | `obj`, `array` | - | - | - | Accepts css style |

View File

@ -1,16 +1,178 @@
import React from 'react';
import { mount } from 'enzyme';
import { mount, shallow } from 'enzyme';
import AvatarEditor from '.';
const baseProps = {
visible: true,
headerLabel: 'test',
chooseFileLabel: 'test',
saveButtonLabel: 'test',
maxSizeFileError: 'test',
image: '',
maxSize: 1,
accept: ['image/png', 'image/jpeg'],
unknownTypeError: 'test',
unknownError: 'test',
displayType: 'auto'
};
describe('<AvatarEditor />', () => {
it('renders without error', () => {
const wrapper = mount(
<AvatarEditor
visible={true}
onSave={(data) =>{console.log(data.croppedImage, data.defaultImage)}}
/>
<AvatarEditor {...baseProps} />
);
expect(wrapper).toExist();
});
it('accepts id', () => {
const wrapper = mount(
<AvatarEditor {...baseProps} id="testId" />
);
expect(wrapper.prop('id')).toEqual('testId');
});
it('accepts className', () => {
const wrapper = mount(
<AvatarEditor {...baseProps} className="test" />
);
expect(wrapper.prop('className')).toEqual('test');
});
it('accepts style', () => {
const wrapper = mount(
<AvatarEditor {...baseProps} style={{color: 'red'}} />
);
expect(wrapper.getDOMNode().style).toHaveProperty('color', 'red');
});
it('componentDidUpdate() props lifecycle test', () => {
const wrapper = shallow(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.componentDidUpdate({visible: false}, wrapper.state());
instance.componentDidUpdate({visible: true}, wrapper.state());
expect(wrapper.props()).toBe(wrapper.props());
});
it('causes function onClose()', () => {
const onClose = jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onClose={onClose} />);
const instance = wrapper.instance();
instance.onClose();
expect(wrapper.state('visible')).toBe(false);
});
it('causes function onSaveButtonClick()', () => {
const onSave = jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onSave={onSave} />);
const instance = wrapper.instance();
wrapper.setState({ isContainsFile: false });
instance.onSaveButtonClick();
wrapper.setState({ isContainsFile: true });
instance.onSaveButtonClick();
expect(wrapper.state('visible')).toBe(true);
});
it('causes function onImageChange()', () => {
const fileString = '';
const onImageChange= jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onImageChange={onImageChange} />);
const instance = wrapper.instance();
instance.onImageChange(fileString);
expect(onImageChange).toHaveBeenCalled();
});
it('causes function onImageChange() no onImageChange', () => {
const fileString = '';
const wrapper = mount(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.onImageChange(fileString);
expect(wrapper.state('croppedImage')).toBe(fileString);
});
it('causes function onDeleteImage()', () => {
const onDeleteImage= jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onDeleteImage={onDeleteImage} />);
const instance = wrapper.instance();
instance.onDeleteImage();
expect(onDeleteImage).toHaveBeenCalled();
});
it('causes function onDeleteImage() no onDeleteImage', () => {
const wrapper = mount(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.onDeleteImage();
expect(wrapper.state('isContainsFile')).toBe(false);
});
it('causes function onPositionChange()', () => {
const data = {test: 'test'};
const wrapper = mount(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.onPositionChange(data);
expect(wrapper.state('test')).toBe('test');
});
it('causes function onLoadFileError()', () => {
const onLoadFileError= jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onLoadFileError={onLoadFileError} />);
const instance = wrapper.instance();
instance.onLoadFileError();
expect(onLoadFileError).toHaveBeenCalled();
});
it('causes function onLoadFileError() no onLoadFileError', () => {
const wrapper = mount(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.onLoadFileError();
expect(wrapper.state('isContainsFile')).toBe(false);
});
it('causes function onLoadFile()', () => {
const file = 'test';
const onLoadFile= jest.fn();
const wrapper = mount(<AvatarEditor {...baseProps} onLoadFile={onLoadFile} />);
const instance = wrapper.instance();
instance.onLoadFile(file);
expect(onLoadFile).toHaveBeenCalled();
expect(wrapper.state('isContainsFile')).toBe(true);
});
it('causes function onLoadFile() no onLoadFile', () => {
const wrapper = mount(<AvatarEditor {...baseProps} />);
const instance = wrapper.instance();
instance.onLoadFile();
expect(wrapper.state('isContainsFile')).toBe(true);
});
});

View File

@ -74,6 +74,9 @@ class AvatarEditor extends React.Component {
if (this.props.visible !== prevProps.visible) {
this.setState({ visible: this.props.visible });
}
if (this.props.image !== prevProps.image) {
this.setState({ isContainsFile: !!this.props.image });
}
}
render() {
@ -111,6 +114,9 @@ class AvatarEditor extends React.Component {
/>
]}
onClose={this.onClose}
className={this.props.className}
id={this.props.id}
style={this.props.style}
/>
);
}
@ -133,7 +139,10 @@ AvatarEditor.propTypes = {
onLoadFileError: PropTypes.func,
unknownTypeError: PropTypes.string,
unknownError: PropTypes.string,
displayType: PropTypes.oneOf(['auto', 'modal', 'aside'])
displayType: PropTypes.oneOf(['auto', 'modal', 'aside']),
className: PropTypes.string,
id: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
};
AvatarEditor.defaultProps = {

View File

@ -5,7 +5,7 @@ import ReactAvatarEditor from 'react-avatar-editor'
import PropTypes from 'prop-types'
import { default as ASCAvatar } from '../../avatar/index'
import accepts from 'attr-accept'
import { Text } from '../../text'
import Text from '../../text'
import { tablet } from '../../../utils/device';
const StyledErrorContainer = styled.div`
@ -42,6 +42,7 @@ const CloseButton = styled.a`
`;
const DropZoneContainer = styled.div`
box-sizing: border-box;
display: block;
width: 100%;
height: 300px;
@ -159,10 +160,12 @@ class AvatarEditorBody extends React.Component {
this.props.deleteImage();
}
onImageChange() {
this.setState({
croppedImage: this.setEditorRef.current.getImage().toDataURL()
});
this.props.onImageChange(this.setEditorRef.current.getImage().toDataURL());
if(this.setEditorRef.current !== null){
this.setState({
croppedImage: this.setEditorRef.current.getImage().toDataURL()
});
this.props.onImageChange(this.setEditorRef.current.getImage().toDataURL());
}
}
dist = 0
scaling = false
@ -247,6 +250,13 @@ class AvatarEditorBody extends React.Component {
height: this.setEditorRef.current.getImage().height
});
}
componentDidUpdate(prevProps){
if(prevProps.image !== this.props.image){
this.setState({
image: this.props.image
});
}
}
render() {
return (
<div
@ -316,11 +326,11 @@ class AvatarEditorBody extends React.Component {
}
<StyledErrorContainer key="errorMsg">
{this.state.errorText !== null &&
<Text.Body
<Text
as="p"
color="#C96C27"
isBold={true}
>{this.state.errorText}</Text.Body>
>{this.state.errorText}</Text>
}
</StyledErrorContainer>
</div>

View File

@ -14,20 +14,23 @@ import { Avatar } from "asc-web-components";
role="admin"
source=""
userName=""
editing={false}
/>
editing={false}
/>
```
If you want to create an avatar with initials, only _first letter of first two words_ of line is used.
### Properties
| Props | Type | Required | Values | Default | Description |
| ------------ | :------: | :------: | :-------------------------------: | :----------: | -------------------------------------------------------- |
| `size` | `oneOf` | - | `max`, `big`, `medium`, `small` | `medium` | Size of avatar |
| `role` | `oneOf` | - | `owner`, `admin`, `guest`, `user` | `user` | Adds a user role table |
| `source` | `string` | - | - | - | The address of the image for an image avatar |
| `userName` | `string` | - | - | - | Need to create an avatar with initials |
| `editing` | `bool` | - | - | `false` | Displays avatar edit layer |
| `editLabel` | `string` | - | - | `Edit photo` | Label for editing layer |
| `editAction` | `func` | - | - | - | Function called when the avatar change button is pressed |
| Props | Type | Required | Values | Default | Description |
| ------------ | :------------: | :------: | :-------------------------------: | :----------: | -------------------------------------------------------- |
| `size` | `oneOf` | - | `max`, `big`, `medium`, `small` | `medium` | Size of avatar |
| `role` | `oneOf` | - | `owner`, `admin`, `guest`, `user` | `user` | Adds a user role table |
| `source` | `string` | - | - | - | The address of the image for an image avatar |
| `userName` | `string` | - | - | - | Need to create an avatar with initials |
| `editing` | `bool` | - | - | `false` | Displays avatar edit layer |
| `editLabel` | `string` | - | - | `Edit photo` | Label for editing layer |
| `editAction` | `func` | - | - | - | Function called when the avatar change button is pressed |
| `className` | `string` | - | - | - | Accepts class |
| `id` | `string` | - | - | - | Accepts id |
| `style` | `obj`, `array` | - | - | - | Accepts css style |

View File

@ -96,29 +96,28 @@ describe('<Avatar />', () => {
expect(wrapper.prop('editing')).toEqual(true);
});
/*
it('not re-render test', () => {
const wrapper = shallow(<Avatar {...baseProps} />).instance();
it('accepts id', () => {
const wrapper = mount(
<Avatar {...baseProps} id="testId" />
);
const shouldUpdate = wrapper.shouldComponentUpdate(wrapper.props, wrapper.state);
expect(shouldUpdate).toBe(false);
expect(wrapper.prop('id')).toEqual('testId');
});
it('re-render test', () => {
const wrapper = shallow(<Avatar {...baseProps} />).instance();
it('accepts className', () => {
const wrapper = mount(
<Avatar {...baseProps} className="test" />
);
const shouldUpdate = wrapper.shouldComponentUpdate({
size: 'max',
role: 'admin',
source: '',
editLabel: 'Edit',
userName: 'Demo User',
editing: false,
editAction: () => jest.fn()
}, wrapper.state);
expect(shouldUpdate).toBe(true);
expect(wrapper.prop('className')).toEqual('test');
});
*/
it('accepts style', () => {
const wrapper = mount(
<Avatar {...baseProps} style={{width: '100px'}} />
);
expect(wrapper.getDOMNode().style).toHaveProperty('width', '100px');
});
});

View File

@ -98,6 +98,7 @@ const NamedAvatar = styled.div`
`;
const EditContainer = styled.div`
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
@ -204,7 +205,11 @@ Avatar.propTypes = {
editLabel: PropTypes.string,
userName: PropTypes.string,
editing: PropTypes.bool,
editAction: PropTypes.func
editAction: PropTypes.func,
className: PropTypes.string,
id: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
};
Avatar.defaultProps = {

View File

@ -17,7 +17,10 @@ import { Backdrop } from "asc-web-components";
### Properties
| Props | Type | Required | Values | Default | Description |
| --------- | :------: | :------: | :----: | :-----: | -------------- |
| `visible` | `bool` | - | - | `false` | Display or not |
| `zIndex` | `number` | - | - | `100` | CSS z-index |
| Props | Type | Required | Values | Default | Description |
| ----------- | :------------: | :------: | :----: | :-----: | ----------------- |
| `className` | `string` | - | - | - | Accepts class |
| `id` | `string` | - | - | - | Accepts id |
| `style` | `obj`, `array` | - | - | - | Accepts css style |
| `visible` | `bool` | - | - | `false` | Display or not |
| `zIndex` | `number` | - | - | `100` | CSS z-index |

View File

@ -2,12 +2,48 @@ import React from 'react';
import { mount } from 'enzyme';
import Backdrop from '.';
const baseProps = {
visible: false
};
describe('<Backdrop />', () => {
it('renders without error', () => {
const wrapper = mount(
<Backdrop visible={false} />
<Backdrop {...baseProps} />
);
expect(wrapper).toExist();
});
it('visible', () => {
const wrapper = mount(
<Backdrop visible />
);
expect(wrapper.prop('visible')).toBe(true);
});
it('accepts id', () => {
const wrapper = mount(
<Backdrop {...baseProps} id="testId" />
);
expect(wrapper.prop('id')).toEqual('testId');
});
it('accepts className', () => {
const wrapper = mount(
<Backdrop {...baseProps} className="test" />
);
expect(wrapper.prop('className')).toEqual('test');
});
it('accepts style', () => {
const wrapper = mount(
<Backdrop {...baseProps} style={{ color: 'red' }} />
);
expect(wrapper.getDOMNode().style).toHaveProperty('color', 'red');
});
});

View File

@ -13,16 +13,19 @@ const StyledBackdrop = styled.div`
top: 0;
`;
const Backdrop = props => {
const Backdrop = props => {
//console.log("Backdrop render");
return (
<StyledBackdrop {...props}/>
<StyledBackdrop {...props} />
);
}
Backdrop.propTypes = {
visible: PropTypes.bool,
zIndex: PropTypes.number
zIndex: PropTypes.number,
className: PropTypes.string,
id: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
};
Backdrop.defaultProps = {

View File

@ -24,14 +24,17 @@ import { Badge } from "asc-web-components";
### Properties
| Props | Type | Required | Values | Default | Description |
| ----------------- | :------: | :------: | :----: | :-------: | -------------------- |
| `number` | `number` | - | - | `0` | Number value |
| `backgroundColor` | `string` | - | - | `#ED7309` | CSS background-color |
| `color` | `string` | - | - | `#FFFFFF` | CSS color |
| `fontSize` | `string` | - | - | `11px` | CSS font-size |
| `fontWeight` | `number` | - | - | `800` | CSS font-weight |
| `borderRadius` | `string` | - | - | `11px` | CSS border-radius |
| `padding` | `string` | - | - | `0 5px` | CSS padding |
| `maxWidth` | `string` | - | - | `50px` | CSS max-width |
| `onClick` | `func` | - | - | - | onClick event |
| Props | Type | Required | Values | Default | Description |
| ----------------- | :------------: | :------: | :----: | :-------: | -------------------- |
| `backgroundColor` | `string` | - | - | `#ED7309` | CSS background-color |
| `borderRadius` | `string` | - | - | `11px` | CSS border-radius |
| `className` | `string` | - | - | - | Accepts class |
| `color` | `string` | - | - | `#FFFFFF` | CSS color |
| `fontSize` | `string` | - | - | `11px` | CSS font-size |
| `fontWeight` | `number` | - | - | `800` | CSS font-weight |
| `id` | `string` | - | - | - | Accepts id |
| `maxWidth` | `string` | - | - | `50px` | CSS max-width |
| `number` | `number` | - | - | `0` | Number value |
| `onClick` | `func` | - | - | - | onClick event |
| `padding` | `string` | - | - | `0 5px` | CSS padding |
| `style` | `obj`, `array` | - | - | - | Accepts css style |

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