Merge branch 'feature/rtl-interface-direction' of https://github.com/ONLYOFFICE/DocSpace into feature/rtl-interface-direction
This commit is contained in:
commit
1299c93e55
@ -71,7 +71,7 @@ public static class DbIPLookupExtension
|
||||
.IsRequired()
|
||||
.HasColumnName("addr_type")
|
||||
.HasColumnType("enum('ipv4','ipv6')");
|
||||
|
||||
|
||||
entity.Property(e => e.IPStart)
|
||||
.IsRequired()
|
||||
.HasColumnName("ip_start")
|
||||
@ -150,6 +150,91 @@ public static class DbIPLookupExtension
|
||||
}
|
||||
public static void PgSqlAddDbIPLookup(this ModelBuilder modelBuilder)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
modelBuilder.Entity<DbIPLookup>(entity =>
|
||||
{
|
||||
entity.ToTable("dbip_lookup")
|
||||
.HasCharSet("utf8mb4");
|
||||
|
||||
entity.HasKey(nameof(DbIPLookup.AddrType), nameof(DbIPLookup.IPStart));
|
||||
|
||||
entity.Property(e => e.AddrType)
|
||||
.IsRequired()
|
||||
.HasColumnName("addr_type")
|
||||
.HasColumnType("enum('ipv4','ipv6')");
|
||||
|
||||
entity.Property(e => e.IPStart)
|
||||
.IsRequired()
|
||||
.HasColumnName("ip_start")
|
||||
.HasColumnType("varbinary(16)");
|
||||
|
||||
entity.Property(e => e.IPEnd)
|
||||
.IsRequired()
|
||||
.HasColumnName("ip_end")
|
||||
.HasColumnType("varbinary(16)");
|
||||
|
||||
entity.Property(e => e.Continent)
|
||||
.IsRequired()
|
||||
.HasColumnName("continent")
|
||||
.HasColumnType("char(2)");
|
||||
|
||||
entity.Property(e => e.Country)
|
||||
.IsRequired()
|
||||
.HasColumnName("country")
|
||||
.HasColumnType("char(2)");
|
||||
|
||||
entity.Property(e => e.StateProvCode)
|
||||
.HasColumnName("stateprov_code")
|
||||
.HasColumnType("varchar(15)");
|
||||
|
||||
entity.Property(e => e.StateProv)
|
||||
.IsRequired()
|
||||
.HasColumnName("stateprov")
|
||||
.HasColumnType("varchar(80)");
|
||||
|
||||
entity.Property(e => e.District)
|
||||
.IsRequired()
|
||||
.HasColumnName("district")
|
||||
.HasColumnType("varchar(80)");
|
||||
|
||||
|
||||
entity.Property(e => e.City)
|
||||
.IsRequired()
|
||||
.HasColumnName("city")
|
||||
.HasColumnType("varchar(80)");
|
||||
|
||||
entity.Property(e => e.ZipCode)
|
||||
.HasColumnName("zipcode")
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
entity.Property(e => e.Latitude)
|
||||
.IsRequired()
|
||||
.HasColumnName("latitude")
|
||||
.HasColumnType("float");
|
||||
|
||||
entity.Property(e => e.Longitude)
|
||||
.IsRequired()
|
||||
.HasColumnName("longitude")
|
||||
.HasColumnType("float");
|
||||
|
||||
entity.Property(e => e.GeonameId)
|
||||
.IsRequired(false)
|
||||
.HasColumnName("geoname_id")
|
||||
.HasColumnType("int(10)");
|
||||
|
||||
entity.Property(e => e.TimezoneOffset)
|
||||
.IsRequired()
|
||||
.HasColumnType("float")
|
||||
.HasColumnName("timezone_offset");
|
||||
|
||||
entity.Property(e => e.TimezoneName)
|
||||
.IsRequired()
|
||||
.HasColumnName("timezone_name")
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
entity.Property(e => e.WeatherCode)
|
||||
.IsRequired()
|
||||
.HasColumnName("weather_code")
|
||||
.HasColumnType("varchar(10)");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
using AutoMapper;
|
||||
|
||||
namespace ASC.Webhooks.Core;
|
||||
namespace ASC.Webhooks.Core;
|
||||
|
||||
[Scope]
|
||||
public class DbWorker
|
||||
@ -63,12 +63,12 @@ public class DbWorker
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<WebhooksConfig> AddWebhookConfig(string uri, string secretKey)
|
||||
public async Task<WebhooksConfig> AddWebhookConfig(string uri, string name, string secretKey, bool? enabled, bool? ssl)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var objForCreate = await webhooksDbContext.WebhooksConfigs
|
||||
.Where(it => it.TenantId == Tenant && it.Uri == uri)
|
||||
.Where(it => it.TenantId == Tenant && it.Uri == uri && it.Name == name)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (objForCreate != null)
|
||||
@ -80,22 +80,32 @@ public class DbWorker
|
||||
{
|
||||
TenantId = Tenant,
|
||||
Uri = uri,
|
||||
SecretKey = secretKey
|
||||
SecretKey = secretKey,
|
||||
Name = name,
|
||||
Enabled = enabled ?? true,
|
||||
SSL = ssl ?? true
|
||||
};
|
||||
|
||||
toAdd = await webhooksDbContext.AddOrUpdateAsync(r => r.WebhooksConfigs, toAdd);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
|
||||
return toAdd;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<WebhooksConfig> GetTenantWebhooks()
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<WebhooksConfigWithStatus> GetTenantWebhooksWithStatus()
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var q = webhooksDbContext.WebhooksConfigs
|
||||
.AsNoTracking()
|
||||
.Where(it => it.TenantId == Tenant)
|
||||
.GroupJoin(webhooksDbContext.WebhooksLogs, c => c.Id, l => l.ConfigId, (configs, logs) => new { configs, logs })
|
||||
.Select(it =>
|
||||
new WebhooksConfigWithStatus
|
||||
{
|
||||
WebhooksConfig = it.configs,
|
||||
Status = it.logs.OrderBy(it => it.Delivery).LastOrDefault().Status
|
||||
})
|
||||
.AsAsyncEnumerable();
|
||||
|
||||
await foreach (var webhook in q)
|
||||
@ -113,7 +123,7 @@ public class DbWorker
|
||||
.AsAsyncEnumerable();
|
||||
}
|
||||
|
||||
public async Task<WebhooksConfig> UpdateWebhookConfig(int id, string uri, string key, bool? enabled)
|
||||
public async Task<WebhooksConfig> UpdateWebhookConfig(int id, string name, string uri, string key, bool? enabled, bool? ssl)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
@ -123,6 +133,11 @@ public class DbWorker
|
||||
|
||||
if (updateObj != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
updateObj.Name = name;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(uri))
|
||||
{
|
||||
updateObj.Uri = uri;
|
||||
@ -138,6 +153,11 @@ public class DbWorker
|
||||
updateObj.Enabled = enabled.Value;
|
||||
}
|
||||
|
||||
if (ssl.HasValue)
|
||||
{
|
||||
updateObj.SSL = ssl.Value;
|
||||
}
|
||||
|
||||
webhooksDbContext.WebhooksConfigs.Update(updateObj);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
}
|
||||
@ -164,29 +184,9 @@ public class DbWorker
|
||||
return removeObj;
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<WebhooksLog> ReadJournal(int startIndex, int limit, DateTime? delivery, string hookUri, int hookId)
|
||||
{
|
||||
var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var q = webhooksDbContext.WebhooksLogs
|
||||
.AsNoTracking()
|
||||
.Where(r => r.TenantId == Tenant);
|
||||
|
||||
if (delivery.HasValue)
|
||||
{
|
||||
var date = delivery.Value;
|
||||
q = q.Where(r => r.Delivery == date);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(hookUri))
|
||||
{
|
||||
q = q.Where(r => r.Config.Uri == hookUri);
|
||||
}
|
||||
|
||||
if (hookId != 0)
|
||||
{
|
||||
q = q.Where(r => r.WebhookId == hookId);
|
||||
}
|
||||
public IAsyncEnumerable<DbWebhooks> ReadJournal(int startIndex, int limit, DateTime? deliveryFrom, DateTime? deliveryTo, string hookUri, int? hookId, int? configId, int? eventId, WebhookGroupStatus? webhookGroupStatus)
|
||||
{
|
||||
var q = GetQueryForJournal(deliveryFrom, deliveryTo, hookUri, hookId, configId, eventId, webhookGroupStatus);
|
||||
|
||||
if (startIndex != 0)
|
||||
{
|
||||
@ -198,17 +198,30 @@ public class DbWorker
|
||||
q = q.Take(limit);
|
||||
}
|
||||
|
||||
return q.OrderByDescending(t => t.Id).AsAsyncEnumerable();
|
||||
return q.AsAsyncEnumerable();
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalByQuery(DateTime? deliveryFrom, DateTime? deliveryTo, string hookUri, int? hookId, int? configId, int? eventId, WebhookGroupStatus? webhookGroupStatus)
|
||||
{
|
||||
return await GetQueryForJournal(deliveryFrom, deliveryTo, hookUri, hookId, configId, eventId, webhookGroupStatus).CountAsync();
|
||||
}
|
||||
|
||||
public async Task<WebhooksLog> ReadJournal(int id)
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return await webhooksDbContext.WebhooksLogs
|
||||
var fromDb = await webhooksDbContext.WebhooksLogs
|
||||
.AsNoTracking()
|
||||
.Where(it => it.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
.Join(webhooksDbContext.WebhooksConfigs, r => r.ConfigId, r => r.Id, (log, config) => new DbWebhooks { Log = log, Config = config })
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (fromDb != null)
|
||||
{
|
||||
fromDb.Log.Config = fromDb.Config;
|
||||
}
|
||||
|
||||
return fromDb.Log;
|
||||
}
|
||||
|
||||
public async Task<WebhooksLog> WriteToJournal(WebhooksLog webhook)
|
||||
@ -228,15 +241,21 @@ public class DbWorker
|
||||
{
|
||||
using var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var webhook = await webhooksDbContext.WebhooksLogs.Where(t => t.Id == id).FirstOrDefaultAsync();
|
||||
webhook.Status = status;
|
||||
webhook.RequestHeaders = requestHeaders;
|
||||
webhook.ResponsePayload = responsePayload;
|
||||
webhook.ResponseHeaders = responseHeaders;
|
||||
webhook.Delivery = delivery;
|
||||
|
||||
webhooksDbContext.WebhooksLogs.Update(webhook);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
var webhook = await webhooksDbContext.WebhooksLogs
|
||||
.Where(t => t.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (webhook != null)
|
||||
{
|
||||
webhook.Status = status;
|
||||
webhook.RequestHeaders = requestHeaders;
|
||||
webhook.ResponsePayload = responsePayload;
|
||||
webhook.ResponseHeaders = responseHeaders;
|
||||
webhook.Delivery = delivery;
|
||||
|
||||
webhooksDbContext.WebhooksLogs.Update(webhook);
|
||||
await webhooksDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return webhook;
|
||||
}
|
||||
@ -288,5 +307,90 @@ public class DbWorker
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return _mapper.Map<DbWebhook, Webhook>(webHook);
|
||||
}
|
||||
|
||||
private IQueryable<DbWebhooks> GetQueryForJournal(DateTime? deliveryFrom, DateTime? deliveryTo, string hookUri, int? hookId, int? configId, int? eventId, WebhookGroupStatus? webhookGroupStatus)
|
||||
{
|
||||
var webhooksDbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var q = webhooksDbContext.WebhooksLogs
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(t => t.Id)
|
||||
.Where(r => r.TenantId == Tenant)
|
||||
.Join(webhooksDbContext.WebhooksConfigs.AsNoTracking(), r => r.ConfigId, r => r.Id, (log, config) => new DbWebhooks { Log = log, Config = config });
|
||||
|
||||
if (deliveryFrom.HasValue)
|
||||
{
|
||||
var from = deliveryFrom.Value;
|
||||
q = q.Where(r => r.Log.Delivery >= from);
|
||||
}
|
||||
|
||||
if (deliveryTo.HasValue)
|
||||
{
|
||||
var to = deliveryTo.Value;
|
||||
q = q.Where(r => r.Log.Delivery <= to);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(hookUri))
|
||||
{
|
||||
q = q.Where(r => r.Config.Uri == hookUri);
|
||||
}
|
||||
|
||||
if (hookId != null)
|
||||
{
|
||||
q = q.Where(r => r.Log.WebhookId == hookId);
|
||||
}
|
||||
|
||||
if (configId != null)
|
||||
{
|
||||
q = q.Where(r => r.Log.ConfigId == configId);
|
||||
}
|
||||
|
||||
if (eventId != null)
|
||||
{
|
||||
q = q.Where(r => r.Log.Id == eventId);
|
||||
}
|
||||
|
||||
if (webhookGroupStatus != null && webhookGroupStatus != WebhookGroupStatus.None)
|
||||
{
|
||||
if ((webhookGroupStatus & WebhookGroupStatus.NotSent) != WebhookGroupStatus.NotSent)
|
||||
{
|
||||
q = q.Where(r => r.Log.Status != 0);
|
||||
}
|
||||
if ((webhookGroupStatus & WebhookGroupStatus.Status2xx) != WebhookGroupStatus.Status2xx)
|
||||
{
|
||||
q = q.Where(r => r.Log.Status < 200 || r.Log.Status >= 300);
|
||||
}
|
||||
if ((webhookGroupStatus & WebhookGroupStatus.Status3xx) != WebhookGroupStatus.Status3xx)
|
||||
{
|
||||
q = q.Where(r => r.Log.Status < 300 || r.Log.Status >= 400);
|
||||
}
|
||||
if ((webhookGroupStatus & WebhookGroupStatus.Status4xx) != WebhookGroupStatus.Status4xx)
|
||||
{
|
||||
q = q.Where(r => r.Log.Status < 400 || r.Log.Status >= 500);
|
||||
}
|
||||
if ((webhookGroupStatus & WebhookGroupStatus.Status5xx) != WebhookGroupStatus.Status5xx)
|
||||
{
|
||||
q = q.Where(r => r.Log.Status < 500);
|
||||
}
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
}
|
||||
public class DbWebhooks
|
||||
{
|
||||
public WebhooksLog Log { get; set; }
|
||||
public WebhooksConfig Config { get; set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum WebhookGroupStatus
|
||||
{
|
||||
None = 0,
|
||||
NotSent = 1,
|
||||
Status2xx = 2,
|
||||
Status3xx = 4,
|
||||
Status4xx = 8,
|
||||
Status5xx = 16
|
||||
}
|
@ -29,10 +29,12 @@ namespace ASC.Webhooks.Core.EF.Model;
|
||||
public class WebhooksConfig : BaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string SecretKey { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public bool SSL { get; set; }
|
||||
|
||||
public override object[] GetKeys()
|
||||
{
|
||||
@ -71,18 +73,30 @@ public static class WebhooksConfigExtension
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
entity.Property(e => e.Uri)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''");
|
||||
.HasDefaultValueSql("''")
|
||||
.HasColumnType("text")
|
||||
.HasCharSet("utf8")
|
||||
.UseCollation("utf8_general_ci");
|
||||
|
||||
entity.Property(e => e.SecretKey)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("name")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("'1'")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
entity.Property(e => e.SSL)
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("'1'")
|
||||
.HasColumnType("tinyint(1)");
|
||||
});
|
||||
}
|
||||
@ -108,7 +122,6 @@ public static class WebhooksConfigExtension
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
entity.Property(e => e.Uri)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
@ -117,9 +130,24 @@ public static class WebhooksConfigExtension
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("name")
|
||||
.IsRequired();
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
entity.Property(e => e.SSL)
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("true");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhooksConfigWithStatus
|
||||
{
|
||||
public WebhooksConfig WebhooksConfig { get; set; }
|
||||
public int? Status { get; set; }
|
||||
}
|
||||
|
@ -47,9 +47,7 @@ public class WebhooksLog
|
||||
public static class WebhooksPayloadExtension
|
||||
{
|
||||
public static ModelBuilderWrapper AddWebhooksLog(this ModelBuilderWrapper modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<WebhooksLog>().Navigation(e => e.Config).AutoInclude();
|
||||
|
||||
{
|
||||
modelBuilder
|
||||
.Add(MySqlAddWebhooksLog, Provider.MySql)
|
||||
.Add(PgSqlAddWebhooksLog, Provider.PostgreSql);
|
||||
|
@ -27,15 +27,16 @@
|
||||
global using System.Text.Json.Serialization;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.Core.Common.Settings;
|
||||
global using ASC.Web.Webhooks;
|
||||
global using ASC.EventBus.Abstractions;
|
||||
global using ASC.EventBus.Events;
|
||||
global using ASC.Webhooks.Core.EF.Context;
|
||||
global using ASC.Webhooks.Core.EF.Model;
|
||||
global using ASC.Webhooks.Core.IntegrationEvents.Events;
|
||||
global using ASC.Webhooks.Core.Resources;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
|
@ -24,46 +24,23 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
namespace ASC.Webhooks;
|
||||
[Scope]
|
||||
public class SslHelper
|
||||
namespace ASC.Webhooks.Core.IntegrationEvents.Events;
|
||||
[ProtoContract]
|
||||
public record WebhookRequestIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public SslHelper(
|
||||
SettingsManager settingsManager
|
||||
)
|
||||
public WebhookRequestIntegrationEvent() : base()
|
||||
{
|
||||
_settingsManager = settingsManager;
|
||||
|
||||
}
|
||||
|
||||
public bool ValidateCertificate(SslPolicyErrors sslPolicyErrors)
|
||||
public WebhookRequestIntegrationEvent(Guid createBy, int tenantId) :
|
||||
base(createBy, tenantId)
|
||||
{
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var settings = _settingsManager.Load<WebHooksSettings>();
|
||||
|
||||
if (!settings.EnableSSLVerification)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new SslException(sslPolicyErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public class SslException : Exception
|
||||
{
|
||||
public SslPolicyErrors Errors { get; set; }
|
||||
public override string Message { get => Errors.ToString(); }
|
||||
|
||||
public SslException(SslPolicyErrors errors)
|
||||
{
|
||||
Errors = errors;
|
||||
}
|
||||
[ProtoMember(1)]
|
||||
public int WebhookId { get; set; }
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ namespace ASC.Webhooks.Core;
|
||||
|
||||
[Serializable]
|
||||
public class WebHooksSettings : ISettings<WebHooksSettings>
|
||||
{
|
||||
public bool EnableSSLVerification { get; set; }
|
||||
{
|
||||
public List<int> Ids { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
@ -38,7 +37,6 @@ public class WebHooksSettings : ISettings<WebHooksSettings>
|
||||
|
||||
public WebHooksSettings GetDefault() => new WebHooksSettings()
|
||||
{
|
||||
EnableSSLVerification = true,
|
||||
Ids = new List<int> { }
|
||||
};
|
||||
}
|
||||
|
@ -30,14 +30,20 @@ namespace ASC.Webhooks.Core;
|
||||
public class WebhookPublisher : IWebhookPublisher
|
||||
{
|
||||
private readonly DbWorker _dbWorker;
|
||||
private readonly ICacheNotify<WebhookRequest> _webhookNotify;
|
||||
private readonly IEventBus _eventBus;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly TenantManager _tenantManager;
|
||||
|
||||
public WebhookPublisher(
|
||||
DbWorker dbWorker,
|
||||
ICacheNotify<WebhookRequest> webhookNotify)
|
||||
IEventBus eventBus,
|
||||
SecurityContext securityContext,
|
||||
TenantManager tenantManager)
|
||||
{
|
||||
_dbWorker = dbWorker;
|
||||
_webhookNotify = webhookNotify;
|
||||
_eventBus = eventBus;
|
||||
_securityContext = securityContext;
|
||||
_tenantManager = tenantManager;
|
||||
}
|
||||
|
||||
public async Task PublishAsync(int webhookId, string requestPayload)
|
||||
@ -72,12 +78,12 @@ public class WebhookPublisher : IWebhookPublisher
|
||||
|
||||
var webhook = await _dbWorker.WriteToJournal(webhooksLog);
|
||||
|
||||
var request = new WebhookRequest
|
||||
_eventBus.Publish(new WebhookRequestIntegrationEvent(
|
||||
_securityContext.CurrentAccount.ID,
|
||||
_tenantManager.GetCurrentTenant().Id)
|
||||
{
|
||||
Id = webhook.Id
|
||||
};
|
||||
|
||||
_webhookNotify.Publish(request, CacheNotifyAction.Update);
|
||||
WebhookId = webhook.Id
|
||||
});
|
||||
|
||||
return webhook;
|
||||
}
|
||||
|
@ -19,34 +19,34 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
|
@ -1,29 +1,29 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
@ -36,24 +36,24 @@ public partial class WebhooksDbContextMigrate : Migration
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
route = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
method = table.Column<string>(type: "varchar(10)", maxLength: 10, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
route = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
method = table.Column<string>(type: "varchar(10)", maxLength: 10, nullable: false, defaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
@ -83,7 +83,7 @@ public partial class WebhooksDbContextMigrate : Migration
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
config_id = table.Column<int>(type: "int", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
creation_time = table.Column<DateTime>(type: "datetime", nullable: false),
|
||||
webhook_id = table.Column<int>(type: "int", nullable: false),
|
||||
request_headers = table.Column<string>(type: "json", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8"),
|
||||
@ -130,7 +130,7 @@ public partial class WebhooksDbContextMigrate : Migration
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks");
|
||||
name: "webhooks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
|
194
migrations/mysql/WebhooksDbContext/20230607172539_WebhooksDbContext_Upgrade1.Designer.cs
generated
Normal file
194
migrations/mysql/WebhooksDbContext/20230607172539_WebhooksDbContext_Upgrade1.Designer.cs
generated
Normal file
@ -0,0 +1,194 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ASC.Webhooks.Core.EF.Context;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.MySql.Migrations.WebhooksDb
|
||||
{
|
||||
[DbContext(typeof(WebhooksDbContext))]
|
||||
[Migration("20230607172539_WebhooksDbContext_Upgrade1")]
|
||||
partial class WebhooksDbContextUpgrade1
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("SSL")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<uint>("TenantId")
|
||||
.HasColumnType("int unsigned")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant_id");
|
||||
|
||||
b.ToTable("webhooks_config", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<int>("ConfigId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("creation_time");
|
||||
|
||||
b.Property<DateTime?>("Delivery")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
|
||||
b.Property<string>("RequestPayload")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("request_payload")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<string>("ResponseHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("response_headers");
|
||||
|
||||
b.Property<string>("ResponsePayload")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("response_payload")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<uint>("TenantId")
|
||||
.HasColumnType("int unsigned")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(36)")
|
||||
.HasColumnName("uid")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.HasIndex("ConfigId");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant_id");
|
||||
|
||||
b.ToTable("webhooks_logs", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
|
||||
{
|
||||
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
|
||||
.WithMany()
|
||||
.HasForeignKey("ConfigId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Config");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.MySql.Migrations.WebhooksDb
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class WebhooksDbContextUpgrade1 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "uri",
|
||||
table: "webhooks_config",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
collation: "utf8_general_ci",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(50)",
|
||||
oldMaxLength: 50,
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ssl",
|
||||
table: "webhooks_config",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValueSql: "'1'");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "route",
|
||||
table: "webhooks",
|
||||
type: "varchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(50)",
|
||||
oldMaxLength: 50,
|
||||
oldDefaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "method",
|
||||
table: "webhooks",
|
||||
type: "varchar(10)",
|
||||
maxLength: 10,
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(10)",
|
||||
oldMaxLength: 10,
|
||||
oldDefaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ssl",
|
||||
table: "webhooks_config");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "uri",
|
||||
table: "webhooks_config",
|
||||
type: "varchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''",
|
||||
oldCollation: "utf8_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "webhooks",
|
||||
keyColumn: "route",
|
||||
keyValue: null,
|
||||
column: "route",
|
||||
value: "");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "route",
|
||||
table: "webhooks",
|
||||
type: "varchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: false,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(200)",
|
||||
oldMaxLength: 200,
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "webhooks",
|
||||
keyColumn: "method",
|
||||
keyValue: null,
|
||||
column: "method",
|
||||
value: "");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "method",
|
||||
table: "webhooks",
|
||||
type: "varchar(10)",
|
||||
maxLength: 10,
|
||||
nullable: false,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(10)",
|
||||
oldMaxLength: 10,
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''")
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.OldAnnotation("MySql:CharSet", "utf8");
|
||||
}
|
||||
}
|
||||
}
|
@ -31,16 +31,14 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''")
|
||||
.IsRequired();
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''")
|
||||
.IsRequired();
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
@ -63,6 +61,18 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("SSL")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
@ -76,10 +86,11 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''");
|
||||
.HasDefaultValueSql("''")
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
@ -111,11 +122,6 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
@ -137,10 +143,6 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<int>("ConfigId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("status");
|
||||
@ -156,6 +158,10 @@ namespace ASC.Migrations.MySql.Migrations
|
||||
.UseCollation("utf8_general_ci")
|
||||
.HasAnnotation("MySql:CharSet", "utf8");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
|
@ -21,34 +21,34 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "6.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("route");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("method");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
|
@ -1,31 +1,31 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
@ -35,7 +35,7 @@ namespace ASC.Migrations.PostgreSql.Migrations;
|
||||
public partial class WebhooksDbContextMigrate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks",
|
||||
columns: table => new
|
||||
@ -48,7 +48,7 @@ public partial class WebhooksDbContextMigrate : Migration
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.id);
|
||||
});
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webhooks_config",
|
||||
@ -115,7 +115,7 @@ public partial class WebhooksDbContextMigrate : Migration
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks");
|
||||
name: "webhooks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webhooks_logs");
|
||||
|
186
migrations/postgre/WebhooksDbContext/20230607172539_WebhooksDbContext_Upgrade1.Designer.cs
generated
Normal file
186
migrations/postgre/WebhooksDbContext/20230607172539_WebhooksDbContext_Upgrade1.Designer.cs
generated
Normal file
@ -0,0 +1,186 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ASC.Webhooks.Core.EF.Context;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
|
||||
{
|
||||
[DbContext(typeof(WebhooksDbContext))]
|
||||
[Migration("20230607172539_WebhooksDbContext_Upgrade1")]
|
||||
partial class WebhooksDbContextUpgrade1
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "7.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.DbWebhook", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Method")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("method")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<string>("Route")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("route")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.ToTable("webhooks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("SSL")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("secret_key")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.Property<int>("TenantId")
|
||||
.HasColumnType("int unsigned")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant_id");
|
||||
|
||||
b.ToTable("webhooks_config", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ConfigId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("creation_time");
|
||||
|
||||
b.Property<DateTime?>("Delivery")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("delivery");
|
||||
|
||||
b.Property<string>("RequestHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("request_headers");
|
||||
|
||||
b.Property<string>("RequestPayload")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("request_payload");
|
||||
|
||||
b.Property<string>("ResponseHeaders")
|
||||
.HasColumnType("json")
|
||||
.HasColumnName("response_headers");
|
||||
|
||||
b.Property<string>("ResponsePayload")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("response_payload");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<int>("TenantId")
|
||||
.HasColumnType("int unsigned")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.HasIndex("ConfigId");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant_id");
|
||||
|
||||
b.ToTable("webhooks_logs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
|
||||
{
|
||||
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
|
||||
.WithMany()
|
||||
.HasForeignKey("ConfigId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Config");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class WebhooksDbContextUpgrade1 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "uri",
|
||||
table: "webhooks_config",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(50)",
|
||||
oldMaxLength: 50,
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "name",
|
||||
table: "webhooks_config",
|
||||
type: "character varying(50)",
|
||||
maxLength: 50,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ssl",
|
||||
table: "webhooks_config",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValueSql: "true");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "name",
|
||||
table: "webhooks_config");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ssl",
|
||||
table: "webhooks_config");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "uri",
|
||||
table: "webhooks_config",
|
||||
type: "character varying(50)",
|
||||
maxLength: 50,
|
||||
nullable: true,
|
||||
defaultValueSql: "''",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true,
|
||||
oldDefaultValueSql: "''");
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,18 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<bool>("SSL")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("ssl")
|
||||
.HasDefaultValueSql("true");
|
||||
|
||||
b.Property<string>("SecretKey")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
@ -76,8 +88,7 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("uri")
|
||||
.HasDefaultValueSql("''");
|
||||
|
||||
@ -102,10 +113,6 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("config_id");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("creation_time");
|
||||
@ -145,6 +152,10 @@ namespace ASC.Migrations.PostgreSql.Migrations
|
||||
.HasColumnType("varchar")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<int>("WebhookId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("webhook_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
|
60
packages/client/public/locales/en/Webhooks.json
Normal file
60
packages/client/public/locales/en/Webhooks.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"Webhook": "Webhook",
|
||||
"Webhooks": "Webhooks",
|
||||
"CreateWebhook": "Create webhook",
|
||||
"WebhooksInfo": "Use webhooks to perform custom actions on the side of any application or website you are using based on various events in ONLYOFFICE Docspace.\r\nHere, you can create and manage all your webhooks, configure them, and browse history of every webhook to audit their performance.",
|
||||
"WebhooksGuide": "Webhooks Guide",
|
||||
"WebhookCreationHint": "This webhook will be assigned to all events in DocSpace",
|
||||
"WebhookName": "Webhook name",
|
||||
"EnterWebhookName": "Enter webhook name",
|
||||
"PayloadUrl": "Payload URL",
|
||||
"EnterUrl": "Enter URL",
|
||||
"SSLVerification": "SSL verification",
|
||||
"SSLHint": "By default, we verify SSL certificates when delivering payloads.",
|
||||
"EnableSSL": "Enable SSL verification",
|
||||
"DisableSSL": "Disable (not recommended)",
|
||||
"EnterSecretKey": "Enter secret key",
|
||||
"SecretKey": "Secret key",
|
||||
"SecretKeyHint": "Setting a webhook secret allows you to verify requests sent to the payload URL.",
|
||||
"ReadMore": "Read more",
|
||||
"SecretKeyWarning": "You cannot retrieve your secret key again once it has been saved. If you've lost or forgotten this secret key, you can reset it, but all integrations using this secret will need to be updated.",
|
||||
"ResetKey": "Reset key",
|
||||
"Generate": "Generate",
|
||||
"DeleteHint": "The webhook will be deleted permanently.\r\nYou will not be able to undo this action.",
|
||||
"NotSent": "Not sent",
|
||||
"WebhookEditedSuccessfully": "Webhook configuration edited successfully",
|
||||
"WebhookRemoved": "Webhook removed",
|
||||
"WebhookHistory": "Webhook history",
|
||||
"DeleteWebhook": "Delete webhook",
|
||||
"SettingsWebhook": "Settings webhook",
|
||||
"DeleteWebhookForeverQuestion": "Delete Webhook forever?",
|
||||
"URL": "URL",
|
||||
"State": "State",
|
||||
"WebhookRedilivered": "Webhook redelivered",
|
||||
"WebhookDetails": "Webhook details",
|
||||
"Request": "Request",
|
||||
"Response": "Response",
|
||||
"FailedToConnect": "We couldn’t deliver this payload: failed to connect to host",
|
||||
"RequestPostHeader": "Request post header",
|
||||
"RequestPostBody": "Request post body",
|
||||
"RequestHeaderCopied": "Request post header successfully copied to clipboard",
|
||||
"RequestBodyCopied": "Request post body successfully copied to clipboard",
|
||||
"ResponsePostHeader": "Response post header",
|
||||
"ResponsePostBody": "Response post body",
|
||||
"ResponseHeaderCopied": "Response post header successfully copied to clipboard",
|
||||
"ResponseBodyCopied": "Response post body successfully copied to clipboard",
|
||||
"PayloadIsTooLarge": "Payload is too large to display.",
|
||||
"ViewRawPayload": "View raw payload",
|
||||
"Retry": "Retry",
|
||||
"UnselectAll": "Unselect all",
|
||||
"EventHint": "Deliveries are automatically deleted after 15 days",
|
||||
"NoResultsMatched": "No results match this filter. Try a different one or clear filter to view all items.",
|
||||
"SelectDate": "Select date",
|
||||
"SelectDeliveryTime": "Select Delivery time",
|
||||
"DeliveryDate": "Delivery date",
|
||||
"From": "From",
|
||||
"Before": "Before",
|
||||
"EventID": "Event ID",
|
||||
"Delivery": "Delivery",
|
||||
"Add": "Add"
|
||||
}
|
@ -28,6 +28,7 @@ import MainBar from "./components/MainBar";
|
||||
import { Portal } from "@docspace/components";
|
||||
import indexedDbHelper from "@docspace/common/utils/indexedDBHelper";
|
||||
import { IndexedDBStores } from "@docspace/common/constants";
|
||||
import { isMobile as isMobileUtils } from "@docspace/components/utils/device";
|
||||
import queryString from "query-string";
|
||||
|
||||
const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
@ -340,7 +341,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
<Layout>
|
||||
{toast}
|
||||
<ReactSmartBanner t={t} ready={ready} />
|
||||
{isEditor || !isMobileOnly ? <></> : <NavMenu />}
|
||||
{isEditor ? <></> : <NavMenu />}
|
||||
{isMobileOnly && <MainBar />}
|
||||
<IndicatorLoader />
|
||||
<ScrollToTop />
|
||||
|
@ -2,6 +2,8 @@ import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { isIOS, isFirefox, isMobileOnly } from "react-device-detect";
|
||||
|
||||
import { mobile } from "@docspace/components/utils/device";
|
||||
|
||||
const StyledMain = styled.main`
|
||||
height: ${isIOS && !isFirefox ? "calc(var(--vh, 1vh) * 100)" : "100vh"};
|
||||
width: 100vw;
|
||||
@ -19,6 +21,15 @@ const StyledMain = styled.main`
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
${!isMobileOnly &&
|
||||
css`
|
||||
@media ${mobile} {
|
||||
height: ${isIOS && !isFirefox
|
||||
? "calc(var(--vh, 1vh) * 100)"
|
||||
: "calc(100vh - 64px)"};
|
||||
}
|
||||
`}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
height: auto;
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled, { css } from "styled-components";
|
||||
import {
|
||||
isMobileOnly,
|
||||
isDesktop as isDesktopDevice,
|
||||
} from "react-device-detect";
|
||||
import { isMobile as isMobileUtils } from "@docspace/components/utils/device";
|
||||
import Backdrop from "@docspace/components/backdrop";
|
||||
import Aside from "@docspace/components/aside";
|
||||
|
||||
@ -12,7 +17,7 @@ import { useNavigate, useLocation } from "react-router-dom";
|
||||
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import { LayoutContextConsumer } from "../Layout/context";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import i18n from "./i18n";
|
||||
import PreparationPortalDialog from "../dialogs/PreparationPortalDialog";
|
||||
@ -61,6 +66,10 @@ const NavMenu = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [showNavMenu, setShowNavMenu] = React.useState(
|
||||
isMobileOnly || isMobileUtils()
|
||||
);
|
||||
|
||||
const [isBackdropVisible, setIsBackdropVisible] = React.useState(
|
||||
props.isBackdropVisible
|
||||
);
|
||||
@ -115,6 +124,16 @@ const NavMenu = (props) => {
|
||||
setIsNavHoverEnabled(false);
|
||||
};
|
||||
|
||||
const onResize = React.useCallback(() => {
|
||||
setShowNavMenu(isMobileUtils() || isMobileOnly);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDesktopDevice) window.addEventListener("resize", onResize);
|
||||
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
isAuthenticated,
|
||||
isLoaded,
|
||||
@ -128,6 +147,8 @@ const NavMenu = (props) => {
|
||||
const isAsideAvailable = !!asideContent;
|
||||
const hideHeader = isDesktop || (!showHeader && isFrame);
|
||||
|
||||
if (!showNavMenu) return <></>;
|
||||
|
||||
const isPreparationPortal = location.pathname === "/preparation-portal";
|
||||
return (
|
||||
<LayoutContextConsumer>
|
||||
|
@ -36,7 +36,7 @@ const StyledNav = styled.nav`
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
padding: 0 0 0 16px;
|
||||
padding: 0 16px 0 16px;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
|
@ -5,7 +5,7 @@ import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { Link as LinkWithoutRedirect } from "react-router-dom";
|
||||
import { isMobileOnly, isMobile } from "react-device-detect";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isDesktop, tablet, mobile } from "@docspace/components/utils/device";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
|
@ -350,7 +350,7 @@ class FilesTableHeader extends React.Component {
|
||||
setIsLoading(true);
|
||||
|
||||
window.DocSpace.navigate(
|
||||
`${window.DocSpace.location.pathname}?${newFilter.toUrlParams}`
|
||||
`${window.DocSpace.location.pathname}?${newFilter.toUrlParams()}`
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,11 @@ import { inject, observer } from "mobx-react";
|
||||
import Section from "@docspace/common/components/Section";
|
||||
import withLoading from "SRC_DIR/HOCs/withLoading";
|
||||
//import commonIconsStyles from "@docspace/components/utils/common-icons-style";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import HistoryHeader from "../categories/developer-tools/Webhooks/WebhookHistory/sub-components/HistoryHeader";
|
||||
import DetailsNavigationHeader from "../categories/developer-tools/Webhooks/WebhookEventDetails/sub-components/DetailsNavigationHeader";
|
||||
|
||||
const ArticleSettings = React.memo(() => {
|
||||
return (
|
||||
<Article>
|
||||
@ -20,23 +25,29 @@ const ArticleSettings = React.memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const Layout = ({
|
||||
currentProductId,
|
||||
setCurrentProductId,
|
||||
language,
|
||||
children,
|
||||
addUsers,
|
||||
}) => {
|
||||
const Layout = ({ currentProductId, setCurrentProductId, language, children, addUsers }) => {
|
||||
useEffect(() => {
|
||||
currentProductId !== "settings" && setCurrentProductId("settings");
|
||||
}, [language, currentProductId, setCurrentProductId]);
|
||||
|
||||
const { id, eventId } = useParams();
|
||||
|
||||
const webhookHistoryPath = `/portal-settings/developer-tools/webhooks/${id}`;
|
||||
const webhookDetailsPath = `/portal-settings/developer-tools/webhooks/${id}/${eventId}`;
|
||||
const currentPath = window.location.pathname;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ArticleSettings />
|
||||
<Section withBodyScroll={true} settingsStudio={true}>
|
||||
<Section.SectionHeader>
|
||||
<SectionHeaderContent />
|
||||
{currentPath === webhookHistoryPath ? (
|
||||
<HistoryHeader />
|
||||
) : currentPath === webhookDetailsPath ? (
|
||||
<DetailsNavigationHeader />
|
||||
) : (
|
||||
<SectionHeaderContent />
|
||||
)}
|
||||
</Section.SectionHeader>
|
||||
|
||||
<Section.SectionBody>{children}</Section.SectionBody>
|
||||
|
@ -0,0 +1,434 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Box from "@docspace/components/box";
|
||||
import TextInput from "@docspace/components/text-input";
|
||||
import Textarea from "@docspace/components/textarea";
|
||||
import Label from "@docspace/components/label";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import Button from "@docspace/components/button";
|
||||
import ComboBox from "@docspace/components/combobox";
|
||||
import Heading from "@docspace/components/heading";
|
||||
import { tablet } from "@docspace/components/utils/device";
|
||||
import { objectToGetParams, loadScript } from "@docspace/common/utils";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import BreakpointWarning from "SRC_DIR/components/BreakpointWarning";
|
||||
import { SortByFieldName } from "../../../../../helpers/constants";
|
||||
|
||||
const Controls = styled(Box)`
|
||||
width: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
min-width: fit-content;
|
||||
}
|
||||
`;
|
||||
|
||||
const ControlsGroup = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
const Frame = styled(Box)`
|
||||
margin-top: 16px;
|
||||
|
||||
> div {
|
||||
border: 1px dashed gray;
|
||||
border-radius: 3px;
|
||||
min-width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Buttons = styled(Box)`
|
||||
margin-top: 16px;
|
||||
button {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Container = styled(Box)`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
`;
|
||||
|
||||
const Preview = styled(Box)`
|
||||
width: 50%;
|
||||
flex-direction: row;
|
||||
|
||||
.frameStyle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const PortalIntegration = (props) => {
|
||||
const { t, setDocumentTitle } = props;
|
||||
|
||||
setDocumentTitle(t("JavascriptSdk"));
|
||||
|
||||
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
|
||||
|
||||
const dataSortBy = [
|
||||
{
|
||||
key: SortByFieldName.ModifiedDate,
|
||||
label: t("Common:LastModifiedDate"),
|
||||
default: true,
|
||||
},
|
||||
{ key: SortByFieldName.Name, label: t("Common:Title") },
|
||||
{ key: SortByFieldName.Type, label: t("Common:Type") },
|
||||
{ key: SortByFieldName.Size, label: t("Common:Size") },
|
||||
{ key: SortByFieldName.CreationDate, label: t("Files:ByCreation") },
|
||||
{ key: SortByFieldName.Author, label: t("Files:ByAuthor") },
|
||||
];
|
||||
|
||||
const dataSortOrder = [
|
||||
{ key: "descending", label: t("Descending"), default: true },
|
||||
{ key: "ascending", label: t("Ascending") },
|
||||
];
|
||||
|
||||
const [config, setConfig] = useState({
|
||||
width: "100%",
|
||||
height: "400px",
|
||||
frameId: "ds-frame",
|
||||
showHeader: false,
|
||||
showTitle: true,
|
||||
showArticle: false,
|
||||
showFilter: false,
|
||||
});
|
||||
|
||||
const [sortBy, setSortBy] = useState(dataSortBy[0]);
|
||||
const [sortOrder, setSortOrder] = useState(dataSortOrder[0]);
|
||||
const [withSubfolders, setWithSubfolders] = useState(false);
|
||||
|
||||
const params = objectToGetParams(config);
|
||||
|
||||
const frameId = config.frameId || "ds-frame";
|
||||
|
||||
const destroyFrame = () => {
|
||||
DocSpace.destroyFrame();
|
||||
};
|
||||
|
||||
const loadFrame = () => {
|
||||
const script = document.getElementById("integration");
|
||||
|
||||
if (script) {
|
||||
destroyFrame();
|
||||
script.remove();
|
||||
}
|
||||
|
||||
const params = objectToGetParams(config);
|
||||
|
||||
loadScript(`${scriptUrl}${params}`, "integration");
|
||||
};
|
||||
|
||||
const onChangeWidth = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, width: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeHeight = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, height: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeFolderId = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, folder: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeFrameId = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, frameId: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeWithSubfolders = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, withSubfolders: !withSubfolders };
|
||||
});
|
||||
|
||||
setWithSubfolders(!withSubfolders);
|
||||
};
|
||||
|
||||
const onChangeSortBy = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, sortby: item.key };
|
||||
});
|
||||
|
||||
setSortBy(item);
|
||||
};
|
||||
|
||||
const onChangeSortOrder = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, sortorder: item.key };
|
||||
});
|
||||
|
||||
setSortOrder(item);
|
||||
};
|
||||
|
||||
const onChangeFilterType = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, filterType: item.key };
|
||||
});
|
||||
|
||||
setFilterType(item);
|
||||
};
|
||||
|
||||
const onChangeDisplayType = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, viewAs: item.key };
|
||||
});
|
||||
|
||||
setDisplayType(item);
|
||||
};
|
||||
|
||||
const onChangeShowHeader = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showHeader: !config.showHeader };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowTitle = () => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showTitle: !config.showTitle };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowArticle = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showArticle: !config.showArticle };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowFilter = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showFilter: !config.showFilter };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeCount = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, count: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangePage = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, page: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeSearch = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, search: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeAuthor = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, authorType: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<BreakpointWarning sectionName={t("JavascriptSdk")} />
|
||||
) : (
|
||||
<Container>
|
||||
<Controls>
|
||||
<Heading level={1} size="small">
|
||||
{t("WindowParameters")}
|
||||
</Heading>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FrameId")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeFrameId}
|
||||
placeholder={t("EnterId")}
|
||||
value={config.frameId}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Width")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeWidth}
|
||||
placeholder={t("EnterWidth")}
|
||||
value={config.width}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Height")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeHeight}
|
||||
placeholder={t("EnterHeight")}
|
||||
value={config.height}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<Checkbox
|
||||
label={t("Header")}
|
||||
onChange={onChangeShowHeader}
|
||||
isChecked={config.showHeader}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Common:Title")}
|
||||
onChange={onChangeShowTitle}
|
||||
isChecked={config.showTitle}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Menu")}
|
||||
onChange={onChangeShowArticle}
|
||||
isChecked={config.showArticle}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Files:Filter")}
|
||||
onChange={onChangeShowFilter}
|
||||
isChecked={config.showFilter}
|
||||
/>
|
||||
<Heading level={1} size="small">
|
||||
{t("DataDisplay")}
|
||||
</Heading>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FolderId")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeFolderId}
|
||||
placeholder={t("EnterId")}
|
||||
value={config.folder}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("ItemsCount")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeCount}
|
||||
placeholder={t("EnterCount")}
|
||||
value={config.count}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Page")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangePage}
|
||||
placeholder={t("EnterPage")}
|
||||
value={config.page}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("SearchTerm")} />
|
||||
<Box
|
||||
style={{ flexDirection: "row", display: "flex", gap: "16px" }}
|
||||
>
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeSearch}
|
||||
placeholder={t("Common:Search")}
|
||||
value={config.search}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Files:WithSubfolders")}
|
||||
onChange={onChangeWithSubfolders}
|
||||
isChecked={withSubfolders}
|
||||
/>
|
||||
</Box>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Files:ByAuthor")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeAuthor}
|
||||
placeholder={t("Common:EnterName")}
|
||||
value={config.authorType}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Common:SortBy")} />
|
||||
<ComboBox
|
||||
onSelect={onChangeSortBy}
|
||||
options={dataSortBy}
|
||||
scaled={true}
|
||||
selectedOption={sortBy}
|
||||
displaySelectedOption
|
||||
directionY="top"
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("SortOrder")} />
|
||||
<ComboBox
|
||||
onSelect={onChangeSortOrder}
|
||||
options={dataSortOrder}
|
||||
scaled={true}
|
||||
selectedOption={sortOrder}
|
||||
displaySelectedOption
|
||||
directionY="top"
|
||||
/>
|
||||
</ControlsGroup>
|
||||
</Controls>
|
||||
<Preview>
|
||||
<Frame>
|
||||
<Box id={frameId} className="frameStyle">
|
||||
{t("Common:Preview")}
|
||||
</Box>
|
||||
</Frame>
|
||||
|
||||
<Buttons>
|
||||
<Button
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Common:Preview")}
|
||||
onClick={loadFrame}
|
||||
/>
|
||||
<Button
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Destroy")}
|
||||
onClick={destroyFrame}
|
||||
/>
|
||||
</Buttons>
|
||||
|
||||
<Heading level={1} size="xsmall">
|
||||
{t("CopyWindowCode")}
|
||||
</Heading>
|
||||
|
||||
<Textarea value={codeBlock} />
|
||||
</Preview>
|
||||
</Container>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup, auth }) => {
|
||||
const { settingsStore, setDocumentTitle } = auth;
|
||||
const { theme } = settingsStore;
|
||||
|
||||
return {
|
||||
theme,
|
||||
setDocumentTitle,
|
||||
};
|
||||
})(
|
||||
withTranslation(["JavascriptSdk", "Files", "EmbeddingPanel", "Common"])(
|
||||
observer(PortalIntegration)
|
||||
)
|
||||
);
|
@ -0,0 +1,52 @@
|
||||
import React, { useEffect, useTransition, Suspense } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import DetailsBar from "./sub-components/DetailsBar";
|
||||
import MessagesDetails from "./sub-components/MessagesDetails";
|
||||
import { WebhookDetailsLoader } from "../sub-components/Loaders";
|
||||
|
||||
const DetailsWrapper = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const EventDetailsHeader = styled.header`
|
||||
padding: 20px 0;
|
||||
`;
|
||||
|
||||
const WebhookEventDetails = (props) => {
|
||||
const { fetchEventData } = props;
|
||||
const { id, eventId } = useParams();
|
||||
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
useEffect(() => {
|
||||
startTransition(() => {
|
||||
fetchEventData(eventId);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Suspense fallback={WebhookDetailsLoader}>
|
||||
<DetailsWrapper>
|
||||
<main>
|
||||
<EventDetailsHeader>
|
||||
<Text fontWeight={600}>Webhook {id}</Text>
|
||||
<DetailsBar />
|
||||
</EventDetailsHeader>
|
||||
<MessagesDetails />
|
||||
</main>
|
||||
</DetailsWrapper>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { fetchEventData } = webhooksStore;
|
||||
|
||||
return { fetchEventData };
|
||||
})(observer(WebhookEventDetails));
|
@ -0,0 +1,99 @@
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import StatusBadge from "../../sub-components/StatusBadge";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const BarWrapper = styled.div`
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
margin-top: 24px;
|
||||
|
||||
background: ${(props) => (props.theme.isBase ? "#f8f9f9" : "#3D3D3D")};
|
||||
border-radius: 3px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.barItemHeader {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
BarWrapper.defaultProps = { theme: Base };
|
||||
|
||||
const BarItem = styled.div`
|
||||
box-sizing: border-box;
|
||||
height: 76px;
|
||||
padding: 16px;
|
||||
flex-basis: 25%;
|
||||
|
||||
@media (max-width: 1300px) {
|
||||
flex-basis: 50%;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const BarItemHeader = ({ children }) => (
|
||||
<Text as="h3" color="#A3A9AE" fontSize="12px" fontWeight={600} className="barItemHeader">
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
|
||||
const FlexWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const DetailsBar = ({ eventDetails }) => {
|
||||
const formatDate = (date) => {
|
||||
return moment(date).format("MMM D, YYYY, h:mm:ss A") + " UTC";
|
||||
};
|
||||
|
||||
const formattedDelivery = formatDate(eventDetails.delivery);
|
||||
const formattedCreationTime = formatDate(eventDetails.creationTime);
|
||||
|
||||
return (
|
||||
<BarWrapper>
|
||||
<BarItem>
|
||||
<BarItemHeader>Status</BarItemHeader>
|
||||
<FlexWrapper>
|
||||
<StatusBadge status={eventDetails.status} />
|
||||
</FlexWrapper>
|
||||
</BarItem>
|
||||
<BarItem>
|
||||
<BarItemHeader>Event ID</BarItemHeader>
|
||||
<Text isInline fontWeight={600}>
|
||||
{eventDetails.id}
|
||||
</Text>
|
||||
</BarItem>
|
||||
<BarItem>
|
||||
<BarItemHeader>Event time</BarItemHeader>
|
||||
<Text isInline fontWeight={600}>
|
||||
{formattedCreationTime}
|
||||
</Text>
|
||||
</BarItem>
|
||||
<BarItem>
|
||||
<BarItemHeader>Delivery time</BarItemHeader>
|
||||
<Text isInline fontWeight={600}>
|
||||
{formattedDelivery}
|
||||
</Text>
|
||||
</BarItem>
|
||||
</BarWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { eventDetails } = webhooksStore;
|
||||
|
||||
return { eventDetails };
|
||||
})(observer(DetailsBar));
|
@ -0,0 +1,83 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { NoBoxShadowToast } from "../../styled-components";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import ArrowPathReactSvgUrl from "PUBLIC_DIR/images/arrow.path.react.svg?url";
|
||||
import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
|
||||
|
||||
import Headline from "@docspace/common/components/Headline";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
|
||||
import { tablet } from "@docspace/components/utils/device";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: ${(props) => props.theme.backgroundColor};
|
||||
z-index: 310;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: calc(100vw - 32px);
|
||||
min-height: 69px;
|
||||
|
||||
.arrow-button {
|
||||
margin-right: 18.5px;
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 8px 0 8px 8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 18px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DetailsNavigationHeader = (props) => {
|
||||
const { retryWebhookEvent } = props;
|
||||
const { eventId } = useParams();
|
||||
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
const navigate = useNavigate();
|
||||
const onBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
const handleRetryEvent = async () => {
|
||||
await retryWebhookEvent(eventId);
|
||||
toastr.success(t("WebhookRedilivered"), <b>{t("Common:Done")}</b>);
|
||||
};
|
||||
|
||||
return (
|
||||
<HeaderContainer>
|
||||
<IconButton
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
size="17"
|
||||
isFill={true}
|
||||
onClick={onBack}
|
||||
className="arrow-button"
|
||||
/>
|
||||
<Headline type="content" truncate={true} className="headline">
|
||||
{t("WebhookDetails")}
|
||||
</Headline>
|
||||
<IconButton iconName={RetryIcon} size="17" isFill={true} onClick={handleRetryEvent} />
|
||||
|
||||
<NoBoxShadowToast />
|
||||
</HeaderContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { retryWebhookEvent } = webhooksStore;
|
||||
|
||||
return { retryWebhookEvent };
|
||||
})(observer(DetailsNavigationHeader));
|
@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Submenu from "@docspace/components/submenu";
|
||||
|
||||
import RequestDetails from "./RequestDetails";
|
||||
import ResponseDetails from "./ResponseDetails";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const SubmenuWrapper = styled.div`
|
||||
.sticky {
|
||||
z-index: 3;
|
||||
|
||||
top: ${() => (isMobileOnly ? "68px" : "0px")};
|
||||
}
|
||||
`;
|
||||
|
||||
const MessagesDetails = ({ eventDetails }) => {
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
const menuData = [
|
||||
{
|
||||
id: "webhookRequest",
|
||||
name: t("Request"),
|
||||
content: <RequestDetails />,
|
||||
},
|
||||
];
|
||||
|
||||
if (eventDetails.status >= 200 && eventDetails.status < 500) {
|
||||
menuData.push({
|
||||
id: "webhookResponse",
|
||||
name: t("Response"),
|
||||
content: <ResponseDetails />,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<SubmenuWrapper>
|
||||
<Submenu data={menuData} startSelect={0} />
|
||||
</SubmenuWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { eventDetails } = webhooksStore;
|
||||
|
||||
return { eventDetails };
|
||||
})(observer(MessagesDetails));
|
@ -0,0 +1,110 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Text from "@docspace/components/text";
|
||||
import Textarea from "@docspace/components/textarea";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import DangerIcon from "PUBLIC_DIR/images/danger.toast.react.svg?url";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DetailsWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
||||
.textareaBody {
|
||||
height: 50vh !important;
|
||||
}
|
||||
|
||||
.mt-7 {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
`;
|
||||
const ErrorMessageTooltip = styled.div`
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 8px 12px;
|
||||
background: #f7cdbe;
|
||||
|
||||
box-shadow: 0px 5px 20px rgba(4, 15, 27, 0.07);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 16px;
|
||||
|
||||
color: black;
|
||||
|
||||
img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
function isJSON(jsonString) {
|
||||
try {
|
||||
const parsedJson = JSON.parse(jsonString);
|
||||
return parsedJson && typeof parsedJson === "object";
|
||||
} catch (e) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const RequestDetails = ({ eventDetails }) => {
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
return (
|
||||
<DetailsWrapper>
|
||||
{eventDetails.status === 0 && (
|
||||
<ErrorMessageTooltip>
|
||||
<img src={DangerIcon} alt="danger icon" />
|
||||
{t("FailedToConnect")}
|
||||
</ErrorMessageTooltip>
|
||||
)}
|
||||
<Text as="h3" fontWeight={600} className="mb-4 mt-7">
|
||||
{t("RequestPostHeader")}
|
||||
</Text>
|
||||
{!eventDetails.requestHeaders ? (
|
||||
<Textarea isDisabled />
|
||||
) : (
|
||||
<Textarea
|
||||
value={eventDetails.requestHeaders}
|
||||
enableCopy
|
||||
hasNumeration
|
||||
isFullHeight
|
||||
isJSONField
|
||||
copyInfoText={t("RequestHeaderCopied")}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Text as="h3" fontWeight={600} className="mb-4 mt-16">
|
||||
{t("RequestPostBody")}
|
||||
</Text>
|
||||
{isJSON(eventDetails.requestPayload) ? (
|
||||
<Textarea
|
||||
value={eventDetails.requestPayload}
|
||||
isJSONField
|
||||
enableCopy
|
||||
hasNumeration
|
||||
isFullHeight
|
||||
copyInfoText={t("RequestBodyCopied")}
|
||||
/>
|
||||
) : (
|
||||
<Textarea value={eventDetails.requestPayload} heightScale className="textareaBody" />
|
||||
)}
|
||||
</DetailsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { eventDetails } = webhooksStore;
|
||||
|
||||
return { eventDetails };
|
||||
})(observer(RequestDetails));
|
@ -0,0 +1,147 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import Textarea from "@docspace/components/textarea";
|
||||
import Button from "@docspace/components/button";
|
||||
import Text from "@docspace/components/text";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import json_beautifier from "csvjson-json_beautifier";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DetailsWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
||||
.textareaBody {
|
||||
height: 50vh !important;
|
||||
}
|
||||
|
||||
.mt-7 {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const LargePayloadStub = styled.div`
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 12px 10px;
|
||||
margin-top: 4px;
|
||||
|
||||
background: #f8f9f9;
|
||||
border: 1px solid #eceef1;
|
||||
border-radius: 3px;
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 16px;
|
||||
`}
|
||||
`;
|
||||
|
||||
function isJSON(jsonString) {
|
||||
try {
|
||||
const parsedJson = JSON.parse(jsonString);
|
||||
return parsedJson && typeof parsedJson === "object";
|
||||
} catch (e) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResponseDetails = ({ eventDetails }) => {
|
||||
const responsePayload = eventDetails.responsePayload?.trim();
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
const beautifiedJSON = isJSON(responsePayload)
|
||||
? json_beautifier(JSON.parse(responsePayload), {
|
||||
inlineShortArrays: true,
|
||||
})
|
||||
: responsePayload;
|
||||
|
||||
const numberOfLines = isJSON(responsePayload)
|
||||
? beautifiedJSON.split("\n").length
|
||||
: responsePayload.split("\n").length;
|
||||
|
||||
const openRawPayload = () => {
|
||||
const rawPayload = window.open("");
|
||||
isJSON(responsePayload)
|
||||
? rawPayload.document.write(beautifiedJSON.replace(/(?:\r\n|\r|\n)/g, "<br/>"))
|
||||
: rawPayload.document.write(responsePayload);
|
||||
rawPayload.focus();
|
||||
};
|
||||
|
||||
return (
|
||||
<DetailsWrapper>
|
||||
<Text as="h3" fontWeight={600} className="mb-4 mt-7">
|
||||
{t("ResponsePostHeader")}
|
||||
</Text>
|
||||
{isJSON(eventDetails.responseHeaders) ? (
|
||||
<Textarea
|
||||
value={eventDetails.responseHeaders}
|
||||
enableCopy
|
||||
hasNumeration
|
||||
isFullHeight
|
||||
isJSONField
|
||||
copyInfoText={t("ResponseHeaderCopied")}
|
||||
/>
|
||||
) : (
|
||||
<Textarea value={eventDetails.responseHeaders} heightScale className="textareaBody" />
|
||||
)}
|
||||
<Text as="h3" fontWeight={600} className="mb-4 mt-16">
|
||||
{t("ResponsePostBody")}
|
||||
</Text>
|
||||
{responsePayload.length > 4000 || numberOfLines > 100 ? (
|
||||
<LargePayloadStub>
|
||||
<Text fontWeight={600} color="#657077">
|
||||
{t("PayloadIsTooLarge")}
|
||||
</Text>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={openRawPayload}
|
||||
label={t("ViewRawPayload")}
|
||||
scale={isMobileOnly}
|
||||
/>
|
||||
</LargePayloadStub>
|
||||
) : responsePayload === "" ? (
|
||||
<Textarea isDisabled />
|
||||
) : isJSON(responsePayload) ? (
|
||||
<Textarea
|
||||
value={responsePayload}
|
||||
isJSONField
|
||||
enableCopy
|
||||
hasNumeration
|
||||
isFullHeight
|
||||
copyInfoText={t("ResponseBodyCopied")}
|
||||
/>
|
||||
) : (
|
||||
<Textarea
|
||||
value={responsePayload}
|
||||
enableCopy
|
||||
heightScale
|
||||
className="textareaBody"
|
||||
copyInfoText={t("ResponseBodyCopied")}
|
||||
/>
|
||||
)}
|
||||
</DetailsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { eventDetails } = webhooksStore;
|
||||
|
||||
return { eventDetails };
|
||||
})(observer(ResponseDetails));
|
@ -0,0 +1,116 @@
|
||||
import React, { useState, useEffect, useTransition, Suspense } from "react";
|
||||
import moment from "moment";
|
||||
import styled from "styled-components";
|
||||
|
||||
import HistoryFilterHeader from "./sub-components/HistoryFilterHeader";
|
||||
import WebhookHistoryTable from "./sub-components/WebhookHistoryTable";
|
||||
import { WebhookHistoryLoader } from "../sub-components/Loaders";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import EmptyFilter from "./sub-components/EmptyFilter";
|
||||
|
||||
const WebhookWrapper = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const parseUrl = (url) => {
|
||||
const urlObj = new URL(url);
|
||||
const searchParams = urlObj.searchParams;
|
||||
|
||||
const params = {};
|
||||
for (const [key, value] of searchParams) {
|
||||
params[key] = value;
|
||||
}
|
||||
params.deliveryDate =
|
||||
params.deliveryDate === "null" ? null : moment(params.deliveryDate, "YYYY-MM-DD");
|
||||
params.deliveryFrom = moment(params.deliveryFrom, "HH:mm");
|
||||
params.deliveryTo = moment(params.deliveryTo, "HH:mm");
|
||||
params.status = JSON.parse(params.status);
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
function hasNoSearchParams(url) {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.search === "";
|
||||
}
|
||||
|
||||
const WebhookHistory = (props) => {
|
||||
const {
|
||||
historyItems,
|
||||
fetchHistoryItems,
|
||||
emptyCheckedIds,
|
||||
clearHistoryFilters,
|
||||
setHistoryFilters,
|
||||
formatFilters,
|
||||
} = props;
|
||||
|
||||
const [isFetchFinished, setIsFetchFinished] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
const fetchItems = async () => {
|
||||
if (hasNoSearchParams(window.location)) {
|
||||
await fetchHistoryItems({
|
||||
configId: id,
|
||||
});
|
||||
} else {
|
||||
const parsedParams = parseUrl(window.location);
|
||||
setHistoryFilters(parsedParams);
|
||||
await fetchHistoryItems({
|
||||
...formatFilters(parsedParams),
|
||||
configId: id,
|
||||
});
|
||||
}
|
||||
setIsFetchFinished(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
startTransition(fetchItems);
|
||||
return clearHistoryFilters;
|
||||
}, []);
|
||||
|
||||
const applyFilters = async ({ deliveryFrom, deliveryTo, groupStatus }) => {
|
||||
emptyCheckedIds();
|
||||
const params = { configId: id, deliveryFrom, deliveryTo, groupStatus };
|
||||
|
||||
await fetchHistoryItems(params);
|
||||
};
|
||||
|
||||
return (
|
||||
<WebhookWrapper>
|
||||
<Suspense fallback={<WebhookHistoryLoader />}>
|
||||
<main>
|
||||
<HistoryFilterHeader applyFilters={applyFilters} />
|
||||
{historyItems.length === 0 && isFetchFinished ? (
|
||||
<EmptyFilter applyFilters={applyFilters} />
|
||||
) : (
|
||||
<WebhookHistoryTable />
|
||||
)}
|
||||
</main>
|
||||
</Suspense>
|
||||
</WebhookWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const {
|
||||
historyItems,
|
||||
fetchHistoryItems,
|
||||
emptyCheckedIds,
|
||||
clearHistoryFilters,
|
||||
setHistoryFilters,
|
||||
formatFilters,
|
||||
} = webhooksStore;
|
||||
|
||||
return {
|
||||
historyItems,
|
||||
fetchHistoryItems,
|
||||
emptyCheckedIds,
|
||||
clearHistoryFilters,
|
||||
setHistoryFilters,
|
||||
formatFilters,
|
||||
};
|
||||
})(observer(WebhookHistory));
|
@ -0,0 +1,88 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import EmptyFilterImg from "PUBLIC_DIR/images/empty_filter.react.svg?url";
|
||||
import EmptyFilterDarkImg from "PUBLIC_DIR/images/empty_filter_dark.react.svg?url";
|
||||
import ClearEmptyFilterIcon from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import Text from "@docspace/components/text";
|
||||
import Link from "@docspace/components/link";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const EmptyFilterWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 149px;
|
||||
`;
|
||||
|
||||
const EmptyFilterContent = styled.div`
|
||||
display: flex;
|
||||
|
||||
.emptyFilterText {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.clearFilter {
|
||||
display: block;
|
||||
margin-top: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clearFilterIcon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.emptyFilterHeading {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const EmptyFilter = (props) => {
|
||||
const { applyFilters, formatFilters, clearHistoryFilters, theme } = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const clearFilters = () => {
|
||||
clearHistoryFilters(null);
|
||||
applyFilters(
|
||||
formatFilters({
|
||||
deliveryDate: null,
|
||||
status: [],
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<EmptyFilterWrapper>
|
||||
<EmptyFilterContent>
|
||||
<img src={theme.isBase ? EmptyFilterImg : EmptyFilterDarkImg} alt="Empty filter" />
|
||||
<div className="emptyFilterText">
|
||||
<Text fontSize="16px" fontWeight={700} as="p" className="emptyFilterHeading">
|
||||
{t("Common:NotFoundTitle")}
|
||||
</Text>
|
||||
<Text fontSize="12px" color={theme.isBase ? "#555F65" : "rgba(255, 255, 255, 0.6)"}>
|
||||
{t("NoResultsMatched")}
|
||||
</Text>
|
||||
<span className="clearFilter" onClick={clearFilters}>
|
||||
<img src={ClearEmptyFilterIcon} alt={t("ClearFilter")} className="clearFilterIcon" />
|
||||
<Link
|
||||
color={theme.isBase ? "#657077" : "inherit"}
|
||||
isHovered
|
||||
fontWeight={600}
|
||||
type="action">
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
</EmptyFilterContent>
|
||||
</EmptyFilterWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore, auth }) => {
|
||||
const { formatFilters, clearHistoryFilters } = webhooksStore;
|
||||
const { theme } = auth.settingsStore;
|
||||
|
||||
return { formatFilters, clearHistoryFilters, theme };
|
||||
})(observer(EmptyFilter));
|
@ -0,0 +1,225 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import moment from "moment";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import SelectorAddButton from "@docspace/components/selector-add-button";
|
||||
import SelectedItem from "@docspace/components/selected-item";
|
||||
|
||||
import Calendar from "@docspace/components/calendar";
|
||||
import TimePicker from "@docspace/components/time-picker";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const TimePickerCell = styled.span`
|
||||
margin-left: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.timePickerItem {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCalendar = styled(Calendar)`
|
||||
position: absolute;
|
||||
${(props) =>
|
||||
props.isMobile &&
|
||||
css`
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
`}
|
||||
`;
|
||||
|
||||
const DeliveryDatePicker = ({
|
||||
Selectors,
|
||||
filters,
|
||||
setFilters,
|
||||
isApplied,
|
||||
setIsApplied,
|
||||
isTimeOpen,
|
||||
setIsTimeOpen,
|
||||
}) => {
|
||||
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
|
||||
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
const calendarRef = useRef();
|
||||
const selectorRef = useRef();
|
||||
|
||||
const setDeliveryDate = (date) => {
|
||||
setFilters((prevFilters) => ({ ...prevFilters, deliveryDate: date }));
|
||||
};
|
||||
const setDeliveryFrom = (date) => {
|
||||
setFilters((prevFilters) => ({ ...prevFilters, deliveryFrom: date }));
|
||||
};
|
||||
const setDeliveryTo = (date) => {
|
||||
setFilters((prevFilters) => ({ ...prevFilters, deliveryTo: date }));
|
||||
};
|
||||
|
||||
const toggleCalendar = () => setIsCalendarOpen((prevIsCalendarOpen) => !prevIsCalendarOpen);
|
||||
const closeCalendar = () => {
|
||||
setIsApplied(false);
|
||||
setIsCalendarOpen(false);
|
||||
};
|
||||
|
||||
const showTimePicker = () => setIsTimeOpen(true);
|
||||
|
||||
const deleteSelectedDate = (e) => {
|
||||
e.stopPropagation();
|
||||
setFilters((prevFilters) => ({
|
||||
deliveryDate: null,
|
||||
deliveryFrom: moment().startOf("day"),
|
||||
deliveryTo: moment().endOf("day"),
|
||||
status: prevFilters.status,
|
||||
}));
|
||||
setIsTimeOpen(false);
|
||||
setIsApplied(false);
|
||||
};
|
||||
|
||||
const handleClick = (e) => {
|
||||
!selectorRef?.current?.contains(e.target) &&
|
||||
!calendarRef?.current?.contains(e.target) &&
|
||||
setIsCalendarOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("click", handleClick, { capture: true });
|
||||
return () => document.removeEventListener("click", handleClick, { capture: true });
|
||||
}, []);
|
||||
|
||||
const CalendarElement = () => (
|
||||
<StyledCalendar
|
||||
selectedDate={filters.deliveryDate}
|
||||
setSelectedDate={setDeliveryDate}
|
||||
onChange={closeCalendar}
|
||||
isMobile={isMobileOnly}
|
||||
forwardedRef={calendarRef}
|
||||
/>
|
||||
);
|
||||
|
||||
const DateSelector = () => (
|
||||
<div>
|
||||
<SelectorAddButton title={t("Add")} onClick={toggleCalendar} style={{ marginRight: "8px" }} />
|
||||
<Text isInline fontWeight={600} color="#A3A9AE">
|
||||
{t("SelectDate")}
|
||||
</Text>
|
||||
{isCalendarOpen && <CalendarElement />}
|
||||
</div>
|
||||
);
|
||||
|
||||
const SelectedDate = () => (
|
||||
<SelectedItem
|
||||
onClose={deleteSelectedDate}
|
||||
text={moment(filters.deliveryDate).format("DD MMM YYYY")}
|
||||
/>
|
||||
);
|
||||
|
||||
const SelectedDateWithCalendar = () => (
|
||||
<div>
|
||||
<SelectedItem
|
||||
onClose={deleteSelectedDate}
|
||||
text={moment(filters.deliveryDate).format("DD MMM YYYY")}
|
||||
onClick={toggleCalendar}
|
||||
/>
|
||||
{isCalendarOpen && <CalendarElement />}
|
||||
</div>
|
||||
);
|
||||
|
||||
const SelectedDateTime = () => (
|
||||
<div>
|
||||
<SelectedItem
|
||||
onClose={deleteSelectedDate}
|
||||
text={
|
||||
moment(filters.deliveryDate).format("DD MMM YYYY") +
|
||||
" " +
|
||||
moment(filters.deliveryFrom).format("HH:mm") +
|
||||
" - " +
|
||||
moment(filters.deliveryTo).format("HH:mm")
|
||||
}
|
||||
onClick={toggleCalendar}
|
||||
/>
|
||||
{isCalendarOpen && <CalendarElement />}
|
||||
</div>
|
||||
);
|
||||
|
||||
const TimeSelectorAdder = () => (
|
||||
<TimePickerCell>
|
||||
<SelectorAddButton title={t("Add")} onClick={showTimePicker} style={{ marginRight: "8px" }} />
|
||||
<Text isInline fontWeight={600} color="#A3A9AE">
|
||||
{t("SelectDeliveryTime")}
|
||||
</Text>
|
||||
</TimePickerCell>
|
||||
);
|
||||
|
||||
const isEqualDates = (firstDate, secondDate) => {
|
||||
return firstDate.format() === secondDate.format();
|
||||
};
|
||||
|
||||
const isTimeEqual =
|
||||
isEqualDates(filters.deliveryFrom, filters.deliveryFrom.clone().startOf("day")) &&
|
||||
isEqualDates(filters.deliveryTo, filters.deliveryTo.clone().endOf("day"));
|
||||
|
||||
const isTimeValid = filters.deliveryTo > filters.deliveryFrom;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text fontWeight={600} fontSize="15px">
|
||||
{t("DeliveryDate")}
|
||||
</Text>
|
||||
<Selectors ref={selectorRef}>
|
||||
{filters.deliveryDate === null ? (
|
||||
<DateSelector />
|
||||
) : isApplied ? (
|
||||
isTimeEqual ? (
|
||||
<SelectedDateWithCalendar />
|
||||
) : (
|
||||
<SelectedDateTime />
|
||||
)
|
||||
) : (
|
||||
<SelectedDate />
|
||||
)}
|
||||
{filters.deliveryDate !== null &&
|
||||
!isApplied &&
|
||||
(isTimeOpen ? (
|
||||
<TimePickerCell>
|
||||
<span className="timePickerItem">
|
||||
<Text isInline fontWeight={600} color="#A3A9AE" style={{ marginRight: "8px" }}>
|
||||
{t("From")}
|
||||
</Text>
|
||||
<TimePicker
|
||||
date={filters.deliveryFrom}
|
||||
setDate={setDeliveryFrom}
|
||||
hasError={!isTimeValid}
|
||||
tabIndex={1}
|
||||
/>
|
||||
</span>
|
||||
<Text isInline fontWeight={600} color="#A3A9AE" style={{ marginRight: "8px" }}>
|
||||
{t("Before")}
|
||||
</Text>
|
||||
<TimePicker
|
||||
date={filters.deliveryTo}
|
||||
setDate={setDeliveryTo}
|
||||
hasError={!isTimeValid}
|
||||
tabIndex={2}
|
||||
/>
|
||||
</TimePickerCell>
|
||||
) : (
|
||||
<TimeSelectorAdder />
|
||||
))}
|
||||
</Selectors>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const {} = webhooksStore;
|
||||
|
||||
return {};
|
||||
})(observer(DeliveryDatePicker));
|
@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import Button from "@docspace/components/button";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const RoundedButton = styled(Button)`
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
padding: 13.5px 15px;
|
||||
|
||||
border-radius: 16px;
|
||||
margin-right: 7px;
|
||||
|
||||
line-height: 20px;
|
||||
`;
|
||||
|
||||
const StatusBadgeSelector = ({ label, statusCode, isStatusSelected, handleStatusClick }) => {
|
||||
const handleOnClick = () => handleStatusClick(statusCode);
|
||||
return (
|
||||
<RoundedButton label={label} onClick={handleOnClick} primary={isStatusSelected(statusCode)} />
|
||||
);
|
||||
};
|
||||
|
||||
const StatusPicker = ({ Selectors, filters, setFilters }) => {
|
||||
const { t } = useTranslation(["Webhooks", "People"]);
|
||||
|
||||
const StatusCodes = ["Not sent", "2XX", "3XX", "4XX", "5XX"];
|
||||
|
||||
const isStatusSelected = (statusCode) => {
|
||||
return filters.status.includes(statusCode);
|
||||
};
|
||||
const handleStatusClick = (statusCode) => {
|
||||
setFilters((prevFilters) => ({
|
||||
...prevFilters,
|
||||
status: prevFilters.status.includes(statusCode)
|
||||
? prevFilters.status.filter((statusItem) => statusItem !== statusCode)
|
||||
: [...prevFilters.status, statusCode],
|
||||
}));
|
||||
};
|
||||
|
||||
const StatusBadgeElements = StatusCodes.map((code) =>
|
||||
code === "Not sent" ? (
|
||||
<StatusBadgeSelector
|
||||
label={t("NotSent")}
|
||||
statusCode={code}
|
||||
isStatusSelected={isStatusSelected}
|
||||
handleStatusClick={handleStatusClick}
|
||||
key={code}
|
||||
/>
|
||||
) : (
|
||||
<StatusBadgeSelector
|
||||
label={code}
|
||||
statusCode={code}
|
||||
isStatusSelected={isStatusSelected}
|
||||
handleStatusClick={handleStatusClick}
|
||||
key={code}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text fontWeight={600} fontSize="15px">
|
||||
{t("People:UserStatus")}
|
||||
</Text>
|
||||
<Selectors>{StatusBadgeElements}</Selectors>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const {} = webhooksStore;
|
||||
|
||||
return {};
|
||||
})(observer(StatusPicker));
|
@ -0,0 +1,161 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import moment from "moment";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Button from "@docspace/components/button";
|
||||
import DeliveryDatePicker from "./DeliveryDatePicker";
|
||||
import StatusPicker from "./StatusPicker";
|
||||
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const DialogBodyWrapper = styled.div`
|
||||
margin-top: -4px;
|
||||
`;
|
||||
|
||||
const Footer = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
button:first-of-type {
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Selectors = styled.div`
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const Separator = styled.hr`
|
||||
border-top: 1px solid;
|
||||
border-color: ${(props) => (props.theme.isBase ? "#eceef1" : "#474747")};
|
||||
margin-bottom: 14px;
|
||||
`;
|
||||
|
||||
Separator.defaultProps = { theme: Base };
|
||||
|
||||
const constructUrl = (baseUrl, filters) => {
|
||||
const url = new URL(baseUrl, "http://127.0.0.1:8092/");
|
||||
url.searchParams.append("deliveryDate", filters.deliveryDate?.format("YYYY-MM-DD") || null);
|
||||
url.searchParams.append("deliveryFrom", filters.deliveryFrom.format("HH:mm"));
|
||||
url.searchParams.append("deliveryTo", filters.deliveryTo.format("HH:mm"));
|
||||
url.searchParams.append("status", JSON.stringify(filters.status));
|
||||
|
||||
return url.pathname + url.search;
|
||||
};
|
||||
|
||||
function areArraysEqual(array1, array2) {
|
||||
return array1.length === array2.length && array1.every((val, index) => val === array2[index]);
|
||||
}
|
||||
|
||||
const FilterDialog = (props) => {
|
||||
const { visible, closeModal, applyFilters, formatFilters, setHistoryFilters, historyFilters } =
|
||||
props;
|
||||
const { t } = useTranslation(["Webhooks", "Files", "Common"]);
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [filters, setFilters] = useState({
|
||||
deliveryDate: null,
|
||||
deliveryFrom: moment().startOf("day"),
|
||||
deliveryTo: moment().endOf("day"),
|
||||
status: [],
|
||||
});
|
||||
|
||||
const [isApplied, setIsApplied] = useState(false);
|
||||
const [isTimeOpen, setIsTimeOpen] = useState(false);
|
||||
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
const handleApplyFilters = () => {
|
||||
if (filters.deliveryTo > filters.deliveryFrom) {
|
||||
const params = formatFilters(filters);
|
||||
|
||||
setHistoryFilters(filters);
|
||||
setIsApplied(true);
|
||||
|
||||
applyFilters(params);
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (historyFilters === null) {
|
||||
if (filters.deliveryDate !== null || filters.status.length > 0) {
|
||||
setFilters({
|
||||
deliveryDate: null,
|
||||
deliveryFrom: moment().startOf("day"),
|
||||
deliveryTo: moment().endOf("day"),
|
||||
status: [],
|
||||
});
|
||||
}
|
||||
isLoaded && navigate(`/portal-settings/developer-tools/webhooks/${id}`);
|
||||
} else {
|
||||
setFilters(historyFilters);
|
||||
setIsTimeOpen(false);
|
||||
setIsApplied(true);
|
||||
navigate(constructUrl(`/portal-settings/developer-tools/webhooks/${id}`, historyFilters));
|
||||
}
|
||||
setIsLoaded(true);
|
||||
}, [historyFilters, visible]);
|
||||
|
||||
const areFiltersChanged =
|
||||
historyFilters !== null
|
||||
? areArraysEqual(filters.status, historyFilters.status) &&
|
||||
filters.deliveryDate === historyFilters?.deliveryDate &&
|
||||
filters.deliveryFrom === historyFilters.deliveryFrom &&
|
||||
filters.deliveryTo === historyFilters.deliveryTo
|
||||
: filters.deliveryDate === null && filters.status.length === 0;
|
||||
|
||||
return (
|
||||
<ModalDialog withFooterBorder visible={visible} onClose={closeModal} displayType="aside">
|
||||
<ModalDialog.Header>{t("Files:Filter")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<DialogBodyWrapper>
|
||||
<DeliveryDatePicker
|
||||
Selectors={Selectors}
|
||||
isApplied={isApplied}
|
||||
setIsApplied={setIsApplied}
|
||||
filters={filters}
|
||||
setFilters={setFilters}
|
||||
isTimeOpen={isTimeOpen}
|
||||
setIsTimeOpen={setIsTimeOpen}
|
||||
/>
|
||||
<Separator />
|
||||
<StatusPicker Selectors={Selectors} filters={filters} setFilters={setFilters} />
|
||||
<Separator />
|
||||
</DialogBodyWrapper>
|
||||
</ModalDialog.Body>
|
||||
{!areFiltersChanged && (
|
||||
<ModalDialog.Footer>
|
||||
<Footer>
|
||||
<Button
|
||||
label={t("Common:ApplyButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
onClick={handleApplyFilters}
|
||||
/>
|
||||
<Button label={t("Common:CancelButton")} size="normal" onClick={closeModal} />
|
||||
</Footer>
|
||||
</ModalDialog.Footer>
|
||||
)}
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { formatFilters, setHistoryFilters, historyFilters } = webhooksStore;
|
||||
|
||||
return { formatFilters, setHistoryFilters, historyFilters };
|
||||
})(observer(FilterDialog));
|
@ -0,0 +1,131 @@
|
||||
import React, { useState } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
import FilterReactSvrUrl from "PUBLIC_DIR/images/filter.react.svg?url";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import FilterDialog from "./FilterDialog";
|
||||
import StatusBar from "./StatusBar";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
|
||||
const ListHeader = styled.header`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
${() =>
|
||||
isMobile &&
|
||||
css`
|
||||
margin-top: 9px;
|
||||
`}
|
||||
${() =>
|
||||
isMobileOnly &&
|
||||
css`
|
||||
margin-top: 35px;
|
||||
padding-right: 8px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ListHeading = styled(Text)`
|
||||
line-height: 22px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const FilterButton = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
z-index: ${(props) => (props.isGroupMenuVisible ? 199 : 201)};
|
||||
|
||||
border: 1px solid;
|
||||
border-color: ${(props) => (props.theme.isBase ? "#d0d5da" : "rgb(71, 71, 71)")};
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:hover {
|
||||
border-color: #a3a9ae;
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.iconButton.hoverColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
z-index: 203;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #4781d1;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
right: -2px;
|
||||
}
|
||||
`;
|
||||
|
||||
FilterButton.defaultProps = { theme: Base };
|
||||
|
||||
const HistoryFilterHeader = (props) => {
|
||||
const { applyFilters, historyFilters, isGroupMenuVisible } = props;
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
const { id } = useParams();
|
||||
|
||||
const [isFiltersVisible, setIsFiltersVisible] = useState(false);
|
||||
|
||||
const openFiltersModal = () => {
|
||||
setIsFiltersVisible(true);
|
||||
};
|
||||
|
||||
const closeFiltersModal = () => {
|
||||
setIsFiltersVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ListHeader>
|
||||
<ListHeading fontWeight={700} fontSize="16px">
|
||||
{t("Webhook")} {id}
|
||||
</ListHeading>
|
||||
|
||||
<FilterButton onClick={openFiltersModal} isGroupMenuVisible={isGroupMenuVisible}>
|
||||
<IconButton iconName={FilterReactSvrUrl} size={16} />
|
||||
<span hidden={historyFilters === null}></span>
|
||||
</FilterButton>
|
||||
</ListHeader>
|
||||
{historyFilters !== null && <StatusBar applyFilters={applyFilters} />}
|
||||
<FilterDialog
|
||||
visible={isFiltersVisible}
|
||||
closeModal={closeFiltersModal}
|
||||
applyFilters={applyFilters}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { historyFilters, isGroupMenuVisible } = webhooksStore;
|
||||
return {
|
||||
historyFilters,
|
||||
isGroupMenuVisible,
|
||||
};
|
||||
})(observer(HistoryFilterHeader));
|
@ -0,0 +1,260 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import ArrowPathReactSvgUrl from "PUBLIC_DIR/images/arrow.path.react.svg?url";
|
||||
import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
|
||||
|
||||
import Headline from "@docspace/common/components/Headline";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import { Hint } from "../../styled-components";
|
||||
|
||||
import { tablet } from "@docspace/components/utils/device";
|
||||
|
||||
import TableGroupMenu from "@docspace/components/table-container/TableGroupMenu";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import DropDownItem from "@docspace/components/drop-down-item";
|
||||
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { showLoader, hideLoader } from "@docspace/common/utils";
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: ${(props) => props.theme.backgroundColor};
|
||||
z-index: 201;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 70px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
${() =>
|
||||
isMobile &&
|
||||
css`
|
||||
margin-bottom: 11px;
|
||||
`}
|
||||
|
||||
${() =>
|
||||
isMobileOnly &&
|
||||
css`
|
||||
margin-top: 7px;
|
||||
margin-left: -14px;
|
||||
padding-left: 14px;
|
||||
margin-right: -14px;
|
||||
padding-right: 14px;
|
||||
`}
|
||||
|
||||
.arrow-button {
|
||||
margin-right: 18.5px;
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 8px 0 8px 8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
${() =>
|
||||
isMobileOnly &&
|
||||
css`
|
||||
margin-right: 13px;
|
||||
`}
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 18px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.table-container_group-menu {
|
||||
margin: 0 0 0 -20px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
flex: 0 0 auto;
|
||||
|
||||
width: calc(100% + 40px);
|
||||
height: 69px;
|
||||
|
||||
${() =>
|
||||
isMobile &&
|
||||
css`
|
||||
height: 60px;
|
||||
margin: 0 0 0 -16px;
|
||||
width: calc(100% + 32px);
|
||||
`}
|
||||
${() =>
|
||||
isMobileOnly &&
|
||||
css`
|
||||
position: absolute;
|
||||
height: 48px;
|
||||
margin: -35px 0 0 -17px;
|
||||
width: calc(100% + 32px);
|
||||
`}
|
||||
}
|
||||
`;
|
||||
|
||||
const HistoryHeader = (props) => {
|
||||
const {
|
||||
isGroupMenuVisible,
|
||||
checkedEventIds,
|
||||
checkAllIds,
|
||||
emptyCheckedIds,
|
||||
retryWebhookEvents,
|
||||
isIndeterminate,
|
||||
areAllIdsChecked,
|
||||
fetchHistoryItems,
|
||||
theme,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = props;
|
||||
const navigate = useNavigate();
|
||||
const onBack = () => {
|
||||
navigate("/portal-settings/developer-tools/webhooks");
|
||||
};
|
||||
const { t } = useTranslation(["Webhooks", "Common", "InfoPanel"]);
|
||||
const { id } = useParams();
|
||||
|
||||
const handleGroupSelection = (isChecked) => {
|
||||
isChecked ? checkAllIds() : emptyCheckedIds();
|
||||
};
|
||||
|
||||
const handleRetryAll = async () => {
|
||||
try {
|
||||
await emptyCheckedIds();
|
||||
const tempIds = checkedEventIds;
|
||||
showLoader();
|
||||
await retryWebhookEvents(tempIds);
|
||||
hideLoader();
|
||||
await fetchHistoryItems({
|
||||
...(historyFilters ? formatFilters(historyFilters) : {}),
|
||||
configId: id,
|
||||
});
|
||||
toastr.success(
|
||||
`${t("WebhookRedilivered")}: ${checkedEventIds.length}`,
|
||||
<b>{t("Common:Done")}</b>,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toastr.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
id: "retry-event-option",
|
||||
label: t("Retry"),
|
||||
onClick: handleRetryAll,
|
||||
iconUrl: RetryIcon,
|
||||
},
|
||||
];
|
||||
|
||||
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && emptyCheckedIds();
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
return () => window.removeEventListener("keyup", onKeyPress);
|
||||
}, []);
|
||||
|
||||
const menuItems = (
|
||||
<>
|
||||
<DropDownItem
|
||||
key="select-all-event-ids"
|
||||
label={t("Common:SelectAll")}
|
||||
data-index={0}
|
||||
onClick={checkAllIds}
|
||||
/>
|
||||
<DropDownItem
|
||||
key="unselect-all-event-ids"
|
||||
label={t("UnselectAll")}
|
||||
data-index={1}
|
||||
onClick={emptyCheckedIds}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
const NavigationHeader = () => (
|
||||
<>
|
||||
<IconButton
|
||||
iconName={ArrowPathReactSvgUrl}
|
||||
size="17"
|
||||
isFill={true}
|
||||
onClick={onBack}
|
||||
className="arrow-button"
|
||||
/>
|
||||
<Headline type="content" truncate={true} className="headline">
|
||||
{t("InfoPanel:SubmenuHistory")}
|
||||
</Headline>
|
||||
{/* <Hint
|
||||
backgroundColor={theme.isBase ? "#F8F9F9" : "#3D3D3D"}
|
||||
color={theme.isBase ? "#555F65" : "#FFFFFF"}>
|
||||
{t("EventHint")}
|
||||
</Hint> */}
|
||||
</>
|
||||
);
|
||||
|
||||
const GroupMenu = () => (
|
||||
<TableGroupMenu
|
||||
checkboxOptions={menuItems}
|
||||
onChange={handleGroupSelection}
|
||||
headerMenu={headerMenu}
|
||||
isChecked={areAllIdsChecked}
|
||||
isIndeterminate={isIndeterminate}
|
||||
withoutInfoPanelToggler
|
||||
/>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return emptyCheckedIds;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<HeaderContainer>
|
||||
{isMobileOnly ? (
|
||||
<>
|
||||
{isGroupMenuVisible && <GroupMenu />}
|
||||
<NavigationHeader />
|
||||
</>
|
||||
) : isGroupMenuVisible ? (
|
||||
<GroupMenu />
|
||||
) : (
|
||||
<NavigationHeader />
|
||||
)}
|
||||
</HeaderContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore, auth }) => {
|
||||
const {
|
||||
isGroupMenuVisible,
|
||||
checkAllIds,
|
||||
emptyCheckedIds,
|
||||
checkedEventIds,
|
||||
retryWebhookEvents,
|
||||
isIndeterminate,
|
||||
areAllIdsChecked,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = webhooksStore;
|
||||
|
||||
const { settingsStore } = auth;
|
||||
|
||||
const { theme } = settingsStore;
|
||||
|
||||
return {
|
||||
isGroupMenuVisible,
|
||||
checkAllIds,
|
||||
emptyCheckedIds,
|
||||
checkedEventIds,
|
||||
retryWebhookEvents,
|
||||
isIndeterminate,
|
||||
areAllIdsChecked,
|
||||
fetchHistoryItems,
|
||||
theme,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
};
|
||||
})(observer(HistoryHeader));
|
@ -0,0 +1,59 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import CrossReactSvgUrl from "PUBLIC_DIR/images/cross.react.svg?url";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledSelectedItem = styled.div`
|
||||
width: fit-content;
|
||||
height: 32px;
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
border-radius: 3px;
|
||||
|
||||
padding: 6px 8px;
|
||||
|
||||
margin-right: 4px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
background: ${(props) => props.theme.filterInput.selectedItems.background};
|
||||
|
||||
:hover {
|
||||
background: ${(props) => props.theme.filterInput.selectedItems.hoverBackground};
|
||||
}
|
||||
.selected-item_label {
|
||||
line-height: 20px;
|
||||
margin-right: 10px;
|
||||
max-width: 23ch;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSelectedItem.defaultProps = { theme: Base };
|
||||
|
||||
const SelectedItem = ({ label, removeSelectedItem }) => {
|
||||
if (!label) return <></>;
|
||||
|
||||
return (
|
||||
<StyledSelectedItem onClick={removeSelectedItem}>
|
||||
<Text className={"selected-item_label"} title={label} truncate={true} noSelect>
|
||||
{label}
|
||||
</Text>
|
||||
<IconButton
|
||||
className="selected-tag-removed"
|
||||
iconName={CrossReactSvgUrl}
|
||||
size={12}
|
||||
onClick={removeSelectedItem}
|
||||
isFill
|
||||
/>
|
||||
</StyledSelectedItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectedItem);
|
@ -0,0 +1,121 @@
|
||||
import React, { useEffect } from "react";
|
||||
import moment from "moment";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import SelectedItem from "./SelectedItem";
|
||||
import Link from "@docspace/components/link";
|
||||
|
||||
const StatusBarWrapper = styled.div`
|
||||
margin-top: 9px;
|
||||
|
||||
.statusBarItem:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.statusActionItem {
|
||||
margin-left: 12px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StatusBar = (props) => {
|
||||
const {
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
applyFilters,
|
||||
clearHistoryFilters,
|
||||
clearDate,
|
||||
unselectStatus,
|
||||
} = props;
|
||||
|
||||
const clearAll = () => {
|
||||
applyFilters(
|
||||
formatFilters({
|
||||
deliveryDate: null,
|
||||
status: [],
|
||||
}),
|
||||
);
|
||||
clearHistoryFilters();
|
||||
};
|
||||
|
||||
const SelectedDateTime = () => {
|
||||
return (
|
||||
<SelectedItem
|
||||
label={
|
||||
moment(historyFilters.deliveryDate).format("DD MMM YYYY") +
|
||||
" " +
|
||||
moment(historyFilters.deliveryFrom).format("HH:mm") +
|
||||
" - " +
|
||||
moment(historyFilters.deliveryTo).format("HH:mm")
|
||||
}
|
||||
removeSelectedItem={clearDate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectedDate = () => (
|
||||
<SelectedItem
|
||||
label={moment(historyFilters.deliveryDate).format("DD MMM YYYY")}
|
||||
removeSelectedItem={clearDate}
|
||||
/>
|
||||
);
|
||||
|
||||
const SelectedStatuses = historyFilters.status.map((statusCode) => (
|
||||
<SelectedItem
|
||||
label={statusCode}
|
||||
key={statusCode}
|
||||
removeSelectedItem={() => unselectStatus(statusCode)}
|
||||
/>
|
||||
));
|
||||
|
||||
const isEqualDates = (firstDate, secondDate) => {
|
||||
return firstDate.format() === secondDate.format();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
applyFilters(formatFilters(historyFilters));
|
||||
if (historyFilters.deliveryDate === null && historyFilters.status.length === 0) {
|
||||
clearHistoryFilters();
|
||||
}
|
||||
}, [historyFilters]);
|
||||
|
||||
return historyFilters.deliveryDate === null && historyFilters.status.length === 0 ? (
|
||||
""
|
||||
) : (
|
||||
<StatusBarWrapper>
|
||||
{historyFilters.deliveryDate !== null ? (
|
||||
!isEqualDates(
|
||||
historyFilters.deliveryFrom,
|
||||
historyFilters.deliveryFrom.clone().startOf("day"),
|
||||
) ||
|
||||
!isEqualDates(historyFilters.deliveryTo, historyFilters.deliveryTo.clone().endOf("day")) ? (
|
||||
<SelectedDateTime />
|
||||
) : (
|
||||
<SelectedDate />
|
||||
)
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{SelectedStatuses}
|
||||
{((historyFilters.deliveryDate !== null && historyFilters.status.length > 0) ||
|
||||
historyFilters.status.length > 1) && (
|
||||
<Link
|
||||
type="action"
|
||||
fontWeight={600}
|
||||
isHovered={true}
|
||||
onClick={clearAll}
|
||||
color="#A3A9AE"
|
||||
className="statusActionItem">
|
||||
Clear all
|
||||
</Link>
|
||||
)}
|
||||
</StatusBarWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { formatFilters, historyFilters, clearHistoryFilters, clearDate, unselectStatus } =
|
||||
webhooksStore;
|
||||
|
||||
return { formatFilters, historyFilters, clearHistoryFilters, clearDate, unselectStatus };
|
||||
})(observer(StatusBar));
|
@ -0,0 +1,103 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import Row from "@docspace/components/row";
|
||||
import { HistoryRowContent } from "./HistoryRowContent";
|
||||
|
||||
import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
|
||||
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const HistoryRow = (props) => {
|
||||
const {
|
||||
historyItem,
|
||||
sectionWidth,
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
const redirectToDetails = () => navigate(window.location.pathname + `/${historyItem.id}`);
|
||||
const handleRetryEvent = async () => {
|
||||
await retryWebhookEvent(historyItem.id);
|
||||
await fetchHistoryItems({
|
||||
...(historyFilters ? formatFilters(historyFilters) : {}),
|
||||
configId: id,
|
||||
});
|
||||
toastr.success(t("WebhookRedilivered"), <b>{t("Common:Done")}</b>);
|
||||
};
|
||||
const handleOnSelect = () => toggleEventId(historyItem.id);
|
||||
const handleRowClick = (e) => {
|
||||
if (
|
||||
e.target.closest(".checkbox") ||
|
||||
e.target.closest(".table-container_row-checkbox") ||
|
||||
e.target.closest(".type-combobox") ||
|
||||
e.target.closest(".table-container_row-context-menu-wrapper") ||
|
||||
e.target.closest(".row_context-menu-wrapper") ||
|
||||
e.detail === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
toggleEventId(historyItem.id);
|
||||
};
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
key: "Webhook details dropdownItem",
|
||||
label: t("WebhookDetails"),
|
||||
icon: InfoIcon,
|
||||
onClick: redirectToDetails,
|
||||
},
|
||||
{
|
||||
key: "Retry dropdownItem",
|
||||
label: t("Retry"),
|
||||
icon: RetryIcon,
|
||||
onClick: handleRetryEvent,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
key={historyItem.id}
|
||||
contextOptions={contextOptions}
|
||||
checkbox
|
||||
checked={isIdChecked(historyItem.id)}
|
||||
onSelect={handleOnSelect}
|
||||
className={isIdChecked(historyItem.id) ? "row-item selected-row-item" : "row-item "}
|
||||
onClick={handleRowClick}>
|
||||
<HistoryRowContent sectionWidth={sectionWidth} historyItem={historyItem} />
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const {
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = webhooksStore;
|
||||
|
||||
return {
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
};
|
||||
})(observer(HistoryRow));
|
@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import RowContent from "@docspace/components/row-content";
|
||||
|
||||
import StatusBadge from "../../../../sub-components/StatusBadge";
|
||||
|
||||
const StyledRowContent = styled(RowContent)`
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.rowMainContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const ContentWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
`;
|
||||
|
||||
const StatusHeader = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const HistoryRowContent = ({ sectionWidth, historyItem }) => {
|
||||
const formattedDelivery = moment(historyItem.delivery).format("MMM D, YYYY, h:mm:ss A") + " UTC";
|
||||
return (
|
||||
<StyledRowContent sectionWidth={sectionWidth}>
|
||||
<ContentWrapper>
|
||||
<StatusHeader>
|
||||
<Text fontWeight={600} fontSize="14px" style={{ marginRight: "8px" }}>
|
||||
{historyItem.id}
|
||||
</Text>
|
||||
<StatusBadge status={historyItem.status} />
|
||||
</StatusHeader>
|
||||
<Text fontWeight={600} fontSize="12px" color="#A3A9AE">
|
||||
{formattedDelivery}
|
||||
</Text>
|
||||
</ContentWrapper>
|
||||
<span></span>
|
||||
</StyledRowContent>
|
||||
);
|
||||
};
|
@ -0,0 +1,90 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import RowContainer from "@docspace/components/row-container";
|
||||
import HistoryRow from "./HistoryRow";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin-top: 11px;
|
||||
|
||||
.row-list-item {
|
||||
cursor: pointer;
|
||||
padding-right: ${() => (isMobileOnly ? "5px" : "15px")};
|
||||
}
|
||||
.row-item::after {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
.row-list-item:has(.selected-row-item) {
|
||||
background-color: ${(props) => (props.theme.isBase ? "#f3f4f4" : "#282828")};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledRowContainer.defaultProps = { theme: Base };
|
||||
|
||||
const HistoryRowView = (props) => {
|
||||
const {
|
||||
historyItems,
|
||||
sectionWidth,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
hasMoreItems,
|
||||
totalItems,
|
||||
fetchMoreItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = props;
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (viewAs !== "table" && viewAs !== "row") return;
|
||||
|
||||
if (sectionWidth < 1025 || isMobile) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
const fetchMoreFiles = () => {
|
||||
const params = historyFilters === null ? {} : formatFilters(historyFilters);
|
||||
fetchMoreItems({ ...params, configId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRowContainer
|
||||
filesLength={historyItems.length}
|
||||
fetchMoreFiles={fetchMoreFiles}
|
||||
hasMoreFiles={hasMoreItems}
|
||||
itemCount={totalItems}
|
||||
draggable
|
||||
useReactWindow={true}
|
||||
itemHeight={59}>
|
||||
{historyItems.map((item) => (
|
||||
<HistoryRow key={item.id} historyItem={item} sectionWidth={sectionWidth} />
|
||||
))}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup, webhooksStore }) => {
|
||||
const { viewAs, setViewAs } = setup;
|
||||
const { historyItems, fetchMoreItems, hasMoreItems, totalItems, historyFilters, formatFilters } =
|
||||
webhooksStore;
|
||||
return {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
historyItems,
|
||||
fetchMoreItems,
|
||||
hasMoreItems,
|
||||
totalItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
};
|
||||
})(observer(HistoryRowView));
|
@ -0,0 +1,108 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import TableHeader from "@docspace/components/table-container/TableHeader";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const TABLE_VERSION = "5";
|
||||
const TABLE_COLUMNS = `webhooksHistoryColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const HistoryTableHeader = (props) => {
|
||||
const {
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
setHideColumns,
|
||||
} = props;
|
||||
const { t, ready } = useTranslation(["Webhooks", "People"]);
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Event ID",
|
||||
title: t("EventID"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 150,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Status",
|
||||
title: t("People:UserStatus"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Delivery",
|
||||
title: t("Delivery"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
ready && setColumns(getColumns(defaultColumns, userId));
|
||||
}, [ready]);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="32px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
setHideColumns={setHideColumns}
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
return {
|
||||
userId: auth.userStore.user.id,
|
||||
};
|
||||
})(observer(HistoryTableHeader));
|
@ -0,0 +1,139 @@
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import styled, { css } from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import TableRow from "@docspace/components/table-container/TableRow";
|
||||
import TableCell from "@docspace/components/table-container/TableCell";
|
||||
import Text from "@docspace/components/text";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import StatusBadge from "../../../../sub-components/StatusBadge";
|
||||
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.textOverflow {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isHighlight &&
|
||||
css`
|
||||
.table-container_cell {
|
||||
background-color: #f3f4f4;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: contents;
|
||||
`;
|
||||
|
||||
const HistoryTableRow = (props) => {
|
||||
const {
|
||||
item,
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
hideColumns,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
const redirectToDetails = () => navigate(window.location.pathname + `/${item.id}`);
|
||||
const handleRetryEvent = async () => {
|
||||
await retryWebhookEvent(item.id);
|
||||
await fetchHistoryItems({
|
||||
...(historyFilters ? formatFilters(historyFilters) : {}),
|
||||
configId: id,
|
||||
});
|
||||
toastr.success(t("WebhookRedilivered"), <b>{t("Common:Done")}</b>);
|
||||
};
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
key: "Webhook details dropdownItem",
|
||||
label: t("WebhookDetails"),
|
||||
icon: InfoIcon,
|
||||
onClick: redirectToDetails,
|
||||
},
|
||||
{
|
||||
key: "Retry dropdownItem",
|
||||
label: t("Retry"),
|
||||
icon: RetryIcon,
|
||||
onClick: handleRetryEvent,
|
||||
},
|
||||
];
|
||||
|
||||
const formattedDelivery = moment(item.delivery).format("MMM D, YYYY, h:mm:ss A") + " UTC";
|
||||
|
||||
const onChange = (e) => {
|
||||
if (
|
||||
e.target.closest(".checkbox") ||
|
||||
e.target.closest(".table-container_row-checkbox") ||
|
||||
e.target.closest(".type-combobox") ||
|
||||
e.target.closest(".table-container_row-context-menu-wrapper") ||
|
||||
e.detail === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
toggleEventId(item.id);
|
||||
};
|
||||
|
||||
const isChecked = isIdChecked(item.id);
|
||||
|
||||
return (
|
||||
<StyledWrapper className={isChecked ? "selected-table-row" : ""} onClick={onChange}>
|
||||
<StyledTableRow contextOptions={contextOptions} checked={isChecked} hideColumns={hideColumns}>
|
||||
<TableCell>
|
||||
<TableCell checked={isChecked} className="checkboxWrapper">
|
||||
<Checkbox onChange={onChange} isChecked={isChecked} />
|
||||
</TableCell>
|
||||
|
||||
<Text fontWeight={600}>{item.id}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusBadge status={item.status} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text fontWeight={600} fontSize="11px" className="textOverflow">
|
||||
{formattedDelivery}
|
||||
</Text>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const {
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
} = webhooksStore;
|
||||
|
||||
return {
|
||||
toggleEventId,
|
||||
isIdChecked,
|
||||
retryWebhookEvent,
|
||||
fetchHistoryItems,
|
||||
historyFilters,
|
||||
formatFilters,
|
||||
};
|
||||
})(observer(HistoryTableRow));
|
@ -0,0 +1,141 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
|
||||
import styled from "styled-components";
|
||||
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import TableContainer from "@docspace/components/table-container/TableContainer";
|
||||
import TableBody from "@docspace/components/table-container/TableBody";
|
||||
import HistoryTableHeader from "./HistoryTableHeader";
|
||||
import HistoryTableRow from "./HistoryTableRow";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const TableWrapper = styled(TableContainer)`
|
||||
margin-top: 0;
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding: 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: ${(props) => (props.theme.isBase ? "#f3f4f4" : "#282828")};
|
||||
}
|
||||
}
|
||||
|
||||
.table-list-item:has(.selected-table-row) {
|
||||
background-color: ${(props) => (props.theme.isBase ? "#f3f4f4" : "#282828")};
|
||||
}
|
||||
`;
|
||||
|
||||
TableWrapper.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "5";
|
||||
const COLUMNS_SIZE = `webhooksHistoryColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelWebhooksHistoryColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const HistoryTableView = (props) => {
|
||||
const {
|
||||
sectionWidth,
|
||||
historyItems,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
hasMoreItems,
|
||||
totalItems,
|
||||
fetchMoreItems,
|
||||
formatFilters,
|
||||
historyFilters,
|
||||
userId,
|
||||
} = props;
|
||||
|
||||
const tableRef = useRef(null);
|
||||
const [hideColumns, setHideColumns] = useState(false);
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (!sectionWidth) return;
|
||||
if (sectionWidth < 1025 || isMobile) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
const fetchMoreFiles = () => {
|
||||
const params = historyFilters === null ? {} : formatFilters(historyFilters);
|
||||
fetchMoreItems({ ...params, configId: id });
|
||||
};
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
return (
|
||||
<TableWrapper
|
||||
forwardedRef={tableRef}
|
||||
style={{
|
||||
gridTemplateColumns: "300px 100px 400px 24px",
|
||||
}}
|
||||
useReactWindow>
|
||||
<HistoryTableHeader
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
setHideColumns={setHideColumns}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={historyItems.length}
|
||||
fetchMoreFiles={fetchMoreFiles}
|
||||
hasMoreFiles={hasMoreItems}
|
||||
itemCount={totalItems}>
|
||||
{historyItems.map((item) => (
|
||||
<HistoryTableRow
|
||||
key={item.id}
|
||||
item={{ ...item, title: item.id }}
|
||||
hideColumns={hideColumns}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</TableWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup, webhooksStore, auth }) => {
|
||||
const { viewAs, setViewAs } = setup;
|
||||
const { historyItems, fetchMoreItems, hasMoreItems, totalItems, formatFilters, historyFilters } =
|
||||
webhooksStore;
|
||||
const { id: userId } = auth.userStore.user;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
historyItems,
|
||||
fetchMoreItems,
|
||||
hasMoreItems,
|
||||
totalItems,
|
||||
formatFilters,
|
||||
historyFilters,
|
||||
userId,
|
||||
};
|
||||
})(observer(HistoryTableView));
|
@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
|
||||
import { Consumer } from "@docspace/components/utils/context";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import HistoryTableView from "./HistoryTableView";
|
||||
import HistoryRowView from "./HistoryRowView";
|
||||
|
||||
const WebhookHistoryTable = (props) => {
|
||||
const { viewAs } = props;
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<HistoryTableView sectionWidth={context.sectionWidth} />
|
||||
) : (
|
||||
<HistoryRowView sectionWidth={context.sectionWidth} />
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ setup }) => {
|
||||
const { viewAs } = setup;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
};
|
||||
})(observer(WebhookHistoryTable));
|
@ -0,0 +1,170 @@
|
||||
import Button from "@docspace/components/button";
|
||||
import React, { useState, useEffect, useTransition, Suspense } from "react";
|
||||
import WebhookDialog from "./sub-components/WebhookDialog";
|
||||
import WebhookInfo from "./sub-components/WebhookInfo";
|
||||
import WebhooksTable from "./sub-components/WebhooksTable";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import styled from "styled-components";
|
||||
import { WebhookConfigsLoader } from "./sub-components/Loaders";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
import { isMobile } from "@docspace/components/utils/device";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { DeleteWebhookDialog } from "./sub-components/DeleteWebhookDialog";
|
||||
import { NoBoxShadowToast } from "./styled-components";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
const MainWrapper = styled.div`
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
|
||||
.toggleButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonSeating = styled.div`
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
width: 100vw;
|
||||
height: 73px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: ${(props) => props.theme.backgroundColor};
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
ButtonSeating.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCreateButton = styled(Button)`
|
||||
width: calc(100% - 32px);
|
||||
`;
|
||||
|
||||
const Webhooks = (props) => {
|
||||
const {
|
||||
loadWebhooks,
|
||||
addWebhook,
|
||||
isWebhookExist,
|
||||
isWebhooksEmpty,
|
||||
setDocumentTitle,
|
||||
currentWebhook,
|
||||
editWebhook,
|
||||
deleteWebhook,
|
||||
} = props;
|
||||
|
||||
const { t, ready } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const [isPending, startTranslation] = useTransition();
|
||||
|
||||
setDocumentTitle(t("Webhooks"));
|
||||
|
||||
const [isCreateOpened, setIsCreateOpened] = useState(false);
|
||||
const [isSettingsOpened, setIsSettingsOpened] = useState(false);
|
||||
const [isDeleteOpened, setIsDeleteOpened] = useState(false);
|
||||
|
||||
const closeCreateModal = () => setIsCreateOpened(false);
|
||||
const openCreateModal = () => setIsCreateOpened(true);
|
||||
const closeSettingsModal = () => setIsSettingsOpened(false);
|
||||
const openSettingsModal = () => setIsSettingsOpened(true);
|
||||
const closeDeleteModal = () => setIsDeleteOpened(false);
|
||||
const openDeleteModal = () => setIsDeleteOpened(true);
|
||||
|
||||
const onCreateWebhook = async (webhookInfo) => {
|
||||
if (!isWebhookExist(webhookInfo)) {
|
||||
await addWebhook(webhookInfo);
|
||||
closeCreateModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleWebhookUpdate = async (webhookInfo) => {
|
||||
await editWebhook(currentWebhook, webhookInfo);
|
||||
toastr.success(t("WebhookEditedSuccessfully"), <b>{t("Common:Done")}</b>);
|
||||
};
|
||||
const handleWebhookDelete = async () => {
|
||||
await deleteWebhook(currentWebhook);
|
||||
toastr.success(t("WebhookRemoved"), <b>{t("Common:Done")}</b>);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
ready && startTranslation(loadWebhooks);
|
||||
}, [ready]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<WebhookConfigsLoader />}>
|
||||
<MainWrapper>
|
||||
<WebhookInfo />
|
||||
{isMobile() ? (
|
||||
<ButtonSeating>
|
||||
<StyledCreateButton
|
||||
label={t("CreateWebhook")}
|
||||
primary
|
||||
size={"normal"}
|
||||
onClick={openCreateModal}
|
||||
/>
|
||||
</ButtonSeating>
|
||||
) : (
|
||||
<Button label={t("CreateWebhook")} primary size={"small"} onClick={openCreateModal} />
|
||||
)}
|
||||
|
||||
{!isWebhooksEmpty && (
|
||||
<WebhooksTable openSettingsModal={openSettingsModal} openDeleteModal={openDeleteModal} />
|
||||
)}
|
||||
<WebhookDialog
|
||||
visible={isCreateOpened}
|
||||
onClose={closeCreateModal}
|
||||
header={t("CreateWebhook")}
|
||||
onSubmit={onCreateWebhook}
|
||||
/>
|
||||
<WebhookDialog
|
||||
visible={isSettingsOpened}
|
||||
onClose={closeSettingsModal}
|
||||
header={t("SettingsWebhook")}
|
||||
isSettingsModal={true}
|
||||
webhook={currentWebhook}
|
||||
onSubmit={handleWebhookUpdate}
|
||||
/>
|
||||
<DeleteWebhookDialog
|
||||
visible={isDeleteOpened}
|
||||
onClose={closeDeleteModal}
|
||||
header={t("DeleteWebhookForeverQuestion")}
|
||||
handleSubmit={handleWebhookDelete}
|
||||
/>
|
||||
<NoBoxShadowToast />
|
||||
</MainWrapper>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore, auth }) => {
|
||||
const {
|
||||
state,
|
||||
loadWebhooks,
|
||||
addWebhook,
|
||||
isWebhookExist,
|
||||
isWebhooksEmpty,
|
||||
currentWebhook,
|
||||
editWebhook,
|
||||
deleteWebhook,
|
||||
} = webhooksStore;
|
||||
const { setDocumentTitle } = auth;
|
||||
|
||||
return {
|
||||
state,
|
||||
loadWebhooks,
|
||||
addWebhook,
|
||||
isWebhookExist,
|
||||
isWebhooksEmpty,
|
||||
setDocumentTitle,
|
||||
currentWebhook,
|
||||
editWebhook,
|
||||
deleteWebhook,
|
||||
};
|
||||
})(observer(Webhooks));
|
@ -0,0 +1,26 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
export const Hint = styled.div`
|
||||
box-sizing: border-box;
|
||||
padding: 8px 12px;
|
||||
background: ${(props) => (props.backgroundColor ? props.backgroundColor : "#f8f7bf")};
|
||||
color: ${(props) => (props.color ? props.color : "initial")};
|
||||
border-radius: 6px;
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
|
||||
${(props) =>
|
||||
props.isTooltip &&
|
||||
css`
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
||||
width: 320px;
|
||||
`}
|
||||
`;
|
@ -0,0 +1,8 @@
|
||||
import Toast from "@docspace/components/toast";
|
||||
import styled from "styled-components";
|
||||
|
||||
export const NoBoxShadowToast = styled(Toast)`
|
||||
.Toastify__toast {
|
||||
box-shadow: none;
|
||||
}
|
||||
`;
|
@ -0,0 +1,2 @@
|
||||
export { Hint } from "./Hint";
|
||||
export { NoBoxShadowToast } from "./NoBoxShadowToast";
|
@ -0,0 +1,60 @@
|
||||
import React, { useEffect } from "react";
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
import styled from "styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const StyledBodyText = styled.div`
|
||||
line-height: 20px;
|
||||
`;
|
||||
|
||||
const Footer = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
button:first-of-type {
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const DeleteWebhookDialog = ({ visible, onClose, header, handleSubmit }) => {
|
||||
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && onClose();
|
||||
|
||||
const { t } = useTranslation(["Webhooks", "Common", "EmptyTrashDialog"]);
|
||||
|
||||
const cleanUpEvent = () => window.removeEventListener("keyup", onKeyPress);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
return cleanUpEvent;
|
||||
});
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
handleSubmit();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialog visible={visible} onClose={onClose} displayType="modal">
|
||||
<ModalDialog.Header>{header}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<StyledBodyText>{t("DeleteHint")}</StyledBodyText>
|
||||
</ModalDialog.Body>
|
||||
|
||||
<ModalDialog.Footer>
|
||||
<Footer>
|
||||
<Button
|
||||
label={t("EmptyTrashDialog:DeleteForeverButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
<Button label={t("Common:CancelButton")} size="normal" onClick={onClose} />
|
||||
</Footer>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import TextInput from "@docspace/components/text-input";
|
||||
|
||||
import Label from "@docspace/components/label";
|
||||
|
||||
const StyledLabel = styled(Label)`
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
line-height: 20px;
|
||||
|
||||
input {
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const LabledInput = ({
|
||||
label,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
name,
|
||||
mask,
|
||||
hasError,
|
||||
className,
|
||||
required = false,
|
||||
}) => {
|
||||
return (
|
||||
<StyledLabel text={label} className={className}>
|
||||
<TextInput
|
||||
name={name}
|
||||
placeholder={placeholder}
|
||||
tabIndex={1}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
required={required}
|
||||
hasError={hasError}
|
||||
{...(mask ? { mask: mask } : {})}
|
||||
/>
|
||||
</StyledLabel>
|
||||
);
|
||||
};
|
@ -0,0 +1,93 @@
|
||||
import React from "react";
|
||||
|
||||
import styled from "styled-components";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
const LoaderWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
||||
.webhookTextLoader {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.webhookButtonLoader {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.labelsLoader {
|
||||
width: 435px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.iconsLoader {
|
||||
width: 131px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.roundedStatusLoader {
|
||||
border-radius: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const NavContainerLoader = styled.nav`
|
||||
width: 184px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const TableHeaderLoader = styled.header`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 33px;
|
||||
`;
|
||||
|
||||
const TableRowLoader = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 27px;
|
||||
`;
|
||||
|
||||
export const WebhookConfigsLoader = () => {
|
||||
const RowLoader = () => (
|
||||
<TableRowLoader>
|
||||
<Loaders.Rectangle width="888px" height="20px" />
|
||||
<div className="iconsLoader">
|
||||
<Loaders.Rectangle width="28px" height="16px" className="roundedStatusLoader" />
|
||||
<Loaders.Rectangle width="16px" height="16px" />
|
||||
</div>
|
||||
</TableRowLoader>
|
||||
);
|
||||
|
||||
return (
|
||||
<LoaderWrapper>
|
||||
<NavContainerLoader>
|
||||
<Loaders.Rectangle width="82px" height="32px" />
|
||||
<Loaders.Rectangle width="82px" height="32px" />
|
||||
</NavContainerLoader>
|
||||
|
||||
<Loaders.Rectangle width="700px" height="88px" className="webhookTextLoader" />
|
||||
|
||||
<Loaders.Rectangle width="159px" height="32px" className="webhookButtonLoader" />
|
||||
|
||||
<TableHeaderLoader>
|
||||
<div className="labelsLoader">
|
||||
<Loaders.Rectangle width="51px" height="16px" />
|
||||
<Loaders.Rectangle width="60px" height="16px" />
|
||||
</div>
|
||||
<div className="iconsLoader">
|
||||
<Loaders.Rectangle width="62px" height="16px" />
|
||||
<Loaders.Rectangle width="16px" height="16px" />
|
||||
</div>
|
||||
</TableHeaderLoader>
|
||||
|
||||
<RowLoader />
|
||||
<RowLoader />
|
||||
<RowLoader />
|
||||
<RowLoader />
|
||||
<RowLoader />
|
||||
</LoaderWrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
|
||||
import styled from "styled-components";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
const LoaderWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.mb-16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mb-23 {
|
||||
margin-bottom: 23px;
|
||||
}
|
||||
|
||||
.mb-24 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DetailsWrapperLoader = styled.div`
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
const DetailsItemWrapper = styled.div`
|
||||
padding: 16px;
|
||||
margin-right: 40px;
|
||||
display: inline-block;
|
||||
`;
|
||||
|
||||
export const WebhookDetailsLoader = () => {
|
||||
const DetailsItemLoader = () => (
|
||||
<DetailsItemWrapper>
|
||||
<Loaders.Rectangle width="37px" height="16px" className="mb-5 display-block" />
|
||||
<Loaders.Rectangle width="180px" height="20px" />
|
||||
</DetailsItemWrapper>
|
||||
);
|
||||
|
||||
const MessageHeader = () => <Loaders.Rectangle width="130px" height="20px" className="mb-4" />;
|
||||
|
||||
return (
|
||||
<LoaderWrapper>
|
||||
<DetailsWrapperLoader>
|
||||
<Loaders.Rectangle width="80px" height="20px" className="mb-24 display-block" />
|
||||
<DetailsItemLoader />
|
||||
<DetailsItemLoader />
|
||||
<DetailsItemLoader />
|
||||
<DetailsItemLoader />
|
||||
</DetailsWrapperLoader>
|
||||
<div className=" mb-23">
|
||||
<Loaders.Rectangle width="43px" height="32px" className="mr-20" />
|
||||
<Loaders.Rectangle width="64px" height="32px" />
|
||||
</div>
|
||||
|
||||
<MessageHeader />
|
||||
<Loaders.Rectangle width="100%" height="212px" className="mb-16" />
|
||||
|
||||
<MessageHeader />
|
||||
<Loaders.Rectangle width="100%" height="469px" />
|
||||
</LoaderWrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1,68 @@
|
||||
import React from "react";
|
||||
|
||||
import styled from "styled-components";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
const LoaderWrapper = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const NavContainerLoader = styled.nav`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 17px;
|
||||
`;
|
||||
|
||||
const HistoryHeaderLoader = styled.header`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 27px;
|
||||
`;
|
||||
|
||||
const HistoryRowWrapper = styled.div`
|
||||
margin-bottom: 27px;
|
||||
|
||||
.historyIconLoader {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.historyContentLoader {
|
||||
display: inline-block;
|
||||
width: calc(100% - 36px);
|
||||
}
|
||||
`;
|
||||
|
||||
export const WebhookHistoryLoader = () => {
|
||||
const HistoryRowLoader = () => (
|
||||
<HistoryRowWrapper>
|
||||
<Loaders.Rectangle width="20px" height="20px" className="historyIconLoader" />
|
||||
<Loaders.Rectangle height="20px" className="historyContentLoader" />
|
||||
</HistoryRowWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
<LoaderWrapper>
|
||||
<NavContainerLoader>
|
||||
<Loaders.Rectangle width="118px" height="22px" />
|
||||
<Loaders.Rectangle width="32px" height="22px" />
|
||||
</NavContainerLoader>
|
||||
|
||||
<HistoryHeaderLoader>
|
||||
<Loaders.Rectangle width="51px" height="16px" />
|
||||
<Loaders.Rectangle width="60px" height="16px" />
|
||||
<Loaders.Rectangle width="60px" height="16px" />
|
||||
<Loaders.Rectangle width="62px" height="16px" />
|
||||
<Loaders.Rectangle width="16px" height="16px" />
|
||||
</HistoryHeaderLoader>
|
||||
|
||||
<HistoryRowLoader />
|
||||
<HistoryRowLoader />
|
||||
<HistoryRowLoader />
|
||||
<HistoryRowLoader />
|
||||
<HistoryRowLoader />
|
||||
<HistoryRowLoader />
|
||||
</LoaderWrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export { WebhookConfigsLoader } from "./WebhookConfigsLoader";
|
||||
export { WebhookHistoryLoader } from "./WebhookHistoryLoader";
|
||||
export { WebhookDetailsLoader } from "./WebhookDetailsLoader";
|
@ -0,0 +1,73 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.react.svg?url";
|
||||
|
||||
import RadioButtonGroup from "@docspace/components/radio-button-group";
|
||||
import Label from "@docspace/components/label";
|
||||
|
||||
import { Hint } from "../styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Header = styled.p`
|
||||
font-weight: 600;
|
||||
margin-top: 22px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
margin-left: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledInfoIcon = styled.img`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SSLVerification = ({ onChange, value }) => {
|
||||
const [isHintVisible, setIsHintVisible] = useState(false);
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
const handleOnChange = (e) => {
|
||||
onChange({ target: { name: e.target.name, value: e.target.value === "true" } });
|
||||
};
|
||||
|
||||
const toggleHint = () => setIsHintVisible((prevIsHintVisible) => !prevIsHintVisible);
|
||||
return (
|
||||
<Label
|
||||
text={
|
||||
<Header>
|
||||
{t("SSLVerification")}{" "}
|
||||
<StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
|
||||
</Header>
|
||||
}>
|
||||
<Hint isTooltip hidden={!isHintVisible} onClick={toggleHint}>
|
||||
{t("SSLHint")}
|
||||
</Hint>
|
||||
<RadioButtonGroup
|
||||
fontSize="13px"
|
||||
fontWeight="400"
|
||||
name="ssl"
|
||||
onClick={handleOnChange}
|
||||
options={[
|
||||
{
|
||||
label: t("EnableSSL"),
|
||||
value: "true",
|
||||
},
|
||||
{
|
||||
label: t("DisableSSL"),
|
||||
value: "false",
|
||||
},
|
||||
]}
|
||||
selected={value ? "true" : "false"}
|
||||
width="100%"
|
||||
orientation="vertical"
|
||||
spacing="8px"
|
||||
/>
|
||||
</Label>
|
||||
);
|
||||
};
|
@ -0,0 +1,165 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.react.svg?url";
|
||||
import Link from "@docspace/components/link";
|
||||
import Label from "@docspace/components/label";
|
||||
|
||||
import { Hint } from "../styled-components";
|
||||
|
||||
import PasswordInput from "@docspace/components/password-input";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const SecretKeyWrapper = styled.div`
|
||||
.link {
|
||||
display: inline-block;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.dotted {
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = styled.p`
|
||||
margin-top: 20px;
|
||||
|
||||
display: block;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
img {
|
||||
margin-left: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledInfoIcon = styled.img`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const SecretKeyInput = (props) => {
|
||||
const {
|
||||
isResetVisible,
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
passwordSettings,
|
||||
isPasswordValid,
|
||||
setIsPasswordValid,
|
||||
setIsResetVisible,
|
||||
webhooksGuideUrl,
|
||||
passwordInputKey,
|
||||
} = props;
|
||||
|
||||
const [isHintVisible, setIsHintVisible] = useState(false);
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
const secretKeyInputRef = useRef(null);
|
||||
|
||||
const toggleHint = () => setIsHintVisible((prevIsHintVisible) => !prevIsHintVisible);
|
||||
|
||||
const handleInputValidation = (isValid) => {
|
||||
setIsPasswordValid(isValid);
|
||||
};
|
||||
|
||||
const generatePassword = () => {
|
||||
secretKeyInputRef.current.onGeneratePassword();
|
||||
};
|
||||
|
||||
const handleHintDisapear = () => {
|
||||
toggleHint();
|
||||
};
|
||||
|
||||
const handleOnChange = (e) => {
|
||||
onChange({ target: { name, value: e.target.value } });
|
||||
};
|
||||
|
||||
const hideReset = () => {
|
||||
generatePassword();
|
||||
setIsResetVisible(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isResetVisible) {
|
||||
onChange({ target: { name, value: secretKeyInputRef.current.state.inputValue } });
|
||||
}
|
||||
}, [isResetVisible]);
|
||||
|
||||
return (
|
||||
<SecretKeyWrapper>
|
||||
<Label
|
||||
text={
|
||||
<Header>
|
||||
{t("SecretKey")} <StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
|
||||
</Header>
|
||||
}
|
||||
fontWeight={600}>
|
||||
<Hint isTooltip hidden={!isHintVisible} onClick={handleHintDisapear}>
|
||||
{t("SecretKeyHint")} <br />
|
||||
<Link
|
||||
type="page"
|
||||
isHovered
|
||||
fontWeight={600}
|
||||
href={webhooksGuideUrl}
|
||||
target="_blank"
|
||||
className="link"
|
||||
color="#333333">
|
||||
{t("ReadMore")}
|
||||
</Link>
|
||||
</Hint>
|
||||
{isResetVisible && (
|
||||
<Hint>
|
||||
{t("SecretKeyWarning")} <br />
|
||||
<Link
|
||||
type="action"
|
||||
fontWeight={600}
|
||||
isHovered={true}
|
||||
onClick={hideReset}
|
||||
className="link"
|
||||
color="#333333">
|
||||
{t("ResetKey")}
|
||||
</Link>
|
||||
</Hint>
|
||||
)}
|
||||
<div hidden={isResetVisible}>
|
||||
<PasswordInput
|
||||
onChange={handleOnChange}
|
||||
inputValue={value}
|
||||
inputName={name}
|
||||
placeholder={t("EnterSecretKey")}
|
||||
onValidateInput={handleInputValidation}
|
||||
ref={secretKeyInputRef}
|
||||
hasError={!isPasswordValid}
|
||||
isDisableTooltip={true}
|
||||
inputType="password"
|
||||
isFullWidth={true}
|
||||
passwordSettings={passwordSettings}
|
||||
key={passwordInputKey}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
fontWeight={600}
|
||||
isHovered={true}
|
||||
onClick={generatePassword}
|
||||
className="link dotted">
|
||||
{t("Generate")}
|
||||
</Link>
|
||||
</div>
|
||||
</Label>
|
||||
</SecretKeyWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ settingsStore, auth }) => {
|
||||
const { passwordSettings } = settingsStore;
|
||||
const { webhooksGuideUrl } = auth.settingsStore;
|
||||
|
||||
return {
|
||||
passwordSettings,
|
||||
webhooksGuideUrl,
|
||||
};
|
||||
})(observer(SecretKeyInput));
|
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import Badge from "@docspace/components/badge";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const StatusBadge = (props) => {
|
||||
const { status, theme } = props;
|
||||
|
||||
const badgeColorScheme =
|
||||
status >= 200 && status < 300
|
||||
? theme.isBase
|
||||
? {
|
||||
backgroundColor: "rgba(53, 173, 23, 0.1)",
|
||||
color: "#35AD17",
|
||||
}
|
||||
: {
|
||||
backgroundColor: "rgba(59, 164, 32, 0.1)",
|
||||
color: "#3BA420",
|
||||
}
|
||||
: theme.isBase
|
||||
? {
|
||||
backgroundColor: "rgba(242, 28, 14, 0.1)",
|
||||
color: "#F21C0E",
|
||||
}
|
||||
: {
|
||||
backgroundColor: "rgba(224, 100, 81, 0.1)",
|
||||
color: "#E06451",
|
||||
};
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
|
||||
if (status === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge
|
||||
backgroundColor={badgeColorScheme.backgroundColor}
|
||||
color={badgeColorScheme.color}
|
||||
label={status === 0 ? t("NotSent") : status.toString()}
|
||||
fontSize="9px"
|
||||
fontWeight={700}
|
||||
noHover
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const { settingsStore } = auth;
|
||||
|
||||
const { theme } = settingsStore;
|
||||
|
||||
return {
|
||||
theme,
|
||||
};
|
||||
})(observer(StatusBadge));
|
@ -0,0 +1,186 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
import { LabledInput } from "./LabledInput";
|
||||
import styled from "styled-components";
|
||||
import { Hint } from "../styled-components";
|
||||
import { SSLVerification } from "./SSLVerification";
|
||||
import SecretKeyInput from "./SecretKeyInput";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const StyledWebhookForm = styled.form`
|
||||
margin-top: 7px;
|
||||
|
||||
.margin-0 {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Footer = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
button:first-of-type {
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
function validateUrl(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const WebhookDialog = (props) => {
|
||||
const { visible, onClose, header, isSettingsModal, onSubmit, webhook } = props;
|
||||
|
||||
const [isResetVisible, setIsResetVisible] = useState(isSettingsModal);
|
||||
|
||||
const [isPasswordValid, setIsPasswordValid] = useState(false);
|
||||
const [isValid, setIsValid] = useState({
|
||||
name: true,
|
||||
uri: true,
|
||||
secretKey: true,
|
||||
});
|
||||
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const [webhookInfo, setWebhookInfo] = useState({
|
||||
id: webhook ? webhook.id : 0,
|
||||
name: webhook ? webhook.name : "",
|
||||
uri: webhook ? webhook.uri : "",
|
||||
secretKey: "",
|
||||
enabled: webhook ? webhook.enabled : true,
|
||||
ssl: webhook ? webhook.ssl : true,
|
||||
});
|
||||
|
||||
const [passwordInputKey, setPasswordInputKey] = useState(0);
|
||||
|
||||
const submitButtonRef = useRef(null);
|
||||
|
||||
const onModalClose = () => {
|
||||
onClose();
|
||||
isSettingsModal && setIsResetVisible(true);
|
||||
};
|
||||
|
||||
const onInputChange = (e) => {
|
||||
if (e.target.name) {
|
||||
!isValid[e.target.name] &&
|
||||
setIsValid((prevIsValid) => ({ ...prevIsValid, [e.target.name]: true }));
|
||||
setWebhookInfo((prevWebhookInfo) => ({
|
||||
...prevWebhookInfo,
|
||||
[e.target.name]: e.target.value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmitClick = () => {
|
||||
const isUrlValid = validateUrl(webhookInfo.uri);
|
||||
setIsValid(() => ({
|
||||
uri: isUrlValid,
|
||||
name: webhookInfo.name !== "",
|
||||
secretKey: isPasswordValid,
|
||||
}));
|
||||
|
||||
if (isUrlValid && (isPasswordValid || isResetVisible)) {
|
||||
submitButtonRef.current.click();
|
||||
}
|
||||
};
|
||||
|
||||
const onFormSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSubmit(webhookInfo);
|
||||
setWebhookInfo({
|
||||
id: webhook ? webhook.id : 0,
|
||||
name: "",
|
||||
uri: "",
|
||||
secretKey: "",
|
||||
enabled: true,
|
||||
});
|
||||
setPasswordInputKey((prevKey) => prevKey + 1);
|
||||
onModalClose();
|
||||
};
|
||||
|
||||
const cleanUpEvent = () => window.removeEventListener("keyup", onKeyPress);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
return cleanUpEvent;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setWebhookInfo({
|
||||
id: webhook ? webhook.id : 0,
|
||||
name: webhook ? webhook.name : "",
|
||||
uri: webhook ? webhook.uri : "",
|
||||
secretKey: "",
|
||||
enabled: webhook ? webhook.enabled : true,
|
||||
ssl: webhook ? webhook.ssl : true,
|
||||
});
|
||||
}, [webhook]);
|
||||
|
||||
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && onModalClose();
|
||||
|
||||
return (
|
||||
<ModalDialog withFooterBorder visible={visible} onClose={onModalClose} displayType="aside">
|
||||
<ModalDialog.Header>{header}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<StyledWebhookForm onSubmit={onFormSubmit}>
|
||||
{!isSettingsModal && <Hint>{t("WebhookCreationHint")}</Hint>}
|
||||
<LabledInput
|
||||
label={t("WebhookName")}
|
||||
placeholder={t("EnterWebhookName")}
|
||||
name="name"
|
||||
value={webhookInfo.name}
|
||||
onChange={onInputChange}
|
||||
hasError={!isValid.name}
|
||||
className={isSettingsModal ? "margin-0" : ""}
|
||||
required
|
||||
/>
|
||||
<LabledInput
|
||||
label={t("PayloadUrl")}
|
||||
placeholder={t("EnterUrl")}
|
||||
name="uri"
|
||||
value={webhookInfo.uri}
|
||||
onChange={onInputChange}
|
||||
hasError={!isValid.uri}
|
||||
required
|
||||
/>
|
||||
<SSLVerification value={webhookInfo.ssl} onChange={onInputChange} />
|
||||
<SecretKeyInput
|
||||
isResetVisible={isResetVisible}
|
||||
name="secretKey"
|
||||
value={webhookInfo.secretKey}
|
||||
onChange={onInputChange}
|
||||
isPasswordValid={isValid.secretKey}
|
||||
setIsPasswordValid={setIsPasswordValid}
|
||||
setIsResetVisible={setIsResetVisible}
|
||||
passwordInputKey={passwordInputKey}
|
||||
/>
|
||||
|
||||
<button type="submit" ref={submitButtonRef} hidden></button>
|
||||
</StyledWebhookForm>
|
||||
</ModalDialog.Body>
|
||||
|
||||
<ModalDialog.Footer>
|
||||
<Footer>
|
||||
<Button
|
||||
label={isSettingsModal ? t("Common:SaveButton") : t("Common:Create")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
onClick={handleSubmitClick}
|
||||
/>
|
||||
<Button label={t("Common:CancelButton")} size="normal" onClick={onModalClose} />
|
||||
</Footer>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default WebhookDialog;
|
@ -0,0 +1,64 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
import Link from "@docspace/components/link";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const InfoWrapper = styled.div`
|
||||
margin-bottom: 25px;
|
||||
`;
|
||||
|
||||
const InfoText = styled(Text)`
|
||||
max-width: 660px;
|
||||
white-space: break-spaces;
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 20px;
|
||||
|
||||
color: ${(props) => (props.theme.isBase ? "#657077" : "rgba(255, 255, 255, 0.6)")};
|
||||
&:hover {
|
||||
color: ${(props) => (props.theme.isBase ? "#657077" : "rgba(255, 255, 255, 0.6)")};
|
||||
}
|
||||
`;
|
||||
|
||||
InfoText.defaultProps = { theme: Base };
|
||||
|
||||
const StyledGuideLink = styled(Link)`
|
||||
color: ${(props) => (props.theme.isBase ? "#316DAA" : "#4781D1")};
|
||||
&:hover {
|
||||
color: ${(props) => (props.theme.isBase ? "#316DAA" : "#4781D1")};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledGuideLink.defaultProps = { theme: Base };
|
||||
|
||||
const WebhookInfo = (props) => {
|
||||
const { t } = useTranslation(["Webhooks"]);
|
||||
const { webhooksGuideUrl } = props;
|
||||
|
||||
return (
|
||||
<InfoWrapper>
|
||||
<InfoText as="p">{t("WebhooksInfo")}</InfoText>
|
||||
<StyledGuideLink
|
||||
fontWeight={600}
|
||||
isHovered
|
||||
type="page"
|
||||
href={webhooksGuideUrl}
|
||||
target="_blank">
|
||||
{t("WebhooksGuide")}
|
||||
</StyledGuideLink>
|
||||
</InfoWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const { webhooksGuideUrl } = auth.settingsStore;
|
||||
|
||||
return {
|
||||
webhooksGuideUrl,
|
||||
};
|
||||
})(observer(WebhookInfo));
|
@ -0,0 +1,108 @@
|
||||
import React, { useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import Row from "@docspace/components/row";
|
||||
|
||||
import { WebhookRowContent } from "./WebhookRowContent";
|
||||
|
||||
import SettingsIcon from "PUBLIC_DIR/images/catalog.settings.react.svg?url";
|
||||
import HistoryIcon from "PUBLIC_DIR/images/history.react.svg?url";
|
||||
import DeleteIcon from "PUBLIC_DIR/images/delete.react.svg?url";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const WebhookRow = (props) => {
|
||||
const {
|
||||
webhook,
|
||||
sectionWidth,
|
||||
toggleEnabled,
|
||||
openSettingsModal,
|
||||
openDeleteModal,
|
||||
setCurrentWebhook,
|
||||
} = props;
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const [isChecked, setIsChecked] = useState(webhook.enabled);
|
||||
|
||||
const handleToggleEnabled = () => {
|
||||
toggleEnabled(webhook);
|
||||
setIsChecked((prevIsChecked) => !prevIsChecked);
|
||||
};
|
||||
|
||||
const redirectToHistory = () => {
|
||||
navigate(window.location.pathname + `/${webhook.id}`);
|
||||
};
|
||||
const handleRowClick = (e) => {
|
||||
if (
|
||||
e.target.closest(".table-container_row-context-menu-wrapper") ||
|
||||
e.target.closest(".toggleButton") ||
|
||||
e.target.closest(".row_context-menu-wrapper") ||
|
||||
e.detail === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
redirectToHistory();
|
||||
};
|
||||
|
||||
const onSettingsOpen = () => {
|
||||
setCurrentWebhook(webhook);
|
||||
openSettingsModal();
|
||||
};
|
||||
const onDeleteOpen = () => {
|
||||
setCurrentWebhook(webhook);
|
||||
openDeleteModal();
|
||||
};
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
key: "Settings dropdownItem",
|
||||
label: t("Common:Settings"),
|
||||
icon: SettingsIcon,
|
||||
onClick: onSettingsOpen,
|
||||
},
|
||||
{
|
||||
key: "Webhook history dropdownItem",
|
||||
label: t("WebhookHistory"),
|
||||
icon: HistoryIcon,
|
||||
onClick: redirectToHistory,
|
||||
},
|
||||
{
|
||||
key: "Separator dropdownItem",
|
||||
isSeparator: true,
|
||||
},
|
||||
{
|
||||
key: "Delete webhook dropdownItem",
|
||||
label: t("DeleteWebhook"),
|
||||
icon: DeleteIcon,
|
||||
onClick: onDeleteOpen,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row
|
||||
sectionWidth={sectionWidth}
|
||||
key={webhook.id}
|
||||
data={webhook}
|
||||
contextOptions={contextOptions}
|
||||
onClick={handleRowClick}>
|
||||
<WebhookRowContent
|
||||
sectionWidth={sectionWidth}
|
||||
webhook={webhook}
|
||||
isChecked={isChecked}
|
||||
handleToggleEnabled={handleToggleEnabled}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { toggleEnabled, deleteWebhook, editWebhook, setCurrentWebhook } =
|
||||
webhooksStore;
|
||||
|
||||
return { toggleEnabled, deleteWebhook, editWebhook, setCurrentWebhook };
|
||||
})(observer(WebhookRow));
|
@ -0,0 +1,73 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import RowContent from "@docspace/components/row-content";
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
|
||||
import StatusBadge from "../../StatusBadge";
|
||||
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
|
||||
const StyledRowContent = styled(RowContent)`
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.rowMainContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mainIcons {
|
||||
min-width: 76px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ContentWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
`;
|
||||
|
||||
const ToggleButtonWrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-left: -52px;
|
||||
`;
|
||||
|
||||
const FlexWrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const WebhookRowContent = ({ sectionWidth, webhook, isChecked, handleToggleEnabled }) => {
|
||||
return (
|
||||
<StyledRowContent sectionWidth={sectionWidth}>
|
||||
<ContentWrapper>
|
||||
<FlexWrapper>
|
||||
<Text fontWeight={600} fontSize="14px" style={{ marginRight: "8px" }}>
|
||||
{webhook.name}
|
||||
</Text>
|
||||
<StatusBadge status={webhook.status} />
|
||||
</FlexWrapper>
|
||||
|
||||
{!isMobileOnly && (
|
||||
<Text fontWeight={600} fontSize="12px" color="#A3A9AE">
|
||||
{webhook.uri}
|
||||
</Text>
|
||||
)}
|
||||
</ContentWrapper>
|
||||
|
||||
<ToggleButtonWrapper>
|
||||
<ToggleButton
|
||||
className="toggle toggleButton"
|
||||
id="toggle id"
|
||||
isChecked={isChecked}
|
||||
onChange={handleToggleEnabled}
|
||||
/>
|
||||
</ToggleButtonWrapper>
|
||||
</StyledRowContent>
|
||||
);
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import RowContainer from "@docspace/components/row-container";
|
||||
|
||||
import WebhookRow from "./WebhookRow";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const WebhooksRowView = (props) => {
|
||||
const { webhooks, sectionWidth, viewAs, setViewAs, openSettingsModal, openDeleteModal } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (viewAs !== "table" && viewAs !== "row") return;
|
||||
|
||||
if (sectionWidth < 1025 || isMobile) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
{webhooks.map((webhook) => (
|
||||
<WebhookRow
|
||||
key={webhook.id}
|
||||
webhook={webhook}
|
||||
sectionWidth={sectionWidth}
|
||||
openSettingsModal={openSettingsModal}
|
||||
openDeleteModal={openDeleteModal}
|
||||
/>
|
||||
))}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore, setup }) => {
|
||||
const { webhooks } = webhooksStore;
|
||||
|
||||
const { viewAs, setViewAs } = setup;
|
||||
|
||||
return {
|
||||
webhooks,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
};
|
||||
})(observer(WebhooksRowView));
|
@ -0,0 +1,105 @@
|
||||
import React, { useState } from "react";
|
||||
import TableHeader from "@docspace/components/table-container/TableHeader";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const TABLE_VERSION = "5";
|
||||
const TABLE_COLUMNS = `webhooksConfigColumns_ver-${TABLE_VERSION}`;
|
||||
|
||||
const getColumns = (defaultColumns, userId) => {
|
||||
const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
const WebhookTableHeader = (props) => {
|
||||
const {
|
||||
userId,
|
||||
sectionWidth,
|
||||
tableRef,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
setHideColumns,
|
||||
} = props;
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
active: true,
|
||||
minWidth: 150,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "URL",
|
||||
title: t("URL"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "State",
|
||||
title: t("State"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const [columns, setColumns] = useState(getColumns(defaultColumns, userId));
|
||||
|
||||
function onColumnChange(key, e) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
setColumns((prevColumns) =>
|
||||
prevColumns.map((item, index) =>
|
||||
index === columnIndex ? { ...item, enable: !item.enable } : item,
|
||||
),
|
||||
);
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="48px"
|
||||
containerRef={tableRef}
|
||||
columns={columns}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
checkboxMargin="12px"
|
||||
showSettings={false}
|
||||
useReactWindow
|
||||
setHideColumns={setHideColumns}
|
||||
infoPanelVisible={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
return {
|
||||
userId: auth.userStore.user.id,
|
||||
};
|
||||
})(observer(WebhookTableHeader));
|
@ -0,0 +1,151 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import TableRow from "@docspace/components/table-container/TableRow";
|
||||
import TableCell from "@docspace/components/table-container/TableCell";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
import SettingsIcon from "PUBLIC_DIR/images/catalog.settings.react.svg?url";
|
||||
import HistoryIcon from "PUBLIC_DIR/images/history.react.svg?url";
|
||||
import DeleteIcon from "PUBLIC_DIR/images/delete.react.svg?url";
|
||||
import StatusBadge from "../../StatusBadge";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: contents;
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.table-container_cell {
|
||||
padding-right: 30px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mr-8 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.textOverflow {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const WebhooksTableRow = (props) => {
|
||||
const {
|
||||
webhook,
|
||||
toggleEnabled,
|
||||
openSettingsModal,
|
||||
openDeleteModal,
|
||||
setCurrentWebhook,
|
||||
hideColumns,
|
||||
} = props;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { t } = useTranslation(["Webhooks", "Common"]);
|
||||
|
||||
const [isChecked, setIsChecked] = useState(webhook.enabled);
|
||||
|
||||
const redirectToHistory = () => {
|
||||
navigate(window.location.pathname + `/${webhook.id}`);
|
||||
};
|
||||
|
||||
const handleRowClick = (e) => {
|
||||
if (
|
||||
e.target.closest(".checkbox") ||
|
||||
e.target.closest(".table-container_row-checkbox") ||
|
||||
e.target.closest(".type-combobox") ||
|
||||
e.target.closest(".table-container_row-context-menu-wrapper") ||
|
||||
e.target.closest(".toggleButton") ||
|
||||
e.detail === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
redirectToHistory();
|
||||
};
|
||||
const handleToggleEnabled = () => {
|
||||
toggleEnabled(webhook);
|
||||
setIsChecked((prevIsChecked) => !prevIsChecked);
|
||||
};
|
||||
|
||||
const onSettingsOpen = () => {
|
||||
setCurrentWebhook(webhook);
|
||||
openSettingsModal();
|
||||
};
|
||||
const onDeleteOpen = () => {
|
||||
setCurrentWebhook(webhook);
|
||||
openDeleteModal();
|
||||
};
|
||||
|
||||
const contextOptions = [
|
||||
{
|
||||
key: "Settings dropdownItem",
|
||||
label: t("Common:Settings"),
|
||||
icon: SettingsIcon,
|
||||
onClick: onSettingsOpen,
|
||||
},
|
||||
{
|
||||
key: "Webhook history dropdownItem",
|
||||
label: t("WebhookHistory"),
|
||||
icon: HistoryIcon,
|
||||
onClick: redirectToHistory,
|
||||
},
|
||||
{
|
||||
key: "Separator dropdownItem",
|
||||
isSeparator: true,
|
||||
},
|
||||
{
|
||||
key: "Delete webhook dropdownItem",
|
||||
label: t("DeleteWebhook"),
|
||||
icon: DeleteIcon,
|
||||
onClick: onDeleteOpen,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledWrapper onClick={handleRowClick}>
|
||||
<StyledTableRow contextOptions={contextOptions} hideColumns={hideColumns}>
|
||||
<TableCell>
|
||||
<Text as="span" fontWeight={600} className="mr-8 textOverflow">
|
||||
{webhook.name}{" "}
|
||||
</Text>
|
||||
<StatusBadge status={webhook.status} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="11px"
|
||||
color="#A3A9AE"
|
||||
fontWeight={600}
|
||||
className="textOverflow">
|
||||
{webhook.uri}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<ToggleButton
|
||||
className="toggle toggleButton"
|
||||
id="toggle id"
|
||||
isChecked={isChecked}
|
||||
onChange={handleToggleEnabled}
|
||||
/>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</StyledWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore }) => {
|
||||
const { toggleEnabled, setCurrentWebhook } = webhooksStore;
|
||||
|
||||
return {
|
||||
toggleEnabled,
|
||||
setCurrentWebhook,
|
||||
};
|
||||
})(observer(WebhooksTableRow));
|
@ -0,0 +1,115 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import styled from "styled-components";
|
||||
|
||||
import TableContainer from "@docspace/components/table-container/TableContainer";
|
||||
import TableBody from "@docspace/components/table-container/TableBody";
|
||||
|
||||
import WebhooksTableRow from "./WebhooksTableRow";
|
||||
import WebhookTableHeader from "./WebhookTableHeader";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const TableWrapper = styled(TableContainer)`
|
||||
margin-top: 16px;
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
margin-top: -1px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: ${(props) => (props.theme.isBase ? "#F8F9F9" : "#282828")};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
TableWrapper.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "5";
|
||||
const COLUMNS_SIZE = `webhooksConfigColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelWebhooksConfigColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const WebhooksTableView = (props) => {
|
||||
const {
|
||||
webhooks,
|
||||
loadWebhooks,
|
||||
sectionWidth,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
openSettingsModal,
|
||||
openDeleteModal,
|
||||
userId,
|
||||
} = props;
|
||||
|
||||
const tableRef = useRef(null);
|
||||
const [hideColumns, setHideColumns] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sectionWidth) return;
|
||||
if (sectionWidth < 1025 || isMobile) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
return (
|
||||
<TableWrapper forwardedRef={tableRef} useReactWindow>
|
||||
<WebhookTableHeader
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
setHideColumns={setHideColumns}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={webhooks.length}
|
||||
fetchMoreFiles={loadWebhooks}
|
||||
hasMoreFiles={false}
|
||||
itemCount={webhooks.length}>
|
||||
{webhooks.map((webhook, index) => (
|
||||
<WebhooksTableRow
|
||||
key={webhook.id}
|
||||
webhook={webhook}
|
||||
index={index}
|
||||
openSettingsModal={openSettingsModal}
|
||||
openDeleteModal={openDeleteModal}
|
||||
hideColumns={hideColumns}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</TableWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ webhooksStore, setup, auth }) => {
|
||||
const { webhooks, loadWebhooks } = webhooksStore;
|
||||
|
||||
const { viewAs, setViewAs } = setup;
|
||||
const { id: userId } = auth.userStore.user;
|
||||
|
||||
return {
|
||||
webhooks,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
loadWebhooks,
|
||||
userId,
|
||||
};
|
||||
})(observer(WebhooksTableView));
|
@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Consumer } from "@docspace/components/utils/context";
|
||||
|
||||
import WebhooksTableView from "./WebhooksTableView";
|
||||
import WebhooksRowView from "./WebhooksRowView";
|
||||
|
||||
const WebhooksTable = (props) => {
|
||||
const { viewAs, openSettingsModal, openDeleteModal } = props;
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<WebhooksTableView
|
||||
sectionWidth={context.sectionWidth}
|
||||
openSettingsModal={openSettingsModal}
|
||||
openDeleteModal={openDeleteModal}
|
||||
/>
|
||||
) : (
|
||||
<WebhooksRowView
|
||||
sectionWidth={context.sectionWidth}
|
||||
openSettingsModal={openSettingsModal}
|
||||
openDeleteModal={openDeleteModal}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
export default inject(({ setup }) => {
|
||||
const { viewAs } = setup;
|
||||
|
||||
return {
|
||||
viewAs,
|
||||
};
|
||||
})(observer(WebhooksTable));
|
@ -1,434 +1,102 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Box from "@docspace/components/box";
|
||||
import TextInput from "@docspace/components/text-input";
|
||||
import Textarea from "@docspace/components/textarea";
|
||||
import Label from "@docspace/components/label";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import Button from "@docspace/components/button";
|
||||
import ComboBox from "@docspace/components/combobox";
|
||||
import Heading from "@docspace/components/heading";
|
||||
import { tablet } from "@docspace/components/utils/device";
|
||||
import { objectToGetParams, loadScript } from "@docspace/common/utils";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import Submenu from "@docspace/components/submenu";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import BreakpointWarning from "SRC_DIR/components/BreakpointWarning";
|
||||
import { SortByFieldName } from "../../../../helpers/constants";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
import config from "PACKAGE_FILE";
|
||||
|
||||
const Controls = styled(Box)`
|
||||
width: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
}
|
||||
import JavascriptSDK from "./JavascriptSDK";
|
||||
import Webhooks from "./Webhooks";
|
||||
|
||||
.label {
|
||||
min-width: fit-content;
|
||||
import AppLoader from "@docspace/common/components/AppLoader";
|
||||
import SSOLoader from "./sub-components/ssoLoader";
|
||||
import { WebhookConfigsLoader } from "./Webhooks/sub-components/Loaders";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
|
||||
const StyledSubmenu = styled(Submenu)`
|
||||
.sticky {
|
||||
margin-top: ${() => (isMobile ? "0" : "4px")};
|
||||
z-index: 201;
|
||||
${() =>
|
||||
isMobileOnly &&
|
||||
css`
|
||||
top: 58px;
|
||||
`}
|
||||
}
|
||||
`;
|
||||
|
||||
const ControlsGroup = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`;
|
||||
const DeveloperToolsWrapper = (props) => {
|
||||
const { loadBaseInfo, developerToolsTab, setTab } = props;
|
||||
const [currentTab, setCurrentTab] = useState(developerToolsTab);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const Frame = styled(Box)`
|
||||
margin-top: 16px;
|
||||
const { t, ready } = useTranslation(["JavascriptSdk", "Webhooks"]);
|
||||
|
||||
> div {
|
||||
border: 1px dashed gray;
|
||||
border-radius: 3px;
|
||||
min-width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Buttons = styled(Box)`
|
||||
margin-top: 16px;
|
||||
button {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Container = styled(Box)`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
`;
|
||||
|
||||
const Preview = styled(Box)`
|
||||
width: 50%;
|
||||
flex-direction: row;
|
||||
|
||||
.frameStyle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const PortalIntegration = (props) => {
|
||||
const { t, setDocumentTitle } = props;
|
||||
|
||||
setDocumentTitle(t("JavascriptSdk"));
|
||||
|
||||
const scriptUrl = `${window.location.origin}/static/scripts/api.js`;
|
||||
|
||||
const dataSortBy = [
|
||||
const data = [
|
||||
{
|
||||
key: SortByFieldName.ModifiedDate,
|
||||
label: t("Common:LastModifiedDate"),
|
||||
default: true,
|
||||
id: "javascript-sdk",
|
||||
name: t("JavascriptSdk"),
|
||||
content: <JavascriptSDK />,
|
||||
},
|
||||
{
|
||||
id: "webhooks",
|
||||
name: t("Webhooks:Webhooks"),
|
||||
content: <Webhooks />,
|
||||
},
|
||||
{ key: SortByFieldName.Name, label: t("Common:Title") },
|
||||
{ key: SortByFieldName.Type, label: t("Common:Type") },
|
||||
{ key: SortByFieldName.Size, label: t("Common:Size") },
|
||||
{ key: SortByFieldName.CreationDate, label: t("Files:ByCreation") },
|
||||
{ key: SortByFieldName.Author, label: t("Files:ByAuthor") },
|
||||
];
|
||||
|
||||
const dataSortOrder = [
|
||||
{ key: "descending", label: t("Descending"), default: true },
|
||||
{ key: "ascending", label: t("Ascending") },
|
||||
];
|
||||
|
||||
const [config, setConfig] = useState({
|
||||
width: "100%",
|
||||
height: "400px",
|
||||
frameId: "ds-frame",
|
||||
showHeader: false,
|
||||
showTitle: true,
|
||||
showArticle: false,
|
||||
showFilter: false,
|
||||
});
|
||||
|
||||
const [sortBy, setSortBy] = useState(dataSortBy[0]);
|
||||
const [sortOrder, setSortOrder] = useState(dataSortOrder[0]);
|
||||
const [withSubfolders, setWithSubfolders] = useState(false);
|
||||
|
||||
const params = objectToGetParams(config);
|
||||
|
||||
const frameId = config.frameId || "ds-frame";
|
||||
|
||||
const destroyFrame = () => {
|
||||
DocSpace.destroyFrame();
|
||||
const load = async () => {
|
||||
await loadBaseInfo();
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const loadFrame = () => {
|
||||
const script = document.getElementById("integration");
|
||||
|
||||
if (script) {
|
||||
destroyFrame();
|
||||
script.remove();
|
||||
useEffect(() => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
if (currentTab !== -1) {
|
||||
setCurrentTab(currentTab);
|
||||
setTab(currentTab);
|
||||
}
|
||||
|
||||
const params = objectToGetParams(config);
|
||||
load();
|
||||
}, []);
|
||||
|
||||
loadScript(`${scriptUrl}${params}`, "integration");
|
||||
const onSelect = (e) => {
|
||||
navigate(
|
||||
combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
config.homepage,
|
||||
`/portal-settings/developer-tools/${e.id}`,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const onChangeWidth = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, width: e.target.value };
|
||||
});
|
||||
};
|
||||
if (!isLoading && !ready)
|
||||
return currentTab === 0 ? (
|
||||
<SSOLoader />
|
||||
) : currentTab === 1 ? (
|
||||
<WebhookConfigsLoader />
|
||||
) : (
|
||||
<AppLoader />
|
||||
);
|
||||
|
||||
const onChangeHeight = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, height: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeFolderId = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, folder: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeFrameId = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, frameId: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeWithSubfolders = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, withSubfolders: !withSubfolders };
|
||||
});
|
||||
|
||||
setWithSubfolders(!withSubfolders);
|
||||
};
|
||||
|
||||
const onChangeSortBy = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, sortby: item.key };
|
||||
});
|
||||
|
||||
setSortBy(item);
|
||||
};
|
||||
|
||||
const onChangeSortOrder = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, sortorder: item.key };
|
||||
});
|
||||
|
||||
setSortOrder(item);
|
||||
};
|
||||
|
||||
const onChangeFilterType = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, filterType: item.key };
|
||||
});
|
||||
|
||||
setFilterType(item);
|
||||
};
|
||||
|
||||
const onChangeDisplayType = (item) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, viewAs: item.key };
|
||||
});
|
||||
|
||||
setDisplayType(item);
|
||||
};
|
||||
|
||||
const onChangeShowHeader = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showHeader: !config.showHeader };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowTitle = () => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showTitle: !config.showTitle };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowArticle = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showArticle: !config.showArticle };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeShowFilter = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, showFilter: !config.showFilter };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeCount = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, count: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangePage = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, page: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeSearch = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, search: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeAuthor = (e) => {
|
||||
setConfig((config) => {
|
||||
return { ...config, authorType: e.target.value };
|
||||
});
|
||||
};
|
||||
|
||||
const codeBlock = `<div id="${frameId}">Fallback text</div>\n<script src="${scriptUrl}${params}"></script>`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<BreakpointWarning sectionName={t("JavascriptSdk")} />
|
||||
) : (
|
||||
<Container>
|
||||
<Controls>
|
||||
<Heading level={1} size="small">
|
||||
{t("WindowParameters")}
|
||||
</Heading>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FrameId")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeFrameId}
|
||||
placeholder={t("EnterId")}
|
||||
value={config.frameId}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Width")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeWidth}
|
||||
placeholder={t("EnterWidth")}
|
||||
value={config.width}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("EmbeddingPanel:Height")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeHeight}
|
||||
placeholder={t("EnterHeight")}
|
||||
value={config.height}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<Checkbox
|
||||
label={t("Header")}
|
||||
onChange={onChangeShowHeader}
|
||||
isChecked={config.showHeader}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Common:Title")}
|
||||
onChange={onChangeShowTitle}
|
||||
isChecked={config.showTitle}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Menu")}
|
||||
onChange={onChangeShowArticle}
|
||||
isChecked={config.showArticle}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Files:Filter")}
|
||||
onChange={onChangeShowFilter}
|
||||
isChecked={config.showFilter}
|
||||
/>
|
||||
<Heading level={1} size="small">
|
||||
{t("DataDisplay")}
|
||||
</Heading>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("FolderId")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeFolderId}
|
||||
placeholder={t("EnterId")}
|
||||
value={config.folder}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("ItemsCount")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeCount}
|
||||
placeholder={t("EnterCount")}
|
||||
value={config.count}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Page")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangePage}
|
||||
placeholder={t("EnterPage")}
|
||||
value={config.page}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("SearchTerm")} />
|
||||
<Box
|
||||
style={{ flexDirection: "row", display: "flex", gap: "16px" }}
|
||||
>
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeSearch}
|
||||
placeholder={t("Common:Search")}
|
||||
value={config.search}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Files:WithSubfolders")}
|
||||
onChange={onChangeWithSubfolders}
|
||||
isChecked={withSubfolders}
|
||||
/>
|
||||
</Box>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Files:ByAuthor")} />
|
||||
<TextInput
|
||||
scale={true}
|
||||
onChange={onChangeAuthor}
|
||||
placeholder={t("Common:EnterName")}
|
||||
value={config.authorType}
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("Common:SortBy")} />
|
||||
<ComboBox
|
||||
onSelect={onChangeSortBy}
|
||||
options={dataSortBy}
|
||||
scaled={true}
|
||||
selectedOption={sortBy}
|
||||
displaySelectedOption
|
||||
directionY="top"
|
||||
/>
|
||||
</ControlsGroup>
|
||||
<ControlsGroup>
|
||||
<Label className="label" text={t("SortOrder")} />
|
||||
<ComboBox
|
||||
onSelect={onChangeSortOrder}
|
||||
options={dataSortOrder}
|
||||
scaled={true}
|
||||
selectedOption={sortOrder}
|
||||
displaySelectedOption
|
||||
directionY="top"
|
||||
/>
|
||||
</ControlsGroup>
|
||||
</Controls>
|
||||
<Preview>
|
||||
<Frame>
|
||||
<Box id={frameId} className="frameStyle">
|
||||
{t("Common:Preview")}
|
||||
</Box>
|
||||
</Frame>
|
||||
|
||||
<Buttons>
|
||||
<Button
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Common:Preview")}
|
||||
onClick={loadFrame}
|
||||
/>
|
||||
<Button
|
||||
primary
|
||||
size="normal"
|
||||
label={t("Destroy")}
|
||||
onClick={destroyFrame}
|
||||
/>
|
||||
</Buttons>
|
||||
|
||||
<Heading level={1} size="xsmall">
|
||||
{t("CopyWindowCode")}
|
||||
</Heading>
|
||||
|
||||
<Textarea value={codeBlock} />
|
||||
</Preview>
|
||||
</Container>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return <StyledSubmenu data={data} startSelect={currentTab} onSelect={onSelect} />;
|
||||
};
|
||||
|
||||
export default inject(({ setup, auth }) => {
|
||||
const { settingsStore, setDocumentTitle } = auth;
|
||||
const { theme } = settingsStore;
|
||||
export default inject(({ setup, webhooksStore }) => {
|
||||
const { initSettings } = setup;
|
||||
const { developerToolsTab, setTab } = webhooksStore;
|
||||
|
||||
return {
|
||||
theme,
|
||||
setDocumentTitle,
|
||||
loadBaseInfo: async () => {
|
||||
await initSettings();
|
||||
},
|
||||
developerToolsTab,
|
||||
setTab,
|
||||
};
|
||||
})(
|
||||
withTranslation(["JavascriptSdk", "Files", "EmbeddingPanel", "Common"])(
|
||||
observer(PortalIntegration)
|
||||
)
|
||||
);
|
||||
})(observer(DeveloperToolsWrapper));
|
||||
|
@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import StyledSettingsSeparator from "SRC_DIR/pages/PortalSettings/StyledSettingsSeparator";
|
||||
|
||||
const StyledLoader = styled.div`
|
||||
.submenu {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
.item {
|
||||
width: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
max-width: 700px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-top: 24px;
|
||||
width: 238px;
|
||||
}
|
||||
`;
|
||||
|
||||
const SSOLoader = (props) => {
|
||||
const { isToggleSSO } = props;
|
||||
return (
|
||||
<StyledLoader>
|
||||
{!isToggleSSO && (
|
||||
<div className="submenu">
|
||||
<Loaders.Rectangle className="item" height="28px" />
|
||||
<Loaders.Rectangle className="item" height="28px" />
|
||||
</div>
|
||||
)}
|
||||
<Loaders.Rectangle className="description" height="60px" />
|
||||
<Loaders.Rectangle height="64px" />
|
||||
|
||||
<Loaders.Rectangle className="category" height="22px" />
|
||||
<StyledSettingsSeparator />
|
||||
<Loaders.Rectangle className="category" height="22px" />
|
||||
</StyledLoader>
|
||||
);
|
||||
};
|
||||
|
||||
export default SSOLoader;
|
@ -281,18 +281,26 @@ export const settingsTree = [
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "portal-settings_catalog-developer",
|
||||
id: "portal-settings_catalog-developer-tools",
|
||||
key: "5",
|
||||
icon: DeveloperReactSvgUrl,
|
||||
link: "developer",
|
||||
link: "developer-tools",
|
||||
tKey: "DeveloperTools",
|
||||
isHeader: true,
|
||||
children: [
|
||||
{
|
||||
id: "portal-settings_catalog-developer-tools",
|
||||
id: "portal-settings_catalog-javascript-sdk",
|
||||
key: "5-0",
|
||||
icon: "",
|
||||
link: "tools",
|
||||
link: "javascript-sdk",
|
||||
tKey: "DeveloperTools",
|
||||
isCategory: true,
|
||||
},
|
||||
{
|
||||
id: "portal-settings_catalog-webhooks",
|
||||
key: "5-1",
|
||||
icon: "",
|
||||
link: "webhooks",
|
||||
tKey: "DeveloperTools",
|
||||
isCategory: true,
|
||||
},
|
||||
|
@ -10,85 +10,65 @@ import Error404 from "SRC_DIR/pages/Errors/404";
|
||||
const PortalSettings = loadable(() => import("../pages/PortalSettings"));
|
||||
|
||||
const CustomizationSettings = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/common/index.js")
|
||||
import("../pages/PortalSettings/categories/common/index.js"),
|
||||
);
|
||||
const LanguageAndTimeZoneSettings = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/common/Customization/language-and-time-zone"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/common/Customization/language-and-time-zone"),
|
||||
);
|
||||
const WelcomePageSettings = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/common/Customization/welcome-page-settings"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/common/Customization/welcome-page-settings"),
|
||||
);
|
||||
const DNSSettings = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/common/Customization/dns-settings")
|
||||
import("../pages/PortalSettings/categories/common/Customization/dns-settings"),
|
||||
);
|
||||
const PortalRenaming = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/common/Customization/portal-renaming"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/common/Customization/portal-renaming"),
|
||||
);
|
||||
const WhiteLabel = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/common/Branding/whitelabel")
|
||||
import("../pages/PortalSettings/categories/common/Branding/whitelabel"),
|
||||
);
|
||||
const SecuritySettings = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/security/index.js")
|
||||
import("../pages/PortalSettings/categories/security/index.js"),
|
||||
);
|
||||
const TfaPage = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/security/access-portal/tfa")
|
||||
import("../pages/PortalSettings/categories/security/access-portal/tfa"),
|
||||
);
|
||||
const PasswordStrengthPage = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/security/access-portal/passwordStrength"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/security/access-portal/passwordStrength"),
|
||||
);
|
||||
const TrustedMailPage = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/security/access-portal/trustedMail"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/security/access-portal/trustedMail"),
|
||||
);
|
||||
const IpSecurityPage = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/security/access-portal/ipSecurity")
|
||||
import("../pages/PortalSettings/categories/security/access-portal/ipSecurity"),
|
||||
);
|
||||
const AdminMessagePage = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/security/access-portal/adminMessage"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/security/access-portal/adminMessage"),
|
||||
);
|
||||
const SessionLifetimePage = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/security/access-portal/sessionLifetime"
|
||||
)
|
||||
);
|
||||
const Integration = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/integration")
|
||||
);
|
||||
const Payments = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/payments")
|
||||
import("../pages/PortalSettings/categories/security/access-portal/sessionLifetime"),
|
||||
);
|
||||
const Integration = loadable(() => import("../pages/PortalSettings/categories/integration"));
|
||||
const Payments = loadable(() => import("../pages/PortalSettings/categories/payments"));
|
||||
const ThirdParty = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/integration/ThirdPartyServicesSettings"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/integration/ThirdPartyServicesSettings"),
|
||||
);
|
||||
const SingleSignOn = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/integration/SingleSignOn")
|
||||
import("../pages/PortalSettings/categories/integration/SingleSignOn"),
|
||||
);
|
||||
const DeveloperTools = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/developer-tools/index.js")
|
||||
import("../pages/PortalSettings/categories/developer-tools/index.js"),
|
||||
);
|
||||
const Backup = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/data-management/index")
|
||||
const WebhookHistory = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookHistory"),
|
||||
);
|
||||
const DeleteDataPage = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/delete-data")
|
||||
const WebhookDetails = loadable(() =>
|
||||
import("../pages/PortalSettings/categories/developer-tools/Webhooks/WebhookEventDetails"),
|
||||
);
|
||||
const Backup = loadable(() => import("../pages/PortalSettings/categories/data-management/index"));
|
||||
const DeleteDataPage = loadable(() => import("../pages/PortalSettings/categories/delete-data"));
|
||||
const RestoreBackup = loadable(() =>
|
||||
import(
|
||||
"../pages/PortalSettings/categories/data-management/backup/restore-backup/index"
|
||||
)
|
||||
import("../pages/PortalSettings/categories/data-management/backup/restore-backup/index"),
|
||||
);
|
||||
|
||||
const PortalSettingsRoutes = {
|
||||
@ -199,13 +179,25 @@ const PortalSettingsRoutes = {
|
||||
element: <Payments />,
|
||||
},
|
||||
{
|
||||
path: "developer",
|
||||
element: <Navigate to="developer/tools" />,
|
||||
path: "developer-tools",
|
||||
element: <Navigate to="javascript-sdk" />,
|
||||
},
|
||||
{
|
||||
path: "developer/tools",
|
||||
path: "developer-tools/javascript-sdk",
|
||||
element: <DeveloperTools />,
|
||||
},
|
||||
{
|
||||
path: "developer-tools/webhooks",
|
||||
element: <DeveloperTools />,
|
||||
},
|
||||
{
|
||||
path: "developer-tools/webhooks/:id",
|
||||
element: <WebhookHistory />,
|
||||
},
|
||||
{
|
||||
path: "developer-tools/webhooks/:id/:eventId",
|
||||
element: <WebhookDetails />,
|
||||
},
|
||||
{
|
||||
path: "backup",
|
||||
element: <Navigate to="backup/data-backup" />,
|
||||
|
223
packages/client/src/store/WebhooksStore.js
Normal file
223
packages/client/src/store/WebhooksStore.js
Normal file
@ -0,0 +1,223 @@
|
||||
import {
|
||||
createWebhook,
|
||||
getAllWebhooks,
|
||||
getWebhooksJournal,
|
||||
removeWebhook,
|
||||
retryWebhook,
|
||||
retryWebhooks,
|
||||
toggleEnabledWebhook,
|
||||
updateWebhook,
|
||||
} from "@docspace/common/api/settings";
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
|
||||
class WebhooksStore {
|
||||
webhooks = [];
|
||||
checkedEventIds = [];
|
||||
historyFilters = null;
|
||||
historyItems = [];
|
||||
startIndex = 0;
|
||||
totalItems = 0;
|
||||
developerToolsTab = 0;
|
||||
currentWebhook = {};
|
||||
eventDetails = {};
|
||||
FETCH_COUNT = 100;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setCurrentWebhook = (webhook) => {
|
||||
this.currentWebhook = webhook;
|
||||
};
|
||||
|
||||
setTab = (tabIndex) => {
|
||||
this.developerToolsTab = tabIndex;
|
||||
};
|
||||
|
||||
loadWebhooks = async () => {
|
||||
try {
|
||||
const webhooksData = await getAllWebhooks();
|
||||
runInAction(() => {
|
||||
this.webhooks = webhooksData.map((data) => ({
|
||||
id: data.configs.id,
|
||||
name: data.configs.name,
|
||||
uri: data.configs.uri,
|
||||
secretKey: data.configs.secretKey,
|
||||
enabled: data.configs.enabled,
|
||||
ssl: data.configs.ssl,
|
||||
status: data.status,
|
||||
}));
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
addWebhook = async (webhook) => {
|
||||
const webhookData = await createWebhook(
|
||||
webhook.name,
|
||||
webhook.uri,
|
||||
webhook.secretKey,
|
||||
webhook.ssl,
|
||||
);
|
||||
|
||||
this.webhooks = [
|
||||
...this.webhooks,
|
||||
{
|
||||
id: webhookData.id,
|
||||
uri: webhookData.uri,
|
||||
name: webhookData.name,
|
||||
enabled: webhookData.enabled,
|
||||
secretKey: webhookData.secretKey,
|
||||
ssl: webhookData.ssl,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
isWebhookExist = (desiredWebhook) => {
|
||||
return this.webhooks.some((webhook) => webhook.id === desiredWebhook.id);
|
||||
};
|
||||
|
||||
toggleEnabled = async (desiredWebhook) => {
|
||||
await toggleEnabledWebhook(desiredWebhook);
|
||||
const index = this.webhooks.findIndex((webhook) => webhook.id === desiredWebhook.id);
|
||||
this.webhooks[index].enabled = !this.webhooks[index].enabled;
|
||||
};
|
||||
|
||||
deleteWebhook = async (webhook) => {
|
||||
await removeWebhook(webhook.id);
|
||||
this.webhooks = this.webhooks.filter((currentWebhook) => currentWebhook.id !== webhook.id);
|
||||
};
|
||||
|
||||
editWebhook = async (prevWebhook, webhookInfo) => {
|
||||
await updateWebhook(
|
||||
prevWebhook.id,
|
||||
webhookInfo.name,
|
||||
webhookInfo.uri,
|
||||
webhookInfo.secretKey || prevWebhook.secretKey,
|
||||
webhookInfo.ssl,
|
||||
);
|
||||
this.webhooks = this.webhooks.map((webhook) =>
|
||||
webhook.id === prevWebhook.id ? { ...prevWebhook, ...webhookInfo } : webhook,
|
||||
);
|
||||
};
|
||||
|
||||
retryWebhookEvent = async (id) => {
|
||||
return await retryWebhook(id);
|
||||
};
|
||||
|
||||
retryWebhookEvents = async (ids) => {
|
||||
return await retryWebhooks(ids);
|
||||
};
|
||||
|
||||
fetchHistoryItems = async (params) => {
|
||||
this.totalItems = 0;
|
||||
this.startIndex = 0;
|
||||
const count = params.count ? params.count : this.FETCH_COUNT;
|
||||
const historyData = await getWebhooksJournal({
|
||||
...params,
|
||||
startIndex: this.startIndex,
|
||||
count: count,
|
||||
});
|
||||
runInAction(() => {
|
||||
this.startIndex = count;
|
||||
this.historyItems = historyData.items;
|
||||
this.totalItems = historyData.total;
|
||||
});
|
||||
};
|
||||
fetchMoreItems = async (params) => {
|
||||
const count = params.count ? params.count : this.FETCH_COUNT;
|
||||
const historyData = await getWebhooksJournal({
|
||||
...params,
|
||||
startIndex: this.startIndex,
|
||||
count: count,
|
||||
});
|
||||
runInAction(() => {
|
||||
this.startIndex = this.startIndex + count;
|
||||
this.historyItems = [...this.historyItems, ...historyData.items];
|
||||
});
|
||||
};
|
||||
fetchEventData = async (eventId) => {
|
||||
const data = await getWebhooksJournal({ eventId });
|
||||
this.eventDetails = data.items[0];
|
||||
};
|
||||
get hasMoreItems() {
|
||||
return this.totalItems > this.startIndex;
|
||||
}
|
||||
|
||||
get isWebhooksEmpty() {
|
||||
return this.webhooks.length === 0;
|
||||
}
|
||||
|
||||
setHistoryFilters = (filters) => {
|
||||
this.historyFilters = filters;
|
||||
};
|
||||
clearHistoryFilters = () => {
|
||||
this.historyFilters = null;
|
||||
};
|
||||
clearDate = () => {
|
||||
this.historyFilters = { ...this.historyFilters, deliveryDate: null };
|
||||
};
|
||||
unselectStatus = (statusCode) => {
|
||||
this.historyFilters = {
|
||||
...this.historyFilters,
|
||||
status: this.historyFilters.status.filter((item) => item !== statusCode),
|
||||
};
|
||||
};
|
||||
|
||||
formatFilters = (filters) => {
|
||||
const params = {};
|
||||
if (filters.deliveryDate !== null) {
|
||||
params.deliveryFrom =
|
||||
filters.deliveryDate.format("YYYY-MM-DD") + "T" + filters.deliveryFrom.format("HH:mm:ss");
|
||||
|
||||
params.deliveryTo =
|
||||
filters.deliveryDate.format("YYYY-MM-DD") + "T" + filters.deliveryTo.format("HH:mm:ss");
|
||||
}
|
||||
|
||||
const statusEnum = {
|
||||
"Not sent": 1,
|
||||
"2XX": 2,
|
||||
"3XX": 4,
|
||||
"4XX": 8,
|
||||
"5XX": 16,
|
||||
};
|
||||
|
||||
if (filters.status.length > 0) {
|
||||
const statusFlag = filters.status.reduce(
|
||||
(sum, currentValue) => sum + statusEnum[currentValue],
|
||||
0,
|
||||
);
|
||||
params.groupStatus = statusFlag;
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
toggleEventId = (id) => {
|
||||
this.checkedEventIds = this.checkedEventIds.includes(id)
|
||||
? this.checkedEventIds.filter((checkedId) => checkedId !== id)
|
||||
: [...this.checkedEventIds, id];
|
||||
};
|
||||
isIdChecked = (id) => {
|
||||
return this.checkedEventIds.includes(id);
|
||||
};
|
||||
checkAllIds = () => {
|
||||
this.checkedEventIds = this.historyItems.map((event) => event.id);
|
||||
};
|
||||
emptyCheckedIds = () => {
|
||||
this.checkedEventIds = [];
|
||||
};
|
||||
get areAllIdsChecked() {
|
||||
return this.checkedEventIds.length === this.historyItems.length;
|
||||
}
|
||||
get isIndeterminate() {
|
||||
return this.checkedEventIds.length > 0 && !this.areAllIdsChecked;
|
||||
}
|
||||
|
||||
get isGroupMenuVisible() {
|
||||
return this.checkedEventIds.length !== 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default WebhooksStore;
|
@ -35,6 +35,7 @@ import AccessRightsStore from "./AccessRightsStore";
|
||||
import TableStore from "./TableStore";
|
||||
import CreateEditRoomStore from "./CreateEditRoomStore";
|
||||
|
||||
import WebhooksStore from "./WebhooksStore";
|
||||
import ClientLoadingStore from "./ClientLoadingStore";
|
||||
|
||||
const oformsStore = new OformsStore(authStore);
|
||||
@ -66,13 +67,10 @@ const filesStore = new FilesStore(
|
||||
settingsStore,
|
||||
thirdPartyStore,
|
||||
accessRightsStore,
|
||||
clientLoadingStore
|
||||
clientLoadingStore,
|
||||
);
|
||||
|
||||
const mediaViewerDataStore = new MediaViewerDataStore(
|
||||
filesStore,
|
||||
settingsStore
|
||||
);
|
||||
const mediaViewerDataStore = new MediaViewerDataStore(filesStore, settingsStore);
|
||||
const secondaryProgressDataStore = new SecondaryProgressDataStore();
|
||||
const primaryProgressDataStore = new PrimaryProgressDataStore();
|
||||
const versionHistoryStore = new VersionHistoryStore(filesStore);
|
||||
@ -82,15 +80,10 @@ const dialogsStore = new DialogsStore(
|
||||
treeFoldersStore,
|
||||
filesStore,
|
||||
selectedFolderStore,
|
||||
versionHistoryStore
|
||||
versionHistoryStore,
|
||||
);
|
||||
|
||||
const peopleStore = new PeopleStore(
|
||||
authStore,
|
||||
setupStore,
|
||||
accessRightsStore,
|
||||
dialogsStore
|
||||
);
|
||||
const peopleStore = new PeopleStore(authStore, setupStore, accessRightsStore, dialogsStore);
|
||||
|
||||
const uploadDataStore = new UploadDataStore(
|
||||
authStore,
|
||||
@ -100,7 +93,7 @@ const uploadDataStore = new UploadDataStore(
|
||||
secondaryProgressDataStore,
|
||||
primaryProgressDataStore,
|
||||
dialogsStore,
|
||||
settingsStore
|
||||
settingsStore,
|
||||
);
|
||||
|
||||
const filesActionsStore = new FilesActionsStore(
|
||||
@ -113,7 +106,7 @@ const filesActionsStore = new FilesActionsStore(
|
||||
dialogsStore,
|
||||
mediaViewerDataStore,
|
||||
accessRightsStore,
|
||||
clientLoadingStore
|
||||
clientLoadingStore,
|
||||
);
|
||||
|
||||
const contextOptionsStore = new ContextOptionsStore(
|
||||
@ -126,7 +119,7 @@ const contextOptionsStore = new ContextOptionsStore(
|
||||
uploadDataStore,
|
||||
versionHistoryStore,
|
||||
settingsStore,
|
||||
selectedFolderStore
|
||||
selectedFolderStore,
|
||||
);
|
||||
|
||||
const hotkeyStore = new HotkeyStore(
|
||||
@ -135,7 +128,7 @@ const hotkeyStore = new HotkeyStore(
|
||||
settingsStore,
|
||||
filesActionsStore,
|
||||
treeFoldersStore,
|
||||
uploadDataStore
|
||||
uploadDataStore,
|
||||
);
|
||||
|
||||
const profileActionsStore = new ProfileActionsStore(
|
||||
@ -143,7 +136,7 @@ const profileActionsStore = new ProfileActionsStore(
|
||||
filesStore,
|
||||
peopleStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore
|
||||
selectedFolderStore,
|
||||
);
|
||||
|
||||
const tableStore = new TableStore(authStore, treeFoldersStore);
|
||||
@ -164,9 +157,11 @@ const createEditRoomStore = new CreateEditRoomStore(
|
||||
authStore.settingsStore,
|
||||
authStore.infoPanelStore,
|
||||
authStore.currentQuotaStore,
|
||||
clientLoadingStore
|
||||
clientLoadingStore,
|
||||
);
|
||||
|
||||
const webhooksStore = new WebhooksStore();
|
||||
|
||||
const store = {
|
||||
auth: authStore,
|
||||
payments: paymentStore,
|
||||
@ -202,6 +197,8 @@ const store = {
|
||||
|
||||
accessRightsStore,
|
||||
createEditRoomStore,
|
||||
|
||||
webhooksStore,
|
||||
clientLoadingStore,
|
||||
};
|
||||
|
||||
|
@ -26,12 +26,7 @@ export function getPortalPasswordSettings(confirmKey = null) {
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function setPortalPasswordSettings(
|
||||
minLength,
|
||||
upperCase,
|
||||
digits,
|
||||
specSymbols
|
||||
) {
|
||||
export function setPortalPasswordSettings(minLength, upperCase, digits, specSymbols) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/security/password",
|
||||
@ -225,13 +220,7 @@ export function restoreWhiteLabelSettings(isDefault) {
|
||||
});
|
||||
}
|
||||
|
||||
export function setCompanyInfoSettings(
|
||||
address,
|
||||
companyName,
|
||||
email,
|
||||
phone,
|
||||
site
|
||||
) {
|
||||
export function setCompanyInfoSettings(address, companyName, email, phone, site) {
|
||||
const data = {
|
||||
settings: { address, companyName, email, phone, site },
|
||||
};
|
||||
@ -267,7 +256,7 @@ export function getCustomSchemaList() {
|
||||
export function setAdditionalResources(
|
||||
feedbackAndSupportEnabled,
|
||||
videoGuidesEnabled,
|
||||
helpCenterEnabled
|
||||
helpCenterEnabled,
|
||||
) {
|
||||
const data = {
|
||||
settings: {
|
||||
@ -314,7 +303,7 @@ export function setCustomSchema(
|
||||
regDateCaption,
|
||||
groupHeadCaption,
|
||||
guestCaption,
|
||||
guestsCaption
|
||||
guestsCaption,
|
||||
) {
|
||||
const data = {
|
||||
userCaption,
|
||||
@ -393,14 +382,7 @@ export function getMachineName(confirmKey = null) {
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function setPortalOwner(
|
||||
email,
|
||||
hash,
|
||||
lng,
|
||||
timeZone,
|
||||
confirmKey = null,
|
||||
analytics
|
||||
) {
|
||||
export function setPortalOwner(email, hash, lng, timeZone, confirmKey = null, analytics) {
|
||||
const options = {
|
||||
method: "put",
|
||||
url: "/settings/wizard/complete",
|
||||
@ -731,6 +713,85 @@ export function removeActiveSession(eventId) {
|
||||
});
|
||||
}
|
||||
|
||||
export function createWebhook(name, uri, secretKey, ssl) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: `/settings/webhook`,
|
||||
data: { name, uri, secretKey, ssl },
|
||||
});
|
||||
}
|
||||
|
||||
export function getAllWebhooks() {
|
||||
return request({
|
||||
method: "get",
|
||||
url: `/settings/webhook`,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateWebhook(id, name, uri, secretKey, ssl) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/settings/webhook`,
|
||||
data: { id, name, uri, secretKey, ssl },
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleEnabledWebhook(webhook) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/settings/webhook`,
|
||||
data: {
|
||||
id: webhook.id,
|
||||
name: webhook.name,
|
||||
uri: webhook.uri,
|
||||
secretKey: webhook.secretKey,
|
||||
enabled: !webhook.enabled,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function removeWebhook(id) {
|
||||
return request({
|
||||
method: "delete",
|
||||
url: `/settings/webhook/${id}`,
|
||||
});
|
||||
}
|
||||
|
||||
export function getWebhooksJournal(props) {
|
||||
const { configId, eventId, count, startIndex, deliveryFrom, deliveryTo, groupStatus } = props;
|
||||
|
||||
const params = {};
|
||||
|
||||
configId && (params.configId = configId);
|
||||
eventId && (params.eventId = eventId);
|
||||
count && (params.count = count);
|
||||
startIndex && (params.startIndex = startIndex);
|
||||
deliveryFrom && (params.deliveryFrom = deliveryFrom);
|
||||
deliveryTo && (params.deliveryTo = deliveryTo);
|
||||
groupStatus && (params.groupStatus = groupStatus);
|
||||
|
||||
return request({
|
||||
method: "get",
|
||||
url: "/settings/webhooks/log?",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function retryWebhook(webhookId) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/settings/webhook/${webhookId}/retry`,
|
||||
});
|
||||
}
|
||||
|
||||
export function retryWebhooks(webhooksIds) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/settings/webhook/retry`,
|
||||
data: { Ids: webhooksIds },
|
||||
});
|
||||
}
|
||||
|
||||
export function muteRoomNotification(id, isMute) {
|
||||
const options = {
|
||||
method: "post",
|
||||
|
@ -94,6 +94,7 @@ const StyledArticle = styled.article`
|
||||
|
||||
@media ${mobile} {
|
||||
height: 100% !important;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
@media ${hugeMobile} {
|
||||
|
@ -5,6 +5,7 @@ import Loaders from "@docspace/common/components/Loaders";
|
||||
import { isTablet as isTabletUtils } from "@docspace/components/utils/device";
|
||||
import { Link } from "react-router-dom";
|
||||
import { isTablet, isMobileOnly } from "react-device-detect";
|
||||
import { isMobile } from "@docspace/components/utils/device";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import {
|
||||
StyledArticleHeader,
|
||||
@ -25,9 +26,10 @@ const ArticleHeader = ({
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isTabletView = (isTabletUtils() || isTablet) && !isMobileOnly;
|
||||
const isTabletView =
|
||||
(isTabletUtils() || isTablet) && !isMobileOnly && !isMobile();
|
||||
|
||||
const onLogoClick = () => {
|
||||
|
||||
onLogoClickAction && onLogoClickAction();
|
||||
navigate("/");
|
||||
};
|
||||
@ -39,7 +41,7 @@ const ArticleHeader = ({
|
||||
? getLogoFromPath(whiteLabelLogoUrls[0].path.dark)
|
||||
: getLogoFromPath(whiteLabelLogoUrls[0].path.light);
|
||||
|
||||
if (isMobileOnly) return <></>;
|
||||
if (isMobileOnly || isMobile()) return <></>;
|
||||
return (
|
||||
<StyledArticleHeader showText={showText} {...rest}>
|
||||
{isTabletView && isBurgerLoading ? (
|
||||
|
@ -5,7 +5,10 @@ import Avatar from "@docspace/components/avatar";
|
||||
import Text from "@docspace/components/text";
|
||||
import ContextMenuButton from "@docspace/components/context-menu-button";
|
||||
import ContextMenu from "@docspace/components/context-menu";
|
||||
import { isTablet as isTabletUtils } from "@docspace/components/utils/device";
|
||||
import {
|
||||
isTablet as isTabletUtils,
|
||||
isMobile as isMobileUtils,
|
||||
} from "@docspace/components/utils/device";
|
||||
import { isTablet, isMobileOnly } from "react-device-detect";
|
||||
import {
|
||||
StyledArticleProfile,
|
||||
@ -22,7 +25,8 @@ const ArticleProfile = (props) => {
|
||||
const ref = useRef(null);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const isTabletView = (isTabletUtils() || isTablet) && !isMobileOnly;
|
||||
const isTabletView =
|
||||
(isTabletUtils() || isTablet) && !isMobileOnly && !isMobileUtils();
|
||||
const avatarSize = isTabletView ? "min" : "base";
|
||||
const userRole = getUserRole(user);
|
||||
|
||||
@ -48,6 +52,8 @@ const ArticleProfile = (props) => {
|
||||
|
||||
const userAvatar = user.hasAvatar ? user.avatar : DefaultUserPhotoPngUrl;
|
||||
|
||||
if (!isMobileOnly && isMobileUtils()) return <></>;
|
||||
|
||||
return (
|
||||
<StyledProfileWrapper showText={showText}>
|
||||
<StyledArticleProfile showText={showText} tablet={isTabletView}>
|
||||
|
@ -2,12 +2,7 @@ import { makeAutoObservable } from "mobx";
|
||||
import api from "../api";
|
||||
import { combineUrl, setCookie, getCookie } from "../utils";
|
||||
import FirebaseHelper from "../utils/firebase";
|
||||
import {
|
||||
ThemeKeys,
|
||||
COOKIE_EXPIRATION_YEAR,
|
||||
LANGUAGE,
|
||||
TenantStatus,
|
||||
} from "../constants";
|
||||
import { ThemeKeys, COOKIE_EXPIRATION_YEAR, LANGUAGE, TenantStatus } from "../constants";
|
||||
import { version } from "../package.json";
|
||||
import SocketIOHelper from "../utils/socket";
|
||||
import { Dark, Base } from "@docspace/components/themes";
|
||||
@ -37,8 +32,7 @@ class SettingsStore {
|
||||
? window.RendererProcessVariable?.theme?.type === "dark"
|
||||
? Dark
|
||||
: Base
|
||||
: window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
: window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? Dark
|
||||
: Base;
|
||||
trustedDomains = [];
|
||||
@ -283,6 +277,10 @@ class SettingsStore {
|
||||
return `${this.helpLink}/administration/docspace-settings.aspx#AutoBackup`;
|
||||
}
|
||||
|
||||
get webhooksGuideUrl() {
|
||||
return `${this.helpLink}/administration/docspace-webhooks.aspx`;
|
||||
}
|
||||
|
||||
get wizardCompleted() {
|
||||
return this.isLoaded && !this.wizardToken;
|
||||
}
|
||||
@ -323,10 +321,7 @@ class SettingsStore {
|
||||
else newSettings = await api.settings.getSettings(true);
|
||||
|
||||
if (window["AscDesktopEditor"] !== undefined || this.personal) {
|
||||
const dp = combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
"/products/files/"
|
||||
);
|
||||
const dp = combineUrl(window.DocSpaceConfig?.proxy?.url, "/products/files/");
|
||||
this.setDefaultPage(dp);
|
||||
}
|
||||
|
||||
@ -336,7 +331,7 @@ class SettingsStore {
|
||||
key,
|
||||
key === "defaultPage"
|
||||
? combineUrl(window.DocSpaceConfig?.proxy?.url, newSettings[key])
|
||||
: newSettings[key]
|
||||
: newSettings[key],
|
||||
);
|
||||
if (key === "culture") {
|
||||
if (newSettings.wizardToken) return;
|
||||
@ -396,7 +391,7 @@ class SettingsStore {
|
||||
this.getPortalSettings(),
|
||||
this.getAppearanceTheme(),
|
||||
this.getWhiteLabelLogoUrls(),
|
||||
this.getBuildVersionInfo()
|
||||
this.getBuildVersionInfo(),
|
||||
);
|
||||
|
||||
await Promise.all(requests);
|
||||
@ -432,12 +427,12 @@ class SettingsStore {
|
||||
setAdditionalResources = async (
|
||||
feedbackAndSupportEnabled,
|
||||
videoGuidesEnabled,
|
||||
helpCenterEnabled
|
||||
helpCenterEnabled,
|
||||
) => {
|
||||
return await api.settings.setAdditionalResources(
|
||||
feedbackAndSupportEnabled,
|
||||
videoGuidesEnabled,
|
||||
helpCenterEnabled
|
||||
helpCenterEnabled,
|
||||
);
|
||||
};
|
||||
|
||||
@ -484,13 +479,7 @@ class SettingsStore {
|
||||
};
|
||||
|
||||
setCompanyInfoSettings = async (address, companyName, email, phone, site) => {
|
||||
return api.settings.setCompanyInfoSettings(
|
||||
address,
|
||||
companyName,
|
||||
email,
|
||||
phone,
|
||||
site
|
||||
);
|
||||
return api.settings.setCompanyInfoSettings(address, companyName, email, phone, site);
|
||||
};
|
||||
|
||||
setLogoUrl = (url) => {
|
||||
@ -549,15 +538,11 @@ class SettingsStore {
|
||||
};
|
||||
|
||||
getLoginLink = (token, code) => {
|
||||
return combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
`/login.ashx?p=${token}&code=${code}`
|
||||
);
|
||||
return combineUrl(window.DocSpaceConfig?.proxy?.url, `/login.ashx?p=${token}&code=${code}`);
|
||||
};
|
||||
|
||||
setModuleInfo = (homepage, productId) => {
|
||||
if (this.homepage === homepage || this.currentProductId === productId)
|
||||
return;
|
||||
if (this.homepage === homepage || this.currentProductId === productId) return;
|
||||
|
||||
console.log(`setModuleInfo('${homepage}', '${productId}')`);
|
||||
|
||||
@ -603,17 +588,12 @@ class SettingsStore {
|
||||
this.setPasswordSettings(settings);
|
||||
};
|
||||
|
||||
setPortalPasswordSettings = async (
|
||||
minLength,
|
||||
upperCase,
|
||||
digits,
|
||||
specSymbols
|
||||
) => {
|
||||
setPortalPasswordSettings = async (minLength, upperCase, digits, specSymbols) => {
|
||||
const settings = await api.settings.setPortalPasswordSettings(
|
||||
minLength,
|
||||
upperCase,
|
||||
digits,
|
||||
specSymbols
|
||||
specSymbols,
|
||||
);
|
||||
this.setPasswordSettings(settings);
|
||||
};
|
||||
@ -680,8 +660,7 @@ class SettingsStore {
|
||||
...versionInfo,
|
||||
};
|
||||
|
||||
if (!this.buildVersionInfo.documentServer)
|
||||
this.buildVersionInfo.documentServer = "6.4.1";
|
||||
if (!this.buildVersionInfo.documentServer) this.buildVersionInfo.documentServer = "6.4.1";
|
||||
};
|
||||
|
||||
setTheme = (key) => {
|
||||
@ -699,8 +678,7 @@ class SettingsStore {
|
||||
case ThemeKeys.SystemStr:
|
||||
default:
|
||||
theme =
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ThemeKeys.DarkStr
|
||||
: ThemeKeys.BaseStr;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import {
|
||||
MainButtonTheme,
|
||||
CatalogItemTheme,
|
||||
CalendarTheme,
|
||||
DateItemTheme,
|
||||
RoundButtonTheme,
|
||||
BadgeTheme,
|
||||
SubmenuTextTheme,
|
||||
SubmenuItemLabelTheme,
|
||||
@ -200,6 +202,24 @@ const ColorTheme = forwardRef(
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ThemeType.RoundButton: {
|
||||
return (
|
||||
<RoundButtonTheme
|
||||
{...props}
|
||||
$currentColorScheme={currentColorScheme}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ThemeType.DateItem: {
|
||||
return (
|
||||
<DateItemTheme
|
||||
{...props}
|
||||
$currentColorScheme={currentColorScheme}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ThemeType.ComboButton: {
|
||||
return (
|
||||
<ComboButtonTheme
|
||||
|
@ -3,7 +3,6 @@ export const ThemeType = {
|
||||
MainButton: "mainButton",
|
||||
CatalogItem: "catalogItem",
|
||||
Badge: "badge",
|
||||
Calendar: 'calendar',
|
||||
SubmenuText: "submenuText",
|
||||
SubmenuItemLabel: "submenuItemLabel",
|
||||
ToggleButton: "toggleButton",
|
||||
@ -14,6 +13,8 @@ export const ThemeType = {
|
||||
FilterBlockItemTag: "filterBlockItemTag",
|
||||
IconWrapper: "iconWrapper",
|
||||
Calendar: "calendar",
|
||||
DateItem: "dateItem",
|
||||
RoundButton: "RoundButton",
|
||||
VersionBadge: "versionBadge",
|
||||
Textarea: "textarea",
|
||||
InputBlock: "inputBlock",
|
||||
|
@ -1,48 +1,15 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import styled, { css } from "styled-components";
|
||||
import {
|
||||
Container,
|
||||
DateItem,
|
||||
CurrentDateItem,
|
||||
RoundButton,
|
||||
} from '@docspace/components/calendar/styled-components';
|
||||
HeaderActionIcon,
|
||||
} from "@docspace/components/calendar/styled-components";
|
||||
|
||||
const getDefaultStyles = ({ $currentColorScheme }) =>
|
||||
$currentColorScheme &&
|
||||
css`
|
||||
${CurrentDateItem} {
|
||||
background: ${$currentColorScheme.main.accent};
|
||||
:hover {
|
||||
background-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
|
||||
:focus {
|
||||
background-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
}
|
||||
${DateItem} {
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.calendar.disabledColor
|
||||
: props.focused
|
||||
? $currentColorScheme.main.accent
|
||||
: props.theme.calendar.color};
|
||||
border-color: ${(props) => (props.focused ? $currentColorScheme.main.accent : 'transparent')};
|
||||
:focus {
|
||||
color: ${$currentColorScheme.main.accent};
|
||||
border-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
}
|
||||
${RoundButton} {
|
||||
:hover {
|
||||
outline: ${(props) =>
|
||||
props.disabled
|
||||
? `1px solid ${props.theme.calendar.outlineColor}`
|
||||
: `2px solid ${$currentColorScheme.main.accent}`};
|
||||
span {
|
||||
border-color: ${(props) =>
|
||||
props.disabled ? props.theme.calendar.disabledArrow : $currentColorScheme.main.accent};
|
||||
}
|
||||
}
|
||||
${HeaderActionIcon} {
|
||||
border-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
`;
|
||||
|
||||
|
28
packages/components/ColorTheme/styled/dateItem.js
Normal file
28
packages/components/ColorTheme/styled/dateItem.js
Normal file
@ -0,0 +1,28 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { DateItem } from "@docspace/components/calendar/styled-components";
|
||||
|
||||
const getDefaultStyles = ({ $currentColorScheme }) =>
|
||||
$currentColorScheme &&
|
||||
css`
|
||||
${(props) =>
|
||||
props.isCurrent &&
|
||||
css`
|
||||
background: ${$currentColorScheme.main.accent};
|
||||
:hover {
|
||||
background-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
|
||||
:focus {
|
||||
background-color: ${$currentColorScheme.main.accent};
|
||||
}
|
||||
`}
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.calendar.disabledColor
|
||||
: props.focused
|
||||
? $currentColorScheme.main.accent
|
||||
: props.theme.calendar.color};
|
||||
border-color: ${(props) => (props.focused ? $currentColorScheme.main.accent : "transparent")};
|
||||
`;
|
||||
|
||||
export default styled(DateItem)(getDefaultStyles);
|
@ -6,7 +6,11 @@ export { default as CatalogItemTheme } from "./catalogItem";
|
||||
|
||||
export { default as BadgeTheme } from "./badge";
|
||||
|
||||
export { default as CalendarTheme } from './calendar'
|
||||
export { default as CalendarTheme } from "./calendar";
|
||||
|
||||
export { default as RoundButtonTheme } from "./roundButton";
|
||||
|
||||
export { default as DateItemTheme } from "./dateItem";
|
||||
|
||||
export { default as SubmenuTextTheme } from "./submenuText";
|
||||
|
||||
|
19
packages/components/ColorTheme/styled/roundButton.js
Normal file
19
packages/components/ColorTheme/styled/roundButton.js
Normal file
@ -0,0 +1,19 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { RoundButton } from "@docspace/components/calendar/styled-components";
|
||||
|
||||
const getDefaultStyles = ({ $currentColorScheme }) =>
|
||||
$currentColorScheme &&
|
||||
css`
|
||||
:hover {
|
||||
outline: ${(props) =>
|
||||
props.disabled
|
||||
? `1px solid ${props.theme.calendar.outlineColor}`
|
||||
: `2px solid ${$currentColorScheme.main.accent}`};
|
||||
span {
|
||||
border-color: ${(props) =>
|
||||
props.disabled ? props.theme.calendar.disabledArrow : $currentColorScheme.main.accent};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default styled(RoundButton)(getDefaultStyles);
|
@ -18,6 +18,8 @@ const Calendar = ({
|
||||
style,
|
||||
initialDate,
|
||||
onChange,
|
||||
isMobile,
|
||||
forwardedRef,
|
||||
}) => {
|
||||
moment.locale(locale);
|
||||
|
||||
@ -41,7 +43,7 @@ const Calendar = ({
|
||||
);
|
||||
}
|
||||
initialDate.startOf("day");
|
||||
setSelectedDate(initialDate);
|
||||
// setSelectedDate(initialDate);
|
||||
setObservedDate(initialDate);
|
||||
}, []);
|
||||
|
||||
@ -51,6 +53,8 @@ const Calendar = ({
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
isMobile={isMobile}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
{selectedScene === 0 ? (
|
||||
<Days
|
||||
@ -61,6 +65,7 @@ const Calendar = ({
|
||||
handleDateChange={handleDateChange}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
) : selectedScene === 1 ? (
|
||||
<Months
|
||||
@ -70,6 +75,7 @@ const Calendar = ({
|
||||
selectedDate={selectedDate}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
) : (
|
||||
<Years
|
||||
@ -79,6 +85,7 @@ const Calendar = ({
|
||||
selectedDate={selectedDate}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
)}
|
||||
</ColorTheme>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "../../themes/base";
|
||||
|
||||
export const ArrowIcon = styled.span`
|
||||
@ -7,6 +7,21 @@ export const ArrowIcon = styled.span`
|
||||
border-bottom: 2px solid;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
${(props) =>
|
||||
props.next &&
|
||||
css`
|
||||
transform: rotate(-45deg);
|
||||
top: 11.5px;
|
||||
left: 12.5px;
|
||||
`}
|
||||
${(props) =>
|
||||
props.previous &&
|
||||
css`
|
||||
transform: rotate(135deg);
|
||||
top: 14px;
|
||||
left: 12.5px;
|
||||
`}
|
||||
`;
|
||||
|
||||
ArrowIcon.defaultProps = { theme: Base };
|
||||
|
@ -0,0 +1,18 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const CalendarContainer = styled.div`
|
||||
display: grid;
|
||||
row-gap: ${(props) => (props.big ? "26.7px" : "9px")};
|
||||
column-gap: ${(props) =>
|
||||
props.big
|
||||
? props.isMobile
|
||||
? "8%"
|
||||
: "41.3px"
|
||||
: props.isMobile
|
||||
? "2%"
|
||||
: "14px"};
|
||||
grid-template-columns: ${(props) =>
|
||||
props.big ? "repeat(4, 1fr)" : "repeat(7, 1fr)"};
|
||||
box-sizing: border-box;
|
||||
padding: ${(props) => (props.big ? "14px 6px 6px 6px" : "0 6px")};
|
||||
`;
|
@ -1,12 +1,13 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const Container = styled.div`
|
||||
width: 432px;
|
||||
height: 446px;
|
||||
box-sizing: border-box;
|
||||
|
||||
padding: 30px 28px 28px 28px;
|
||||
|
||||
width: ${(props) => (props.isMobile ? "100%" : "432px")};
|
||||
height: ${(props) => (props.isMobile ? "420px" : "446px")};
|
||||
padding: ${(props) => (props.isMobile ? "16px" : "30px 28px 28px 28px")};
|
||||
box-shadow: 0px 12px 40px rgba(4, 15, 27, 0.12);
|
||||
border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
z-index: 320;
|
||||
background-color: ${(props) => props.theme.backgroundColor};
|
||||
`;
|
||||
|
@ -1,18 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
import { DateItem } from "./DateItem";
|
||||
|
||||
export const CurrentDateItem = styled(DateItem)`
|
||||
background: #4781D1;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
|
||||
:hover{
|
||||
background-color: #4781D1;
|
||||
color: white;
|
||||
}
|
||||
|
||||
:focus{
|
||||
background-color: #4781D1;
|
||||
color: white;
|
||||
}
|
||||
`;
|
@ -1,4 +1,4 @@
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "../../themes/base";
|
||||
|
||||
export const DateItem = styled.button`
|
||||
@ -6,15 +6,8 @@ export const DateItem = styled.button`
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
border-radius: 50%;
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.calendar.disabledColor
|
||||
: props.focused
|
||||
? "#4781D1"
|
||||
: props.theme.calendar.color};
|
||||
|
||||
border: 2px solid;
|
||||
border-color: ${(props) => (props.focused ? "#4781D1" : "transparent")};
|
||||
background-color: transparent;
|
||||
|
||||
width: ${(props) => (props.big ? "60px" : "40px")};
|
||||
@ -27,9 +20,37 @@ export const DateItem = styled.button`
|
||||
:hover {
|
||||
cursor: ${(props) => (props.disabled ? "default" : "pointer")};
|
||||
background: ${(props) =>
|
||||
props.disabled
|
||||
? "transparent"
|
||||
: props.theme.calendar.onHoverBackground};
|
||||
props.disabled ? "transparent" : props.theme.calendar.onHoverBackground};
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isCurrent &&
|
||||
css`
|
||||
color: white !important;
|
||||
|
||||
:hover {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
:focus {
|
||||
color: white !important;
|
||||
}
|
||||
`}
|
||||
${(props) =>
|
||||
props.isSecondary &&
|
||||
css`
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.calendar.disabledColor
|
||||
: props.theme.calendar.pastColor} !important;
|
||||
|
||||
:hover {
|
||||
cursor: ${(props) => (props.disabled ? "auto" : "pointer")};
|
||||
color: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.calendar.disabledColor
|
||||
: props.theme.calendar.pastColor} !important;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
DateItem.defaultProps = { theme: Base };
|
||||
|
@ -1,10 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const DaysContainer = styled.div`
|
||||
display: grid;
|
||||
row-gap: 9px;
|
||||
column-gap: 14px;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
box-sizing: border-box;
|
||||
padding: 0 6px;
|
||||
`
|
@ -0,0 +1,9 @@
|
||||
import styled from "styled-components";
|
||||
import { ArrowIcon } from "./ArrowIcon";
|
||||
|
||||
export const HeaderActionIcon = styled(ArrowIcon)`
|
||||
transform: rotate(225deg);
|
||||
top: 11px;
|
||||
right: -14.29px;
|
||||
border-color: ${(props) => props.theme.calendar.outlineColor};
|
||||
`;
|
@ -8,4 +8,4 @@ export const HeaderContainer = styled.header`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
`;
|
||||
|
@ -1,10 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export const MonthsContainer = styled.div`
|
||||
display: grid;
|
||||
row-gap: 26.7px;
|
||||
column-gap: 41.3px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
box-sizing: border-box;
|
||||
padding: 14px 6px 6px 6px;
|
||||
`
|
@ -1,8 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
import { ArrowIcon } from "./ArrowIcon";
|
||||
|
||||
export const NextIcon = styled(ArrowIcon)`
|
||||
transform: rotate(-45deg);
|
||||
top: 11.5px;
|
||||
left: 12.5px;
|
||||
`;
|
@ -1,8 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
import { ArrowIcon } from "./ArrowIcon";
|
||||
|
||||
export const PrevIcon = styled(ArrowIcon)`
|
||||
transform: rotate(135deg);
|
||||
top: 14px;
|
||||
left: 12.5px;
|
||||
`;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user