Merge remote-tracking branch 'remotes/origin/master' into feature/grid-component
This commit is contained in:
commit
581c32e672
@ -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 && \
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
@ -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
@ -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"
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -52,6 +52,7 @@ class PasswordField extends React.Component {
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
spacing='33px'
|
||||
/>
|
||||
<PasswordInput
|
||||
inputName={inputName}
|
||||
|
@ -40,6 +40,7 @@ class RadioField extends React.Component {
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
spacing='33px'
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
24
products/ASC.People/Client/src/store/portal/actions.js
Normal file
24
products/ASC.People/Client/src/store/portal/actions.js
Normal 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));
|
||||
});
|
||||
};
|
||||
};
|
18
products/ASC.People/Client/src/store/portal/reducers.js
Normal file
18
products/ASC.People/Client/src/store/portal/reducers.js
Normal 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;
|
@ -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);
|
||||
};
|
||||
|
@ -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
@ -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>
|
||||
|
@ -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()
|
||||
|
@ -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}");
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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} />
|
||||
);
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
|
@ -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)));
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
</>
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
)
|
||||
|
||||
);
|
||||
|
@ -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)));
|
||||
|
@ -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;
|
@ -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>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
74
web/ASC.Web.Client/src/store/confirm/actions.js
Normal file
74
web/ASC.Web.Client/src/store/confirm/actions.js
Normal 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));
|
||||
};
|
||||
};
|
18
web/ASC.Web.Client/src/store/confirm/reducer.js
Normal file
18
web/ASC.Web.Client/src/store/confirm/reducer.js
Normal 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;
|
@ -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;
|
@ -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
@ -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';
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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 = {
|
||||
|
@ -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";
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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"
|
||||
|
34
web/ASC.Web.Common/src/components/module-tile/README.md
Normal file
34
web/ASC.Web.Common/src/components/module-tile/README.md
Normal 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 |
|
149
web/ASC.Web.Common/src/components/module-tile/index.js
Normal file
149
web/ASC.Web.Common/src/components/module-tile/index.js
Normal 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;
|
@ -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>
|
||||
));
|
61
web/ASC.Web.Common/src/components/module-tile/tile.test.js
Normal file
61
web/ASC.Web.Common/src/components/module-tile/tile.test.js
Normal 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);
|
||||
});
|
||||
});
|
@ -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));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
}
|
@ -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"
|
||||
|
@ -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}
|
||||
|
530
web/ASC.Web.Components/src/components/all/all.stories.js
Normal file
530
web/ASC.Web.Components/src/components/all/all.stories.js
Normal 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>
|
||||
));
|
@ -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 |
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -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>
|
||||
|
@ -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 |
|
||||
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -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 |
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user