diff --git a/Dockerfile b/Dockerfile index 5641edcfb..4aa37d447 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,7 +86,7 @@ WORKDIR /app # Copy from build stages COPY --from=backend /build/ . -COPY --from=frontend /build/ wwwroot/build/ +COPY --from=frontend /build/browser wwwroot/build/ EXPOSE 80 EXPOSE 443 diff --git a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj index 63646d3a9..884c1e0bc 100644 --- a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj +++ b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj @@ -1,7 +1,7 @@  net7.0 - 11.0 + latest enable enable diff --git a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs index e853d6e20..93760635d 100644 --- a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs +++ b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs @@ -15,10 +15,10 @@ using Squidex.Infrastructure.Json; namespace Squidex.Extensions.Text.ElasticSearch; -public sealed class ElasticSearchTextIndex : ITextIndex, IInitializable +public sealed partial class ElasticSearchTextIndex : ITextIndex, IInitializable { - private static readonly Regex LanguageRegex = new Regex(@"[^\w]+([a-z\-_]{2,}):", RegexOptions.Compiled | RegexOptions.ExplicitCapture); - private static readonly Regex LanguageRegexStart = new Regex(@"$^([a-z\-_]{2,}):", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex RegexLanguageNormal = BuildLanguageRegexNormal(); + private static readonly Regex RegexLanguageStart = BuildLanguageRegexStart(); private readonly IJsonSerializer jsonSerializer; private readonly IElasticSearchClient elasticClient; private readonly QueryParser queryParser = new QueryParser(ElasticSearchIndexDefinition.GetFieldPath); @@ -227,4 +227,10 @@ public sealed class ElasticSearchTextIndex : ITextIndex, IInitializable { return scope == SearchScope.Published ? "servePublished" : "serveAll"; } + + [GeneratedRegex("[^\\w]+([a-z\\-_]{2,}):", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildLanguageRegexNormal(); + + [GeneratedRegex("$^([a-z\\-_]{2,}):", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildLanguageRegexStart(); } diff --git a/backend/i18n/translator/Squidex.Translator/Squidex.Translator.csproj b/backend/i18n/translator/Squidex.Translator/Squidex.Translator.csproj index 0bfffc3b4..1adeb37db 100644 --- a/backend/i18n/translator/Squidex.Translator/Squidex.Translator.csproj +++ b/backend/i18n/translator/Squidex.Translator/Squidex.Translator.csproj @@ -3,7 +3,7 @@ Exe net7.0 - 11.0 + latest enable NU1608 diff --git a/backend/src/Migrations/Migrations.csproj b/backend/src/Migrations/Migrations.csproj index 458c4d6e8..70a27707b 100644 --- a/backend/src/Migrations/Migrations.csproj +++ b/backend/src/Migrations/Migrations.csproj @@ -1,7 +1,7 @@  net7.0 - 11.0 + latest enable enable diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj index 778c209f1..7748b2ec0 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj @@ -2,7 +2,7 @@ net7.0 Squidex.Domain.Apps.Core - 11.0 + latest enable en enable diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs index e28608098..097ad12f0 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs @@ -23,7 +23,7 @@ using Squidex.Text; namespace Squidex.Domain.Apps.Core.Scripting; -public sealed class ScriptingCompleter +public sealed partial class ScriptingCompleter { private readonly IEnumerable descriptors; private static readonly FilterSchema DynamicData = new FilterSchema(FilterSchemaType.Object) @@ -112,9 +112,9 @@ public sealed class ScriptingCompleter return new Process(descriptors).UsageTrigger(); } - private sealed class Process + private sealed partial class Process { - private static readonly Regex PropertyRegex = new Regex(@"^(?!\d)[\w$]+$", RegexOptions.Compiled); + private static readonly Regex RegexProperty = BuildPropertyRegex(); private readonly Stack prefixes = new Stack(); private readonly Dictionary result = new Dictionary(); private readonly IEnumerable descriptors; @@ -523,7 +523,7 @@ public sealed class ScriptingCompleter { prefixes.Push(name); } - else if (PropertyRegex.IsMatch(name)) + else if (RegexProperty.IsMatch(name)) { prefixes.Push($".{name}"); } @@ -532,5 +532,8 @@ public sealed class ScriptingCompleter prefixes.Push($"['{name}']"); } } + + [GeneratedRegex("^(?!\\d)[\\w$]+$", RegexOptions.Compiled)] + private static partial Regex BuildPropertyRegex(); } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 0ecc9f021..4fb5c5495 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -2,7 +2,7 @@ net7.0 Squidex.Domain.Apps.Core - 11.0 + latest enable en enable diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj index f247b82a5..2e7d62edc 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj @@ -1,7 +1,7 @@  net7.0 - 11.0 + latest enable enable diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplatesClient.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplatesClient.cs index 7add382f8..6cfbf1d11 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplatesClient.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplatesClient.cs @@ -11,9 +11,9 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps.Templates; -public sealed class TemplatesClient +public sealed partial class TemplatesClient { - private static readonly Regex Regex = new Regex("\\* \\[(?.*)\\]\\((?<Name>.*)\\/README\\.md\\): (?<Description>.*)", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex RegexTemplate = BuildTemplateRegex(); private readonly IHttpClientFactory httpClientFactory; private readonly TemplatesOptions options; @@ -34,7 +34,7 @@ public sealed class TemplatesClient var text = await httpClient.GetStringAsync(url, ct); - foreach (Match match in Regex.Matches(text).OfType<Match>()) + foreach (var match in RegexTemplate.Matches(text).OfType<Match>()) { var currentName = match.Groups["Name"].Value; @@ -61,7 +61,7 @@ public sealed class TemplatesClient var text = await httpClient.GetStringAsync(url, ct); - foreach (Match match in Regex.Matches(text).OfType<Match>()) + foreach (Match match in RegexTemplate.Matches(text).OfType<Match>()) { var title = match.Groups["Title"].Value; @@ -97,4 +97,7 @@ public sealed class TemplatesClient return null; } + + [GeneratedRegex("\\* \\[(?<Title>.*)\\]\\((?<Name>.*)\\/README\\.md\\): (?<Description>.*)", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildTemplateRegex(); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index abb308e63..bdedc1ae4 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <NeutralLanguage>en</NeutralLanguage> <Nullable>enable</Nullable> diff --git a/backend/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/backend/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj index cb1e9dcf6..926d19563 100644 --- a/backend/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj +++ b/backend/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index 6946662d9..390e4c47d 100644 --- a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj index 81e3fe785..6b4937e1c 100644 --- a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj +++ b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj b/backend/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj index d2b7e2c3d..adddb2b04 100644 --- a/backend/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj +++ b/backend/src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Infrastructure</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj b/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj index 96cf9374d..2b77d4dc0 100644 --- a/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj +++ b/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Infrastructure</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj b/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj index 6da25bf78..a1e626c90 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj +++ b/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Infrastructure</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex.Infrastructure/Language.cs b/backend/src/Squidex.Infrastructure/Language.cs index f4118cb97..1f424e5f1 100644 --- a/backend/src/Squidex.Infrastructure/Language.cs +++ b/backend/src/Squidex.Infrastructure/Language.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure; [TypeConverter(typeof(LanguageTypeConverter))] public partial record Language { - private static readonly Regex CultureRegex = new Regex("^(?<Code>[a-z]{2})(\\-[a-z]{2})?$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); + private static readonly Regex RegexCulture = BuildCultureRegex(); public static Language GetLanguage(string iso2Code) { @@ -85,7 +85,7 @@ public partial record Language if (input.Length != 2) { - var match = CultureRegex.Match(input); + var match = RegexCulture.Match(input); if (!match.Success) { @@ -107,4 +107,7 @@ public partial record Language { return EnglishName; } + + [GeneratedRegex("^(?<Code>[a-z]{2})(\\-[a-z]{2})?$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, "en-US")] + private static partial Regex BuildCultureRegex(); } diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 029014e54..41c3d5e6f 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <NeutralLanguage>en</NeutralLanguage> <Nullable>enable</Nullable> diff --git a/backend/src/Squidex.Infrastructure/StringExtensions.cs b/backend/src/Squidex.Infrastructure/StringExtensions.cs index 81dad468d..d50377a9f 100644 --- a/backend/src/Squidex.Infrastructure/StringExtensions.cs +++ b/backend/src/Squidex.Infrastructure/StringExtensions.cs @@ -13,10 +13,10 @@ using System.Text.RegularExpressions; namespace Squidex.Infrastructure; -public static class StringExtensions +public static partial class StringExtensions { - private static readonly Regex RegexEmail = new Regex(@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-||_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+([a-z]+|\d|-|\.{0,1}|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); - private static readonly Regex RegexProperty = new Regex("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex RegexEmail = BuildEmailRegex(); + private static readonly Regex RegexProperty = BuildPropertyRegex(); private static readonly JsonSerializerOptions JsonEscapeOptions = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping @@ -113,4 +113,10 @@ public static class StringExtensions return sb; } + + [GeneratedRegex("^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-||_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+([a-z]+|\\d|-|\\.{0,1}|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])?([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled, "en-US")] + private static partial Regex BuildEmailRegex(); + + [GeneratedRegex("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildPropertyRegex(); } diff --git a/backend/src/Squidex.Shared/DefaultClients.cs b/backend/src/Squidex.Shared/DefaultClients.cs index 554685565..7a94ed733 100644 --- a/backend/src/Squidex.Shared/DefaultClients.cs +++ b/backend/src/Squidex.Shared/DefaultClients.cs @@ -5,10 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Shared +namespace Squidex.Shared; + +public static class DefaultClients { - public static class DefaultClients - { - public const string Frontend = "squidex-frontend"; - } + public const string Frontend = "squidex-frontend"; } diff --git a/backend/src/Squidex.Shared/Identity/SquidexClaimTypes.cs b/backend/src/Squidex.Shared/Identity/SquidexClaimTypes.cs index 335668e41..a523089f5 100644 --- a/backend/src/Squidex.Shared/Identity/SquidexClaimTypes.cs +++ b/backend/src/Squidex.Shared/Identity/SquidexClaimTypes.cs @@ -5,36 +5,35 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Shared.Identity +namespace Squidex.Shared.Identity; + +public static class SquidexClaimTypes { - public static class SquidexClaimTypes - { - public const string ClientSecret = "urn:squidex:clientSecret"; + public const string ClientSecret = "urn:squidex:clientSecret"; - public const string Consent = "urn:squidex:consent"; + public const string Consent = "urn:squidex:consent"; - public const string ConsentForEmails = "urn:squidex:consent:emails"; + public const string ConsentForEmails = "urn:squidex:consent:emails"; - public const string Custom = "urn:squidex:custom"; + public const string Custom = "urn:squidex:custom"; - public const string DisplayName = "urn:squidex:name"; + public const string DisplayName = "urn:squidex:name"; - public const string Hidden = "urn:squidex:hidden"; + public const string Hidden = "urn:squidex:hidden"; - public const string Invited = "urn:squidex:invited"; + public const string Invited = "urn:squidex:invited"; - public const string NotifoKey = "urn:squidex:notifo"; + public const string NotifoKey = "urn:squidex:notifo"; - public const string Permissions = "urn:squidex:permissions"; + public const string Permissions = "urn:squidex:permissions"; - public const string PictureUrl = "urn:squidex:picture"; + public const string PictureUrl = "urn:squidex:picture"; - public const string PictureUrlStore = "store"; + public const string PictureUrlStore = "store"; - public const string Answer = "urn:squidex:answer"; + public const string Answer = "urn:squidex:answer"; - public const string TotalApps = "urn:squidex:internal:totalApps"; + public const string TotalApps = "urn:squidex:internal:totalApps"; - public const string UIProperty = "urn:squidex:ui"; - } + public const string UIProperty = "urn:squidex:ui"; } diff --git a/backend/src/Squidex.Shared/Identity/SquidexClaimsExtensions.cs b/backend/src/Squidex.Shared/Identity/SquidexClaimsExtensions.cs index 743747e75..054473dea 100644 --- a/backend/src/Squidex.Shared/Identity/SquidexClaimsExtensions.cs +++ b/backend/src/Squidex.Shared/Identity/SquidexClaimsExtensions.cs @@ -15,236 +15,241 @@ using System.Text.RegularExpressions; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Security; -namespace Squidex.Shared.Identity +namespace Squidex.Shared.Identity; + +public static partial class SquidexClaimsExtensions { - public static class SquidexClaimsExtensions - { - private const string ClientPrefix = "client_"; + private const string ClientPrefix = "client_"; - private static readonly Regex KeyValueAppClaim = new Regex("(?<App>[\\S]+),(?<Key>[^=]+)=(?<Value>.+)", RegexOptions.Compiled); - private static readonly Regex KeyValueClaim = new Regex("(?<Key>[^=]+)=(?<Value>.+)", RegexOptions.Compiled); + private static readonly Regex RegexKeyValueAppClaim = BuildKeyValueAppClaimRegex(); + private static readonly Regex RegexKeyValueClaim = BuildKeyValueClaimRegex(); - public static PermissionSet Permissions(this IEnumerable<Claim> user) - { - var permissions = user.GetClaims(SquidexClaimTypes.Permissions).Select(x => x.Value); + public static PermissionSet Permissions(this IEnumerable<Claim> user) + { + var permissions = user.GetClaims(SquidexClaimTypes.Permissions).Select(x => x.Value); - return new PermissionSet(permissions); - } + return new PermissionSet(permissions); + } - public static bool IsHidden(this IEnumerable<Claim> user) - { - return user.HasClaimValue(SquidexClaimTypes.Hidden, "true"); - } + public static bool IsHidden(this IEnumerable<Claim> user) + { + return user.HasClaimValue(SquidexClaimTypes.Hidden, "true"); + } - public static bool HasConsent(this IEnumerable<Claim> user) - { - return user.HasClaimValue(SquidexClaimTypes.Consent, "true"); - } + public static bool HasConsent(this IEnumerable<Claim> user) + { + return user.HasClaimValue(SquidexClaimTypes.Consent, "true"); + } - public static bool HasConsentForEmails(this IEnumerable<Claim> user) - { - return user.HasClaimValue(SquidexClaimTypes.ConsentForEmails, "true"); - } + public static bool HasConsentForEmails(this IEnumerable<Claim> user) + { + return user.HasClaimValue(SquidexClaimTypes.ConsentForEmails, "true"); + } - public static bool HasDisplayName(this IEnumerable<Claim> user) - { - return user.HasClaim(SquidexClaimTypes.DisplayName); - } + public static bool HasDisplayName(this IEnumerable<Claim> user) + { + return user.HasClaim(SquidexClaimTypes.DisplayName); + } - public static bool HasPictureUrl(this IEnumerable<Claim> user) - { - return user.HasClaim(SquidexClaimTypes.PictureUrl); - } + public static bool HasPictureUrl(this IEnumerable<Claim> user) + { + return user.HasClaim(SquidexClaimTypes.PictureUrl); + } - public static bool IsPictureUrlStored(this IEnumerable<Claim> user) - { - return user.HasClaimValue(SquidexClaimTypes.PictureUrl, SquidexClaimTypes.PictureUrlStore); - } + public static bool IsPictureUrlStored(this IEnumerable<Claim> user) + { + return user.HasClaimValue(SquidexClaimTypes.PictureUrl, SquidexClaimTypes.PictureUrlStore); + } - public static string? ClientSecret(this IEnumerable<Claim> user) - { - return user.GetClaimValue(SquidexClaimTypes.ClientSecret); - } + public static string? ClientSecret(this IEnumerable<Claim> user) + { + return user.GetClaimValue(SquidexClaimTypes.ClientSecret); + } - public static string? PictureUrl(this IEnumerable<Claim> user) - { - return user.GetClaimValue(SquidexClaimTypes.PictureUrl); - } + public static string? PictureUrl(this IEnumerable<Claim> user) + { + return user.GetClaimValue(SquidexClaimTypes.PictureUrl); + } - public static string? DisplayName(this IEnumerable<Claim> user) - { - return user.GetClaimValue(SquidexClaimTypes.DisplayName); - } + public static string? DisplayName(this IEnumerable<Claim> user) + { + return user.GetClaimValue(SquidexClaimTypes.DisplayName); + } - public static string? Answer(this IEnumerable<Claim> user, string name) - { - var prefix = $"{name}="; + public static string? Answer(this IEnumerable<Claim> user, string name) + { + var prefix = $"{name}="; - foreach (var claim in user) + foreach (var claim in user) + { + if (claim.Type == SquidexClaimTypes.Answer && claim.Value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - if (claim.Type == SquidexClaimTypes.Answer && claim.Value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - return claim.Value[prefix.Length..]; - } + return claim.Value[prefix.Length..]; } - - return null; } - public static int GetTotalApps(this IEnumerable<Claim> user) - { - var value = user.GetClaimValue(SquidexClaimTypes.TotalApps); + return null; + } - int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result); + public static int GetTotalApps(this IEnumerable<Claim> user) + { + var value = user.GetClaimValue(SquidexClaimTypes.TotalApps); - return result; - } + int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result); - public static bool HasClaim(this IEnumerable<Claim> user, string type) - { - return user.GetClaims(type).Any(); - } + return result; + } - public static bool HasClaimValue(this IEnumerable<Claim> user, string type, string value) - { - return user.GetClaims(type).Any(x => string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase)); - } + public static bool HasClaim(this IEnumerable<Claim> user, string type) + { + return user.GetClaims(type).Any(); + } - public static IEnumerable<Claim> GetSquidexClaims(this IEnumerable<Claim> user) + public static bool HasClaimValue(this IEnumerable<Claim> user, string type, string value) + { + return user.GetClaims(type).Any(x => string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase)); + } + + public static IEnumerable<Claim> GetSquidexClaims(this IEnumerable<Claim> user) + { + const string prefix = "urn:squidex:"; + + foreach (var claim in user) { - const string prefix = "urn:squidex:"; + var type = GetType(claim); - foreach (var claim in user) + if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - var type = GetType(claim); - - if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - yield return claim; - } + yield return claim; } } + } - public static IEnumerable<(string Name, string Value)> GetCustomProperties(this IEnumerable<Claim> user) + public static IEnumerable<(string Name, string Value)> GetCustomProperties(this IEnumerable<Claim> user) + { + var prefix = $"{SquidexClaimTypes.Custom}:"; + + foreach (var claim in user) { - var prefix = $"{SquidexClaimTypes.Custom}:"; + var type = GetType(claim); - foreach (var claim in user) + if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - var type = GetType(claim); + var name = type[prefix.Length..].ToString(); - if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - var name = type[prefix.Length..].ToString(); + yield return (name.Trim(), claim.Value.Trim()); + } + else if (type.Equals(SquidexClaimTypes.Custom, StringComparison.OrdinalIgnoreCase)) + { + var match = RegexKeyValueClaim.Match(claim.Value); - yield return (name.Trim(), claim.Value.Trim()); - } - else if (type.Equals(SquidexClaimTypes.Custom, StringComparison.OrdinalIgnoreCase)) + if (match.Success) { - var match = KeyValueClaim.Match(claim.Value); - - if (match.Success) - { - yield return (match.Groups["Key"].Value.Trim(), match.Groups["Value"].Value.Trim()); - } + yield return (match.Groups["Key"].Value.Trim(), match.Groups["Value"].Value.Trim()); } } } + } - public static IEnumerable<(string Name, JsonValue Value)> GetUIProperties(this IEnumerable<Claim> user, string app) + public static IEnumerable<(string Name, JsonValue Value)> GetUIProperties(this IEnumerable<Claim> user, string app) + { + var prefix = $"{SquidexClaimTypes.UIProperty}:{app}:"; + + static JsonValue Parse(string value) { - var prefix = $"{SquidexClaimTypes.UIProperty}:{app}:"; + value = value.Trim(); - static JsonValue Parse(string value) + try { - value = value.Trim(); - - try - { - var root = JsonDocument.Parse(value).RootElement; + var root = JsonDocument.Parse(value).RootElement; - return JsonValue.Create(root); - } - catch - { - return JsonValue.Create(value); - } + return JsonValue.Create(root); } - - foreach (var claim in user) + catch { - var type = GetType(claim); - - if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - var name = type[prefix.Length..].ToString(); - - yield return (name.Trim(), Parse(claim.Value)); - } - else if (type.Equals(SquidexClaimTypes.UIProperty, StringComparison.OrdinalIgnoreCase)) - { - if (!claim.Value.StartsWith(app, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var match = KeyValueAppClaim.Match(claim.Value); - - if (match.Success) - { - yield return (match.Groups["Key"].Value.Trim(), Parse(match.Groups["Value"].Value)); - } - } + return JsonValue.Create(value); } } - public static string? PictureNormalizedUrl(this IEnumerable<Claim> user) + foreach (var claim in user) { - var url = user.FirstOrDefault(x => x.Type == SquidexClaimTypes.PictureUrl)?.Value; + var type = GetType(claim); + + if (type.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + var name = type[prefix.Length..].ToString(); - if (Uri.IsWellFormedUriString(url, UriKind.Absolute) && url.Contains("gravatar", StringComparison.Ordinal)) + yield return (name.Trim(), Parse(claim.Value)); + } + else if (type.Equals(SquidexClaimTypes.UIProperty, StringComparison.OrdinalIgnoreCase)) { - if (url.Contains('?', StringComparison.Ordinal)) + if (!claim.Value.StartsWith(app, StringComparison.OrdinalIgnoreCase)) { - url += "&d=404"; + continue; } - else + + var match = RegexKeyValueAppClaim.Match(claim.Value); + + if (match.Success) { - url += "?d=404"; + yield return (match.Groups["Key"].Value.Trim(), Parse(match.Groups["Value"].Value)); } } - - return url; } + } - private static string? GetClaimValue(this IEnumerable<Claim> user, string type) - { - return user.GetClaims(type).FirstOrDefault()?.Value; - } + public static string? PictureNormalizedUrl(this IEnumerable<Claim> user) + { + var url = user.FirstOrDefault(x => x.Type == SquidexClaimTypes.PictureUrl)?.Value; - private static IEnumerable<Claim> GetClaims(this IEnumerable<Claim> user, string request) + if (Uri.IsWellFormedUriString(url, UriKind.Absolute) && url.Contains("gravatar", StringComparison.Ordinal)) { - foreach (var claim in user) + if (url.Contains('?', StringComparison.Ordinal)) { - var type = GetType(claim); - - if (type.Equals(request, StringComparison.OrdinalIgnoreCase)) - { - yield return claim; - } + url += "&d=404"; + } + else + { + url += "?d=404"; } } - private static ReadOnlySpan<char> GetType(Claim claim) + return url; + } + + private static string? GetClaimValue(this IEnumerable<Claim> user, string type) + { + return user.GetClaims(type).FirstOrDefault()?.Value; + } + + private static IEnumerable<Claim> GetClaims(this IEnumerable<Claim> user, string request) + { + foreach (var claim in user) { - var type = claim.Type.AsSpan(); + var type = GetType(claim); - if (type.StartsWith(ClientPrefix, StringComparison.OrdinalIgnoreCase)) + if (type.Equals(request, StringComparison.OrdinalIgnoreCase)) { - type = type[ClientPrefix.Length..]; + yield return claim; } + } + } + + private static ReadOnlySpan<char> GetType(Claim claim) + { + var type = claim.Type.AsSpan(); - return type; + if (type.StartsWith(ClientPrefix, StringComparison.OrdinalIgnoreCase)) + { + type = type[ClientPrefix.Length..]; } + + return type; } + + [GeneratedRegex("(?<App>[\\S]+),(?<Key>[^=]+)=(?<Value>.+)", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildKeyValueAppClaimRegex(); + + [GeneratedRegex("(?<Key>[^=]+)=(?<Value>.+)", RegexOptions.ExplicitCapture | RegexOptions.Compiled)] + private static partial Regex BuildKeyValueClaimRegex(); } diff --git a/backend/src/Squidex.Shared/PermissionExtensions.cs b/backend/src/Squidex.Shared/PermissionExtensions.cs index e7dd49925..9c2c3b6b2 100644 --- a/backend/src/Squidex.Shared/PermissionExtensions.cs +++ b/backend/src/Squidex.Shared/PermissionExtensions.cs @@ -8,29 +8,28 @@ using System.Linq; using Squidex.Infrastructure.Security; -namespace Squidex.Shared +namespace Squidex.Shared; + +public static class PermissionExtensions { - public static class PermissionExtensions + public static bool Allows(this PermissionSet permissions, string id, string app = Permission.Any, string schema = Permission.Any, string team = Permission.Any) { - public static bool Allows(this PermissionSet permissions, string id, string app = Permission.Any, string schema = Permission.Any, string team = Permission.Any) - { - var permission = PermissionIds.ForApp(id, app, schema, team); + var permission = PermissionIds.ForApp(id, app, schema, team); - return permissions.Allows(permission); - } + return permissions.Allows(permission); + } - public static string[] ToAppNames(this PermissionSet permissions) - { - var matching = permissions.Where(x => x.StartsWith("squidex.apps.")); + public static string[] ToAppNames(this PermissionSet permissions) + { + var matching = permissions.Where(x => x.StartsWith("squidex.apps.")); - var result = - matching - .Select(x => x.Id.Split('.')).Where(x => x.Length > 2) - .Select(x => x[2]) - .Distinct() - .ToArray(); + var result = + matching + .Select(x => x.Id.Split('.')).Where(x => x.Length > 2) + .Select(x => x[2]) + .Distinct() + .ToArray(); - return result; - } + return result; } } diff --git a/backend/src/Squidex.Shared/PermissionIds.cs b/backend/src/Squidex.Shared/PermissionIds.cs index 18298cba0..cb31e887c 100644 --- a/backend/src/Squidex.Shared/PermissionIds.cs +++ b/backend/src/Squidex.Shared/PermissionIds.cs @@ -9,219 +9,218 @@ using System; using Squidex.Infrastructure; using Squidex.Infrastructure.Security; -namespace Squidex.Shared +namespace Squidex.Shared; + +public static class PermissionIds { - public static class PermissionIds + public const string All = "squidex.*"; + + public const string Admin = "squidex.admin.*"; + + // Admin App Creation + public const string AdminAppCreate = "squidex.admin.apps.create"; + + // Admin Team Creation + public const string AdminTeamCreate = "squidex.admin.teams.create"; + + // Backup Admin + public const string AdminRestore = "squidex.admin.restore"; + + // Event Admin + public const string AdminEvents = "squidex.admin.events"; + public const string AdminEventsRead = "squidex.admin.events.read"; + public const string AdminEventsManage = "squidex.admin.events.manage"; + + // User Admin + public const string AdminUsers = "squidex.admin.users"; + public const string AdminUsersRead = "squidex.admin.users.read"; + public const string AdminUsersCreate = "squidex.admin.users.create"; + public const string AdminUsersUpdate = "squidex.admin.users.update"; + public const string AdminUsersUnlock = "squidex.admin.users.unlock"; + public const string AdminUsersLock = "squidex.admin.users.lock"; + + // Team + public const string Team = "squidex.teams.{team}"; + + // Team Transfer + public const string Transfer = "squidex.transfer"; + + // Team General + public const string TeamAdmin = "squidex.teams.{team}.*"; + public const string TeamUpdate = "squidex.teams.{team}.update"; + + // Team Contributors + public const string TeamContributors = "squidex.teams.{team}.contributors"; + public const string TeamContributorsRead = "squidex.teams.{team}.contributors.read"; + public const string TeamContributorsAssign = "squidex.teams.{team}.contributors.assign"; + public const string TeamContributorsRevoke = "squidex.teams.{team}.contributors.revoke"; + + // Team Plans + public const string TeamPlans = "squidex.teams.{team}.plans"; + public const string TeamPlansRead = "squidex.teams.{team}.plans.read"; + public const string TeamPlansChange = "squidex.teams.{team}.plans.change"; + + // Team Usage + public const string TeamUsage = "squidex.teams.{team}.usage"; + + // Team History + public const string TeamHistory = "squidex.teams.{team}.history"; + + // App + public const string App = "squidex.apps.{app}"; + + // App + public const string AppNoScripting = "squidex.apps.{app}.no-scripting"; + + // App General + public const string AppAdmin = "squidex.apps.{app}.*"; + public const string AppDelete = "squidex.apps.{app}.delete"; + public const string AppTransfer = "squidex.apps.{app}.transfer"; + public const string AppUpdate = "squidex.apps.{app}.update"; + public const string AppUpdateSettings = "squidex.apps.{app}.settings"; + + // App Image + public const string AppImageUpload = "squidex.apps.{app}.image"; + public const string AppImageDelete = "squidex.apps.{app}.image"; + + // App History + public const string AppHistory = "squidex.apps.{app}.history"; + + // App Ping + public const string AppPing = "squidex.apps.{app}.ping"; + + // App Search + public const string AppSearch = "squidex.apps.{app}.search"; + + // App Translate + public const string AppTranslate = "squidex.apps.{app}.translate"; + + // App Usage + public const string AppUsage = "squidex.apps.{app}.usage"; + + // App Comments + public const string AppComments = "squidex.apps.{app}.comments"; + public const string AppCommentsRead = "squidex.apps.{app}.comments.read"; + public const string AppCommentsCreate = "squidex.apps.{app}.comments.create"; + public const string AppCommentsUpdate = "squidex.apps.{app}.comments.update"; + public const string AppCommentsDelete = "squidex.apps.{app}.comments.delete"; + + // App Clients + public const string AppClients = "squidex.apps.{app}.clients"; + public const string AppClientsRead = "squidex.apps.{app}.clients.read"; + public const string AppClientsCreate = "squidex.apps.{app}.clients.create"; + public const string AppClientsUpdate = "squidex.apps.{app}.clients.update"; + public const string AppClientsDelete = "squidex.apps.{app}.clients.delete"; + + // App Contributors + public const string AppContributors = "squidex.apps.{app}.contributors"; + public const string AppContributorsRead = "squidex.apps.{app}.contributors.read"; + public const string AppContributorsAssign = "squidex.apps.{app}.contributors.assign"; + public const string AppContributorsRevoke = "squidex.apps.{app}.contributors.revoke"; + + // App Languages + public const string AppLanguages = "squidex.apps.{app}.languages"; + public const string AppLanguagesRead = "squidex.apps.{app}.languages.read"; + public const string AppLanguagesCreate = "squidex.apps.{app}.languages.create"; + public const string AppLanguagesUpdate = "squidex.apps.{app}.languages.update"; + public const string AppLanguagesDelete = "squidex.apps.{app}.languages.delete"; + + // App Roles + public const string AppRoles = "squidex.apps.{app}.roles"; + public const string AppRolesRead = "squidex.apps.{app}.roles.read"; + public const string AppRolesCreate = "squidex.apps.{app}.roles.create"; + public const string AppRolesUpdate = "squidex.apps.{app}.roles.update"; + public const string AppRolesDelete = "squidex.apps.{app}.roles.delete"; + + // App Workflows + public const string AppWorkflows = "squidex.apps.{app}.workflows"; + public const string AppWorkflowsRead = "squidex.apps.{app}.workflows.read"; + public const string AppWorkflowsCreate = "squidex.apps.{app}.workflows.create"; + public const string AppWorkflowsUpdate = "squidex.apps.{app}.workflows.update"; + public const string AppWorkflowsDelete = "squidex.apps.{app}.workflows.delete"; + + // App Backups + public const string AppBackups = "squidex.apps.{app}.backups"; + public const string AppBackupsRead = "squidex.apps.{app}.backups.read"; + public const string AppBackupsCreate = "squidex.apps.{app}.backups.create"; + public const string AppBackupsDelete = "squidex.apps.{app}.backups.delete"; + public const string AppBackupsDownload = "squidex.apps.{app}.backups.download"; + + // App Plans + public const string AppPlans = "squidex.apps.{app}.plans"; + public const string AppPlansRead = "squidex.apps.{app}.plans.read"; + public const string AppPlansChange = "squidex.apps.{app}.plans.change"; + + // App Assets + public const string AppAssets = "squidex.apps.{app}.assets"; + public const string AppAssetsRead = "squidex.apps.{app}.assets.read"; + public const string AppAssetsCreate = "squidex.apps.{app}.assets.create"; + public const string AppAssetsUpload = "squidex.apps.{app}.assets.upload"; + public const string AppAssetsUpdate = "squidex.apps.{app}.assets.update"; + public const string AppAssetsDelete = "squidex.apps.{app}.assets.delete"; + + // App Asset Folders + public const string AppAssetFolders = "squidex.apps.{app}.assets.folders"; + public const string AppAssetFoldersCreate = "squidex.apps.{app}.assets.folders.create"; + public const string AppAssetFoldersUpdate = "squidex.apps.{app}.assets.folders.update"; + public const string AppAssetFoldersDelete = "squidex.apps.{app}.assets.folders.delete"; + + // App Asset Scripts + public const string AppAssetScripts = "squidex.apps.{app}.asset-scripts"; + public const string AppAssetSScriptsRead = "squidex.apps.{app}.asset-scripts.read"; + public const string AppAssetsScriptsUpdate = "squidex.apps.{app}.asset-scripts.update"; + + // App Rules + public const string AppRules = "squidex.apps.{app}.rules"; + public const string AppRulesRead = "squidex.apps.{app}.rules.read"; + public const string AppRulesCreate = "squidex.apps.{app}.rules.create"; + public const string AppRulesUpdate = "squidex.apps.{app}.rules.update"; + public const string AppRulesDisable = "squidex.apps.{app}.rules.disable"; + public const string AppRulesDelete = "squidex.apps.{app}.rules.delete"; + + // App Rule Events + public const string AppRulesEvents = "squidex.apps.{app}.rules.events"; + public const string AppRulesEventsRun = "squidex.apps.{app}.rules.events.run"; + public const string AppRulesEventsRead = "squidex.apps.{app}.rules.events.read"; + public const string AppRulesEventsUpdate = "squidex.apps.{app}.rules.events.update"; + public const string AppRulesEventsDelete = "squidex.apps.{app}.rules.events.delete"; + + // App Schemas + public const string AppSchemas = "squidex.apps.{app}.schemas"; + public const string AppSchemasRead = "squidex.apps.{app}.schemas.read"; + public const string AppSchemasCreate = "squidex.apps.{app}.schemas.create"; + public const string AppSchemasUpdate = "squidex.apps.{app}.schemas.{schema}.update"; + public const string AppSchemasScripts = "squidex.apps.{app}.schemas.{schema}.scripts"; + public const string AppSchemasPublish = "squidex.apps.{app}.schemas.{schema}.publish"; + public const string AppSchemasDelete = "squidex.apps.{app}.schemas.{schema}.delete"; + + // App Contents + public const string AppContents = "squidex.apps.{app}.contents.{schema}"; + public const string AppContentsRead = "squidex.apps.{app}.contents.{schema}.read"; + public const string AppContentsReadOwn = "squidex.apps.{app}.contents.{schema}.read.own"; + public const string AppContentsCreate = "squidex.apps.{app}.contents.{schema}.create"; + public const string AppContentsUpdate = "squidex.apps.{app}.contents.{schema}.update"; + public const string AppContentsUpdateOwn = "squidex.apps.{app}.contents.{schema}.update.own"; + public const string AppContentsChangeStatusCancel = "squidex.apps.{app}.contents.{schema}.changestatus.cancel"; + public const string AppContentsChangeStatusCancelOwn = "squidex.apps.{app}.contents.{schema}.changestatus.cancel.own"; + public const string AppContentsChangeStatus = "squidex.apps.{app}.contents.{schema}.changestatus"; + public const string AppContentsChangeStatusOwn = "squidex.apps.{app}.contents.{schema}.changestatus.own"; + public const string AppContentsUpsert = "squidex.apps.{app}.contents.{schema}.upsert"; + public const string AppContentsVersionCreate = "squidex.apps.{app}.contents.{schema}.version.create"; + public const string AppContentsVersionCreateOwn = "squidex.apps.{app}.contents.{schema}.version.create.own"; + public const string AppContentsVersionDelete = "squidex.apps.{app}.contents.{schema}.version.delete"; + public const string AppContentsVersionDeleteOwn = "squidex.apps.{app}.contents.{schema}.version.delete.own"; + public const string AppContentsDelete = "squidex.apps.{app}.contents.{schema}.delete"; + public const string AppContentsDeleteOwn = "squidex.apps.{app}.contents.{schema}.delete.own"; + + public static Permission ForApp(string id, string app = Permission.Any, string schema = Permission.Any, string team = Permission.Any) { - public const string All = "squidex.*"; + Guard.NotNull(id); - public const string Admin = "squidex.admin.*"; - - // Admin App Creation - public const string AdminAppCreate = "squidex.admin.apps.create"; + id = id.Replace("{app}", app ?? Permission.Any, StringComparison.Ordinal); + id = id.Replace("{schema}", schema ?? Permission.Any, StringComparison.Ordinal); + id = id.Replace("{team}", team ?? Permission.Any, StringComparison.Ordinal); - // Admin Team Creation - public const string AdminTeamCreate = "squidex.admin.teams.create"; - - // Backup Admin - public const string AdminRestore = "squidex.admin.restore"; - - // Event Admin - public const string AdminEvents = "squidex.admin.events"; - public const string AdminEventsRead = "squidex.admin.events.read"; - public const string AdminEventsManage = "squidex.admin.events.manage"; - - // User Admin - public const string AdminUsers = "squidex.admin.users"; - public const string AdminUsersRead = "squidex.admin.users.read"; - public const string AdminUsersCreate = "squidex.admin.users.create"; - public const string AdminUsersUpdate = "squidex.admin.users.update"; - public const string AdminUsersUnlock = "squidex.admin.users.unlock"; - public const string AdminUsersLock = "squidex.admin.users.lock"; - - // Team - public const string Team = "squidex.teams.{team}"; - - // Team Transfer - public const string Transfer = "squidex.transfer"; - - // Team General - public const string TeamAdmin = "squidex.teams.{team}.*"; - public const string TeamUpdate = "squidex.teams.{team}.update"; - - // Team Contributors - public const string TeamContributors = "squidex.teams.{team}.contributors"; - public const string TeamContributorsRead = "squidex.teams.{team}.contributors.read"; - public const string TeamContributorsAssign = "squidex.teams.{team}.contributors.assign"; - public const string TeamContributorsRevoke = "squidex.teams.{team}.contributors.revoke"; - - // Team Plans - public const string TeamPlans = "squidex.teams.{team}.plans"; - public const string TeamPlansRead = "squidex.teams.{team}.plans.read"; - public const string TeamPlansChange = "squidex.teams.{team}.plans.change"; - - // Team Usage - public const string TeamUsage = "squidex.teams.{team}.usage"; - - // Team History - public const string TeamHistory = "squidex.teams.{team}.history"; - - // App - public const string App = "squidex.apps.{app}"; - - // App - public const string AppNoScripting = "squidex.apps.{app}.no-scripting"; - - // App General - public const string AppAdmin = "squidex.apps.{app}.*"; - public const string AppDelete = "squidex.apps.{app}.delete"; - public const string AppTransfer = "squidex.apps.{app}.transfer"; - public const string AppUpdate = "squidex.apps.{app}.update"; - public const string AppUpdateSettings = "squidex.apps.{app}.settings"; - - // App Image - public const string AppImageUpload = "squidex.apps.{app}.image"; - public const string AppImageDelete = "squidex.apps.{app}.image"; - - // App History - public const string AppHistory = "squidex.apps.{app}.history"; - - // App Ping - public const string AppPing = "squidex.apps.{app}.ping"; - - // App Search - public const string AppSearch = "squidex.apps.{app}.search"; - - // App Translate - public const string AppTranslate = "squidex.apps.{app}.translate"; - - // App Usage - public const string AppUsage = "squidex.apps.{app}.usage"; - - // App Comments - public const string AppComments = "squidex.apps.{app}.comments"; - public const string AppCommentsRead = "squidex.apps.{app}.comments.read"; - public const string AppCommentsCreate = "squidex.apps.{app}.comments.create"; - public const string AppCommentsUpdate = "squidex.apps.{app}.comments.update"; - public const string AppCommentsDelete = "squidex.apps.{app}.comments.delete"; - - // App Clients - public const string AppClients = "squidex.apps.{app}.clients"; - public const string AppClientsRead = "squidex.apps.{app}.clients.read"; - public const string AppClientsCreate = "squidex.apps.{app}.clients.create"; - public const string AppClientsUpdate = "squidex.apps.{app}.clients.update"; - public const string AppClientsDelete = "squidex.apps.{app}.clients.delete"; - - // App Contributors - public const string AppContributors = "squidex.apps.{app}.contributors"; - public const string AppContributorsRead = "squidex.apps.{app}.contributors.read"; - public const string AppContributorsAssign = "squidex.apps.{app}.contributors.assign"; - public const string AppContributorsRevoke = "squidex.apps.{app}.contributors.revoke"; - - // App Languages - public const string AppLanguages = "squidex.apps.{app}.languages"; - public const string AppLanguagesRead = "squidex.apps.{app}.languages.read"; - public const string AppLanguagesCreate = "squidex.apps.{app}.languages.create"; - public const string AppLanguagesUpdate = "squidex.apps.{app}.languages.update"; - public const string AppLanguagesDelete = "squidex.apps.{app}.languages.delete"; - - // App Roles - public const string AppRoles = "squidex.apps.{app}.roles"; - public const string AppRolesRead = "squidex.apps.{app}.roles.read"; - public const string AppRolesCreate = "squidex.apps.{app}.roles.create"; - public const string AppRolesUpdate = "squidex.apps.{app}.roles.update"; - public const string AppRolesDelete = "squidex.apps.{app}.roles.delete"; - - // App Workflows - public const string AppWorkflows = "squidex.apps.{app}.workflows"; - public const string AppWorkflowsRead = "squidex.apps.{app}.workflows.read"; - public const string AppWorkflowsCreate = "squidex.apps.{app}.workflows.create"; - public const string AppWorkflowsUpdate = "squidex.apps.{app}.workflows.update"; - public const string AppWorkflowsDelete = "squidex.apps.{app}.workflows.delete"; - - // App Backups - public const string AppBackups = "squidex.apps.{app}.backups"; - public const string AppBackupsRead = "squidex.apps.{app}.backups.read"; - public const string AppBackupsCreate = "squidex.apps.{app}.backups.create"; - public const string AppBackupsDelete = "squidex.apps.{app}.backups.delete"; - public const string AppBackupsDownload = "squidex.apps.{app}.backups.download"; - - // App Plans - public const string AppPlans = "squidex.apps.{app}.plans"; - public const string AppPlansRead = "squidex.apps.{app}.plans.read"; - public const string AppPlansChange = "squidex.apps.{app}.plans.change"; - - // App Assets - public const string AppAssets = "squidex.apps.{app}.assets"; - public const string AppAssetsRead = "squidex.apps.{app}.assets.read"; - public const string AppAssetsCreate = "squidex.apps.{app}.assets.create"; - public const string AppAssetsUpload = "squidex.apps.{app}.assets.upload"; - public const string AppAssetsUpdate = "squidex.apps.{app}.assets.update"; - public const string AppAssetsDelete = "squidex.apps.{app}.assets.delete"; - - // App Asset Folders - public const string AppAssetFolders = "squidex.apps.{app}.assets.folders"; - public const string AppAssetFoldersCreate = "squidex.apps.{app}.assets.folders.create"; - public const string AppAssetFoldersUpdate = "squidex.apps.{app}.assets.folders.update"; - public const string AppAssetFoldersDelete = "squidex.apps.{app}.assets.folders.delete"; - - // App Asset Scripts - public const string AppAssetScripts = "squidex.apps.{app}.asset-scripts"; - public const string AppAssetSScriptsRead = "squidex.apps.{app}.asset-scripts.read"; - public const string AppAssetsScriptsUpdate = "squidex.apps.{app}.asset-scripts.update"; - - // App Rules - public const string AppRules = "squidex.apps.{app}.rules"; - public const string AppRulesRead = "squidex.apps.{app}.rules.read"; - public const string AppRulesCreate = "squidex.apps.{app}.rules.create"; - public const string AppRulesUpdate = "squidex.apps.{app}.rules.update"; - public const string AppRulesDisable = "squidex.apps.{app}.rules.disable"; - public const string AppRulesDelete = "squidex.apps.{app}.rules.delete"; - - // App Rule Events - public const string AppRulesEvents = "squidex.apps.{app}.rules.events"; - public const string AppRulesEventsRun = "squidex.apps.{app}.rules.events.run"; - public const string AppRulesEventsRead = "squidex.apps.{app}.rules.events.read"; - public const string AppRulesEventsUpdate = "squidex.apps.{app}.rules.events.update"; - public const string AppRulesEventsDelete = "squidex.apps.{app}.rules.events.delete"; - - // App Schemas - public const string AppSchemas = "squidex.apps.{app}.schemas"; - public const string AppSchemasRead = "squidex.apps.{app}.schemas.read"; - public const string AppSchemasCreate = "squidex.apps.{app}.schemas.create"; - public const string AppSchemasUpdate = "squidex.apps.{app}.schemas.{schema}.update"; - public const string AppSchemasScripts = "squidex.apps.{app}.schemas.{schema}.scripts"; - public const string AppSchemasPublish = "squidex.apps.{app}.schemas.{schema}.publish"; - public const string AppSchemasDelete = "squidex.apps.{app}.schemas.{schema}.delete"; - - // App Contents - public const string AppContents = "squidex.apps.{app}.contents.{schema}"; - public const string AppContentsRead = "squidex.apps.{app}.contents.{schema}.read"; - public const string AppContentsReadOwn = "squidex.apps.{app}.contents.{schema}.read.own"; - public const string AppContentsCreate = "squidex.apps.{app}.contents.{schema}.create"; - public const string AppContentsUpdate = "squidex.apps.{app}.contents.{schema}.update"; - public const string AppContentsUpdateOwn = "squidex.apps.{app}.contents.{schema}.update.own"; - public const string AppContentsChangeStatusCancel = "squidex.apps.{app}.contents.{schema}.changestatus.cancel"; - public const string AppContentsChangeStatusCancelOwn = "squidex.apps.{app}.contents.{schema}.changestatus.cancel.own"; - public const string AppContentsChangeStatus = "squidex.apps.{app}.contents.{schema}.changestatus"; - public const string AppContentsChangeStatusOwn = "squidex.apps.{app}.contents.{schema}.changestatus.own"; - public const string AppContentsUpsert = "squidex.apps.{app}.contents.{schema}.upsert"; - public const string AppContentsVersionCreate = "squidex.apps.{app}.contents.{schema}.version.create"; - public const string AppContentsVersionCreateOwn = "squidex.apps.{app}.contents.{schema}.version.create.own"; - public const string AppContentsVersionDelete = "squidex.apps.{app}.contents.{schema}.version.delete"; - public const string AppContentsVersionDeleteOwn = "squidex.apps.{app}.contents.{schema}.version.delete.own"; - public const string AppContentsDelete = "squidex.apps.{app}.contents.{schema}.delete"; - public const string AppContentsDeleteOwn = "squidex.apps.{app}.contents.{schema}.delete.own"; - - public static Permission ForApp(string id, string app = Permission.Any, string schema = Permission.Any, string team = Permission.Any) - { - Guard.NotNull(id); - - id = id.Replace("{app}", app ?? Permission.Any, StringComparison.Ordinal); - id = id.Replace("{schema}", schema ?? Permission.Any, StringComparison.Ordinal); - id = id.Replace("{team}", team ?? Permission.Any, StringComparison.Ordinal); - - return new Permission(id); - } + return new Permission(id); } } diff --git a/backend/src/Squidex.Shared/Squidex.Shared.csproj b/backend/src/Squidex.Shared/Squidex.Shared.csproj index cc282e17a..50018b35d 100644 --- a/backend/src/Squidex.Shared/Squidex.Shared.csproj +++ b/backend/src/Squidex.Shared/Squidex.Shared.csproj @@ -1,7 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>9.0</LangVersion> + <LangVersion>latest</LangVersion> + <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> diff --git a/backend/src/Squidex.Shared/Texts.cs b/backend/src/Squidex.Shared/Texts.cs index 06d13d5ab..c0ceb20a6 100644 --- a/backend/src/Squidex.Shared/Texts.cs +++ b/backend/src/Squidex.Shared/Texts.cs @@ -7,15 +7,14 @@ using System.Resources; -namespace Squidex.Shared +namespace Squidex.Shared; + +public static class Texts { - public static class Texts - { - private static ResourceManager? resourceManager; + private static ResourceManager? resourceManager; - public static ResourceManager ResourceManager - { - get => resourceManager ??= new ResourceManager("Squidex.Shared.Texts", typeof(Texts).Assembly); - } + public static ResourceManager ResourceManager + { + get => resourceManager ??= new ResourceManager("Squidex.Shared.Texts", typeof(Texts).Assembly); } } diff --git a/backend/src/Squidex.Shared/Users/ClientUser.cs b/backend/src/Squidex.Shared/Users/ClientUser.cs index 10d840253..76f145b04 100644 --- a/backend/src/Squidex.Shared/Users/ClientUser.cs +++ b/backend/src/Squidex.Shared/Users/ClientUser.cs @@ -12,46 +12,45 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Security; using Squidex.Shared.Identity; -namespace Squidex.Shared.Users +namespace Squidex.Shared.Users; + +public sealed class ClientUser : IUser { - public sealed class ClientUser : IUser - { - private readonly RefToken token; - private readonly List<Claim> claims; + private readonly RefToken token; + private readonly List<Claim> claims; - public string Id - { - get => token.Identifier; - } + public string Id + { + get => token.Identifier; + } - public string Email - { - get => token.ToString(); - } + public string Email + { + get => token.ToString(); + } - public bool IsLocked - { - get => false; - } + public bool IsLocked + { + get => false; + } - public IReadOnlyList<Claim> Claims - { - get => claims; - } + public IReadOnlyList<Claim> Claims + { + get => claims; + } - public object Identity => throw new NotSupportedException(); + public object Identity => throw new NotSupportedException(); - public ClientUser(RefToken token) - { - Guard.NotNull(token); + public ClientUser(RefToken token) + { + Guard.NotNull(token); - this.token = token; + this.token = token; - claims = new List<Claim> - { - new Claim(OpenIdClaims.ClientId, token.Identifier), - new Claim(SquidexClaimTypes.DisplayName, token.Identifier) - }; - } + claims = new List<Claim> + { + new Claim(OpenIdClaims.ClientId, token.Identifier), + new Claim(SquidexClaimTypes.DisplayName, token.Identifier) + }; } } diff --git a/backend/src/Squidex.Shared/Users/IUser.cs b/backend/src/Squidex.Shared/Users/IUser.cs index 45305382e..3b7822470 100644 --- a/backend/src/Squidex.Shared/Users/IUser.cs +++ b/backend/src/Squidex.Shared/Users/IUser.cs @@ -8,18 +8,17 @@ using System.Collections.Generic; using System.Security.Claims; -namespace Squidex.Shared.Users +namespace Squidex.Shared.Users; + +public interface IUser { - public interface IUser - { - bool IsLocked { get; } + bool IsLocked { get; } - string Id { get; } + string Id { get; } - string Email { get; } + string Email { get; } - object Identity { get; } + object Identity { get; } - IReadOnlyList<Claim> Claims { get; } - } + IReadOnlyList<Claim> Claims { get; } } diff --git a/backend/src/Squidex.Shared/Users/IUserResolver.cs b/backend/src/Squidex.Shared/Users/IUserResolver.cs index 31f679844..73ba66209 100644 --- a/backend/src/Squidex.Shared/Users/IUserResolver.cs +++ b/backend/src/Squidex.Shared/Users/IUserResolver.cs @@ -9,29 +9,28 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace Squidex.Shared.Users +namespace Squidex.Shared.Users; + +public interface IUserResolver { - public interface IUserResolver - { - Task<(IUser? User, bool Created)> CreateUserIfNotExistsAsync(string email, bool invited = false, - CancellationToken ct = default); + Task<(IUser? User, bool Created)> CreateUserIfNotExistsAsync(string email, bool invited = false, + CancellationToken ct = default); - Task SetClaimAsync(string id, string type, string value, bool silent = false, - CancellationToken ct = default); + Task SetClaimAsync(string id, string type, string value, bool silent = false, + CancellationToken ct = default); - Task<IUser?> FindByIdOrEmailAsync(string idOrEmail, - CancellationToken ct = default); + Task<IUser?> FindByIdOrEmailAsync(string idOrEmail, + CancellationToken ct = default); - Task<IUser?> FindByIdAsync(string idOrEmail, - CancellationToken ct = default); + Task<IUser?> FindByIdAsync(string idOrEmail, + CancellationToken ct = default); - Task<List<IUser>> QueryByEmailAsync(string email, - CancellationToken ct = default); + Task<List<IUser>> QueryByEmailAsync(string email, + CancellationToken ct = default); - Task<List<IUser>> QueryAllAsync( - CancellationToken ct = default); + Task<List<IUser>> QueryAllAsync( + CancellationToken ct = default); - Task<Dictionary<string, IUser>> QueryManyAsync(string[] ids, - CancellationToken ct = default); - } + Task<Dictionary<string, IUser>> QueryManyAsync(string[] ids, + CancellationToken ct = default); } diff --git a/backend/src/Squidex.Web/IgnoreHashFileProvider.cs b/backend/src/Squidex.Web/IgnoreHashFileProvider.cs index 8f08a059b..e1e8d4ad7 100644 --- a/backend/src/Squidex.Web/IgnoreHashFileProvider.cs +++ b/backend/src/Squidex.Web/IgnoreHashFileProvider.cs @@ -11,7 +11,7 @@ using Microsoft.Extensions.Primitives; namespace Squidex.Web; -public sealed class IgnoreHashFileProvider : IFileProvider +public sealed partial class IgnoreHashFileProvider : IFileProvider { private readonly char[] pathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, '\\' }; private readonly Dictionary<string, string> map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); @@ -21,7 +21,7 @@ public sealed class IgnoreHashFileProvider : IFileProvider { this.inner = inner; - var regex = new Regex("^(?<Name>[^.]+)\\.[0-9a-f]{4,}\\.(?<Extension>.+)$"); + var regex = BuildFileWithHashRegex(); void MapDirectory(string path) { @@ -91,4 +91,7 @@ public sealed class IgnoreHashFileProvider : IFileProvider return $"{path1}/{path2}"; } + + [GeneratedRegex("^(?<Name>[^.]+)(\\.|-)[0-9A-Za-z]{4,}\\.(?<Extension>.+)$")] + private static partial Regex BuildFileWithHashRegex(); } diff --git a/backend/src/Squidex.Web/Squidex.Web.csproj b/backend/src/Squidex.Web/Squidex.Web.csproj index 8ae350cb9..466ec63fb 100644 --- a/backend/src/Squidex.Web/Squidex.Web.csproj +++ b/backend/src/Squidex.Web/Squidex.Web.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 55b82d17d..bb52dc590 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -3,7 +3,7 @@ <TargetFramework>net7.0</TargetFramework> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <NeutralLanguage>en</NeutralLanguage> <NoWarn>NU1608</NoWarn> diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index fad96e852..f7e714d28 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -3,7 +3,7 @@ <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Domain.Apps.Core</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index 21f5e6939..ab58226c5 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -3,7 +3,7 @@ <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Domain.Apps.Entities</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <NeutralLanguage>en</NeutralLanguage> diff --git a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj index 4e0261ee2..5a1e6d530 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj +++ b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj @@ -3,7 +3,7 @@ <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Domain.Users</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index 6a81fbfc9..31ae26342 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -3,7 +3,7 @@ <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Infrastructure</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <NeutralLanguage>en</NeutralLanguage> diff --git a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj index 2c8d82ea6..8d0f198a4 100644 --- a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj +++ b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj @@ -3,7 +3,7 @@ <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <RootNamespace>Squidex.Web</RootNamespace> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> diff --git a/backend/tools/GenerateLanguages/GenerateLanguages.csproj b/backend/tools/GenerateLanguages/GenerateLanguages.csproj index 46f5dbfe3..078cbdd9f 100644 --- a/backend/tools/GenerateLanguages/GenerateLanguages.csproj +++ b/backend/tools/GenerateLanguages/GenerateLanguages.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> - <LangVersion>11.0</LangVersion> + <LangVersion>latest</LangVersion> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> diff --git a/frontend/.storybook/main.js b/frontend/.storybook/main.js index e6876b586..479553f12 100644 --- a/frontend/.storybook/main.js +++ b/frontend/.storybook/main.js @@ -5,6 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +const CopyPlugin = require('copy-webpack-plugin'); + module.exports = { stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ @@ -16,6 +18,17 @@ module.exports = { name: "@storybook/angular", options: {} }, + webpackFinal: async config => { + /* + * Copy lazy loaded libraries to output. + */ + config.plugins.push(new CopyPlugin({ + patterns: [ + { from: './node_modules/ace-builds/src-min/', to: 'dependencies/ace/' }, + ] + })); + return config; + }, docs: { autodocs: true } diff --git a/frontend/angular.json b/frontend/angular.json index c43ddff75..aa2893676 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -18,11 +18,11 @@ "prefix": "sqx", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:application", "options": { "outputPath": "build", "index": "src/index.html", - "main": "src/main.ts", + "browser": "src/main.ts", "polyfills": [ "zone.js" ], @@ -30,6 +30,7 @@ "inlineStyleLanguage": "scss", "allowedCommonJsDependencies": [ "@tweenjs/tween.js", + "client-only", "copy-to-clipboard", "cropperjs", "crypto-js", @@ -37,12 +38,16 @@ "crypto-js/enc-base64.js", "crypto-js/enc-utf8.js", "crypto-js/sha256.js", - "mousetrap", + "graphql-ws", + "markdown-it", "mersenne-twister", + "mousetrap", "nullthrows", "pikaday", "pikaday/pikaday", "progressbar.js", + "react-dom", + "react", "set-value", "slugify" ], @@ -117,12 +122,10 @@ "outputHashing": "all" }, "development": { - "buildOptimizer": false, "extractLicenses": false, "namedChunks": true, "optimization": false, - "sourceMap": true, - "vendorChunk": true + "sourceMap": true } }, "defaultConfiguration": "production" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1d7a8f3ac..2bce567b9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "squidex", "version": "0.0.0", "dependencies": { + "@angular-devkit/architect": "^0.1700.0", "@angular/animations": "17.0.2", "@angular/cdk": "17.0.0", "@angular/cdk-experimental": "17.0.0", @@ -30,6 +31,7 @@ "angular-gridster2": "16.0.0", "angular-mentions": "1.5.0", "bootstrap": "5.2.3", + "copy-webpack-plugin": "^11.0.0", "core-js": "3.33.2", "cropperjs": "2.0.0-alpha.1", "date-fns": "2.30.0", @@ -148,59 +150,19 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1602.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.10.tgz", - "integrity": "sha512-FwemQXh3edqA/S6zPpsqKei5v7gt0R0WpjJoAJaz+FOpfDwij1fwnKr88XINY8xcefTcQaTDQxJZheJShA/hHw==", - "dev": true, - "peer": true, + "version": "0.1700.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.0.tgz", + "integrity": "sha512-whi7HvOjv1J3He9f+H8xNJWKyjAmWuWNl8gxNW6EZP/XLcrOu+/5QT4bPtXQBRIL/avZuc++5sNQS+kReaNCig==", "dependencies": { - "@angular-devkit/core": "16.2.10", + "@angular-devkit/core": "17.0.0", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/architect/node_modules/@angular-devkit/core": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.10.tgz", - "integrity": "sha512-eo7suLDjyu5bSlEr4TluYkFm4v2PVLSAPgnau8XHHlN5Yg4P/BZ00ve7LA7C9S1gzRSCrxQhK5ki4rnoFTo5zg==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "picomatch": "2.3.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^16.14.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/architect/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@angular-devkit/build-angular": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.0.tgz", @@ -324,48 +286,6 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1700.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.0.tgz", - "integrity": "sha512-whi7HvOjv1J3He9f+H8xNJWKyjAmWuWNl8gxNW6EZP/XLcrOu+/5QT4bPtXQBRIL/avZuc++5sNQS+kReaNCig==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.0.0", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.0.tgz", - "integrity": "sha512-QUu3LnEi4A8t733v2+I0sLtyBJx3Q7zdTAhaauCbxbFhDid0cbYm8hYsyG/njor1irTPxSJbn6UoetVkwUQZxg==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "picomatch": "3.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", @@ -1174,15 +1094,6 @@ "ajv": "^6.9.1" } }, - "node_modules/@angular-devkit/build-angular/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/terser": { "version": "5.24.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", @@ -1725,26 +1636,10 @@ "webpack-dev-server": "^4.0.0" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1700.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.0.tgz", - "integrity": "sha512-whi7HvOjv1J3He9f+H8xNJWKyjAmWuWNl8gxNW6EZP/XLcrOu+/5QT4bPtXQBRIL/avZuc++5sNQS+kReaNCig==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.0.0", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { + "node_modules/@angular-devkit/core": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.0.tgz", "integrity": "sha512-QUu3LnEi4A8t733v2+I0sLtyBJx3Q7zdTAhaauCbxbFhDid0cbYm8hYsyG/njor1irTPxSJbn6UoetVkwUQZxg==", - "dev": true, "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", @@ -1767,11 +1662,10 @@ } } }, - "node_modules/@angular-devkit/build-webpack/node_modules/picomatch": { + "node_modules/@angular-devkit/core/node_modules/picomatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "dev": true, "engines": { "node": ">=10" }, @@ -1779,47 +1673,9 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/core": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.1.7.tgz", - "integrity": "sha512-AXc9/F57Nf/A26yGu+w7PhNYriTvwazPTQsVPW/SBcTcpBa/hAsBTbPl8o8ErRJneJIoYqy/EIuabf9iiU8bRA==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^16.14.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@angular-devkit/core/node_modules/source-map": { "version": "0.7.4", - "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">= 8" } @@ -2443,48 +2299,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.1700.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.0.tgz", - "integrity": "sha512-whi7HvOjv1J3He9f+H8xNJWKyjAmWuWNl8gxNW6EZP/XLcrOu+/5QT4bPtXQBRIL/avZuc++5sNQS+kReaNCig==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.0.0", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.0.tgz", - "integrity": "sha512-QUu3LnEi4A8t733v2+I0sLtyBJx3Q7zdTAhaauCbxbFhDid0cbYm8hYsyG/njor1irTPxSJbn6UoetVkwUQZxg==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "picomatch": "3.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.0.tgz", @@ -2538,27 +2352,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular/cli/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@angular/cli/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@angular/common": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.2.tgz", @@ -6672,7 +6465,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -8451,33 +8243,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.0.tgz", - "integrity": "sha512-QUu3LnEi4A8t733v2+I0sLtyBJx3Q7zdTAhaauCbxbFhDid0cbYm8hYsyG/njor1irTPxSJbn6UoetVkwUQZxg==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "picomatch": "3.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.0.tgz", @@ -8514,27 +8279,6 @@ "node": ">=12" } }, - "node_modules/@schematics/angular/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@schematics/angular/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@sigstore/bundle": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.0.tgz", @@ -11066,7 +10810,6 @@ "version": "8.44.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", - "dev": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -11074,7 +10817,6 @@ }, "node_modules/@types/eslint-scope": { "version": "3.7.4", - "dev": true, "license": "MIT", "dependencies": { "@types/eslint": "*", @@ -11179,8 +10921,7 @@ "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -11240,7 +10981,6 @@ "version": "20.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "devOptional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -11997,7 +11737,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -12006,26 +11745,22 @@ "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -12035,14 +11770,12 @@ "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -12054,7 +11787,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -12063,7 +11795,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -12071,14 +11802,12 @@ "node_modules/@webassemblyjs/utf8": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -12094,7 +11823,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -12107,7 +11835,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -12119,7 +11846,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -12133,7 +11859,6 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" @@ -12156,14 +11881,12 @@ "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/@yarnpkg/esbuild-plugin-pnp": { "version": "3.0.0-rc.15", @@ -12306,7 +12029,6 @@ "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -12318,7 +12040,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, "peerDependencies": { "acorn": "^8" } @@ -12439,7 +12160,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -12453,7 +12173,6 @@ }, "node_modules/ajv-formats": { "version": "2.1.1", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -12469,7 +12188,6 @@ }, "node_modules/ajv-keywords": { "version": "5.1.0", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" @@ -13964,7 +13682,6 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "dev": true, "license": "MIT" }, "node_modules/builtins": { @@ -14451,7 +14168,6 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0" @@ -14686,7 +14402,6 @@ }, "node_modules/commander": { "version": "2.20.3", - "dev": true, "license": "MIT" }, "node_modules/common-path-prefix": { @@ -14926,8 +14641,8 @@ }, "node_modules/copy-webpack-plugin": { "version": "11.0.0", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "dependencies": { "fast-glob": "^3.2.11", "glob-parent": "^6.0.1", @@ -14949,7 +14664,6 @@ }, "node_modules/copy-webpack-plugin/node_modules/glob-parent": { "version": "6.0.2", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -14960,7 +14674,6 @@ }, "node_modules/copy-webpack-plugin/node_modules/globby": { "version": "13.1.2", - "dev": true, "license": "MIT", "dependencies": { "dir-glob": "^3.0.1", @@ -14978,7 +14691,6 @@ }, "node_modules/copy-webpack-plugin/node_modules/slash": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -15782,7 +15494,6 @@ }, "node_modules/dir-glob": { "version": "3.0.1", - "dev": true, "license": "MIT", "dependencies": { "path-type": "^4.0.0" @@ -16302,7 +16013,6 @@ "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -16486,8 +16196,7 @@ "node_modules/es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==" }, "node_modules/es-set-tostringtag": { "version": "2.0.1", @@ -17119,7 +16828,6 @@ }, "node_modules/eslint-scope": { "version": "5.1.1", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -17424,7 +17132,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -17435,7 +17142,6 @@ }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -17443,7 +17149,6 @@ }, "node_modules/estraverse": { "version": "4.3.0", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -17508,7 +17213,6 @@ }, "node_modules/events": { "version": "3.3.0", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -17773,14 +17477,12 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -17794,7 +17496,6 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -18626,7 +18327,6 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "dev": true, "license": "BSD-2-Clause" }, "node_modules/global": { @@ -18726,7 +18426,6 @@ }, "node_modules/graceful-fs": { "version": "4.2.10", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -19321,7 +19020,6 @@ "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, "engines": { "node": ">= 4" } @@ -20726,7 +20424,6 @@ }, "node_modules/jest-worker": { "version": "27.4.5", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -20739,7 +20436,6 @@ }, "node_modules/jest-worker/node_modules/has-flag": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -20747,7 +20443,6 @@ }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -20952,12 +20647,10 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -20979,8 +20672,7 @@ "node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" }, "node_modules/jsonfile": { "version": "6.1.0", @@ -21586,7 +21278,6 @@ }, "node_modules/loader-runner": { "version": "4.2.0", - "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" @@ -22299,7 +21990,6 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -22361,7 +22051,6 @@ }, "node_modules/mime-db": { "version": "1.51.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -22369,7 +22058,6 @@ }, "node_modules/mime-types": { "version": "2.1.34", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.51.0" @@ -22815,7 +22503,6 @@ }, "node_modules/neo-async": { "version": "2.6.2", - "dev": true, "license": "MIT" }, "node_modules/next-tick": { @@ -24325,7 +24012,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -25013,7 +24699,6 @@ }, "node_modules/punycode": { "version": "2.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -25161,7 +24846,6 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -25752,7 +25436,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -26100,7 +25783,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -26247,7 +25929,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, "dependencies": { "randombytes": "^2.1.0" } @@ -26706,7 +26387,6 @@ }, "node_modules/source-map-support": { "version": "0.5.21", - "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -26715,7 +26395,6 @@ }, "node_modules/source-map-support/node_modules/source-map": { "version": "0.6.1", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -27680,7 +27359,6 @@ }, "node_modules/tapable": { "version": "2.2.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -27823,7 +27501,6 @@ "version": "5.19.2", "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", - "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -27841,7 +27518,6 @@ "version": "5.3.9", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", @@ -27873,7 +27549,6 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv": { "version": "6.12.6", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -27888,7 +27563,6 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { "version": "3.5.2", - "dev": true, "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" @@ -27896,12 +27570,10 @@ }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", - "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", @@ -28509,8 +28181,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -28727,7 +28398,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -29077,7 +28747,6 @@ }, "node_modules/watchpack": { "version": "2.4.0", - "dev": true, "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -29114,7 +28783,6 @@ "version": "5.88.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -29289,7 +28957,6 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", - "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -29326,7 +28993,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -29342,7 +29008,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -29350,14 +29015,12 @@ "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", diff --git a/frontend/package.json b/frontend/package.json index 1ea393d67..626d25e88 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ }, "private": true, "dependencies": { + "@angular-devkit/architect": "^0.1700.0", "@angular/animations": "17.0.2", "@angular/cdk": "17.0.0", "@angular/cdk-experimental": "17.0.0", @@ -37,6 +38,7 @@ "angular-gridster2": "16.0.0", "angular-mentions": "1.5.0", "bootstrap": "5.2.3", + "copy-webpack-plugin": "^11.0.0", "core-js": "3.33.2", "cropperjs": "2.0.0-alpha.1", "date-fns": "2.30.0", diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 6f01c9463..e9bbdbda0 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -5,13 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component } from '@angular/core'; +import { NgIf } from '@angular/common'; +import { Component, Injector } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { AnalyticsService, CopyGlobalDirective, DialogRendererComponent, RootViewComponent, TourGuideComponent, TourTemplateComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-app', styleUrls: ['./app.component.scss'], templateUrl: './app.component.html', + imports: [ + CopyGlobalDirective, + DialogRendererComponent, + NgIf, + RootViewComponent, + RouterOutlet, + TourGuideComponent, + TourTemplateComponent, + TranslatePipe, + ], }) export class AppComponent { public isLoaded?: boolean | null; + + constructor(injector: Injector) { + injector.get(AnalyticsService); + } } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts deleted file mode 100644 index f5d369044..000000000 --- a/frontend/src/app/app.module.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -/* eslint-disable global-require */ -/* eslint-disable import/no-dynamic-require */ - -import { APP_BASE_HREF, CommonModule } from '@angular/common'; -import { HttpClientModule } from '@angular/common/http'; -import { ApplicationRef, DoBootstrap, NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ActivatedRouteSnapshot, BaseRouteReuseStrategy, RouteReuseStrategy, RouterModule } from '@angular/router'; -import { NgChartsModule } from 'ng2-charts'; -import { environment } from './../environments/environment'; -import { AppComponent } from './app.component'; -import { routing } from './app.routes'; -import { ApiUrlConfig, DateHelper, LocalizerService, SqxFrameworkModule, SqxSharedModule, TitlesConfig, UIOptions } from './shared'; -import { SqxShellModule } from './shell'; - -const options = (window as any)['options'] || {}; - -DateHelper.setlocale(options.more?.culture); - -function basePath() { - const baseElements = document.getElementsByTagName('base'); - - let baseHref: string = null!; - - if (baseElements.length > 0) { - baseHref = baseElements[0].href; - } - - if (baseHref.indexOf('http') === 0) { - baseHref = new URL(baseHref).pathname; - } - - if (!baseHref) { - baseHref = ''; - } - - let path = options.embedPath || '/'; - - while (baseHref.endsWith('/')) { - baseHref = baseHref.substring(0, baseHref.length - 1); - } - - return `${baseHref}${path}`; -} - -function configApiUrl() { - const baseElements = document.getElementsByTagName('base'); - - let baseHref: string = null!; - - if (baseElements.length > 0) { - baseHref = baseElements[0].href; - } - - if (!baseHref) { - baseHref = '/'; - } - - if (baseHref.indexOf('http') === 0) { - return new ApiUrlConfig(baseHref); - } else { - return new ApiUrlConfig(`${window.location.protocol}//${window.location.host}${baseHref}`); - } -} - -function configUIOptions() { - return new UIOptions(options); -} - -function configTitles() { - return new TitlesConfig(undefined, 'i18n:common.product'); -} - -function configLocalizerService() { - return new LocalizerService(environment.textResolver()).logMissingKeys(environment.textLogger); -} - -export class AppRouteReuseStrategy extends BaseRouteReuseStrategy { - public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) { - return (future.routeConfig === curr.routeConfig) || (future.data['reuseId'] && future.data['reuseId'] === curr.data['reuseId']); - } -} - -@NgModule({ - imports: [ - BrowserAnimationsModule, - BrowserModule, - CommonModule, - FormsModule, - HttpClientModule, - NgChartsModule.forRoot(), - ReactiveFormsModule, - RouterModule, - SqxFrameworkModule.forRoot(), - SqxSharedModule.forRoot(), - SqxShellModule, - routing, - ], - declarations: [ - AppComponent, - ], - providers: [ - { provide: ApiUrlConfig, useFactory: configApiUrl }, - { provide: LocalizerService, useFactory: configLocalizerService }, - { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, - { provide: TitlesConfig, useFactory: configTitles }, - { provide: UIOptions, useFactory: configUIOptions }, - { provide: APP_BASE_HREF, useValue: basePath() }, - ], -}) -export class AppModule implements DoBootstrap { - public ngDoBootstrap(appRef: ApplicationRef) { - try { - appRef.bootstrap(AppComponent); - } catch (e) { - // eslint-disable-next-line no-console - console.log('Application element not found.'); - } - } -} diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 6142e77e4..7f0664a56 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -5,76 +5,75 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ModuleWithProviders } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { AppMustExistGuard, LoadAppsGuard, LoadSettingsGuard, LoadTeamsGuard, MustBeAuthenticatedGuard, MustBeNotAuthenticatedGuard, TeamMustExistGuard, UnsetAppGuard, UnsetTeamGuard } from './shared'; +import { Routes } from '@angular/router'; +import { appMustExistGuard, loadAppsGuard, loadSettingsGuard, loadTeamsGuard, mustBeAuthenticatedGuard, mustBeNotAuthenticatedGuard, teamMustExistGuard, unsetAppGuard, unsetTeamGuard } from './shared'; import { AppAreaComponent, ForbiddenPageComponent, HomePageComponent, InternalAreaComponent, LoginPageComponent, LogoutPageComponent, NotFoundPageComponent, TeamsAreaComponent } from './shell'; -const routes: Routes = [ +export const APP_ROUTES: Routes = [ { path: '', component: HomePageComponent, - canActivate: [MustBeNotAuthenticatedGuard], + canActivate: [mustBeNotAuthenticatedGuard], }, { path: 'app', component: InternalAreaComponent, - canActivate: [MustBeAuthenticatedGuard, LoadAppsGuard, LoadTeamsGuard, LoadSettingsGuard], + canActivate: [mustBeAuthenticatedGuard, loadAppsGuard, loadTeamsGuard, loadSettingsGuard], children: [ { path: '', - loadChildren: () => import('./features/apps/module').then(m => m.SqxFeatureAppsModule), - canActivate: [UnsetAppGuard, UnsetTeamGuard], + loadChildren: () => import('./features/apps/routes').then(m => m.APPS_ROUTES), + canActivate: [unsetAppGuard, unsetTeamGuard], }, { path: 'administration', - loadChildren: () => import('./features/administration/module').then(m => m.SqxFeatureAdministrationModule), - canActivate: [UnsetAppGuard, UnsetTeamGuard], + loadChildren: () => import('./features/administration/routes').then(m => m.ADMINISTRATION_ROUTES), + canActivate: [unsetAppGuard, unsetTeamGuard], }, { path: 'teams', component: TeamsAreaComponent, - canActivate: [UnsetAppGuard, UnsetTeamGuard], + canActivate: [unsetAppGuard, unsetTeamGuard], children: [ { path: ':teamName', - canActivate: [TeamMustExistGuard], - loadChildren: () => import('./features/teams/module').then(m => m.SqxFeatureTeamsModule), + canActivate: [teamMustExistGuard], + loadChildren: () => import('./features/teams/routes').then(m => m.TEAM_ROUTES), }, ], }, { path: ':appName', component: AppAreaComponent, - canActivate: [AppMustExistGuard], + canActivate: [appMustExistGuard], children: [ { path: '', - loadChildren: () => import('./features/dashboard/module').then(m => m.SqxFeatureDashboardModule), + loadChildren: () => import('./features/dashboard/routes').then(m => m.DASHBOARD_ROUTES), }, { path: 'content', - loadChildren: () => import('./features/content/module').then(m => m.SqxFeatureContentModule), + loadChildren: () => import('./features/content/routes').then(m => m.CONTENT_ROUTES), }, { path: 'schemas', - loadChildren: () => import('./features/schemas/module').then(m => m.SqxFeatureSchemasModule), + loadChildren: () => import('./features/schemas/routes').then(m => m.SCHEMAS_ROUTES), }, { path: 'assets', - loadChildren: () => import('./features/assets/module').then(m => m.SqxFeatureAssetsModule), + loadChildren: () => import('./features/assets/routes').then(m => m.ASSETS_ROUTES), }, { path: 'rules', - loadChildren: () => import('./features/rules/module').then(m => m.SqxFeatureRulesModule), + loadChildren: () => import('./features/rules/routes').then(m => m.RULES_ROUTES), }, { path: 'settings', - loadChildren: () => import('./features/settings/module').then(m => m.SqxFeatureSettingsModule), + loadChildren: () => import('./features/settings/routes').then(m => m.SETTINGS_ROUTES), }, { path: 'api', - loadChildren: () => import('./features/api/module').then(m => m.SqxFeatureApiModule), + loadChildren: () => import('./features/api/routes').then(m => m.API_ROUTES), }, ], }, @@ -97,5 +96,3 @@ const routes: Routes = [ component: NotFoundPageComponent, }, ]; - -export const routing: ModuleWithProviders<RouterModule> = RouterModule.forRoot(routes, { useHash: false }); diff --git a/frontend/src/app/assets/squid.svg b/frontend/src/app/assets/squid.svg index ec5cd7d1f..d2836ceb5 100644 --- a/frontend/src/app/assets/squid.svg +++ b/frontend/src/app/assets/squid.svg @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg - id="Layer_1" - viewBox="0 0 204.02457 346.98514" - version="1.1" - sodipodi:docname="squid.svg" - width="204.02457" - height="346.98514" - inkscape:version="1.3 (0e150ed6c4, 2023-07-21)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg"> + id="Layer_1" + viewBox="0 0 204.02457 346.98514" + version="1.1" + sodipodi:docname="squid.svg" + width="204.02457" + height="346.98514" + inkscape:version="1.3 (0e150ed6c4, 2023-07-21)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" +> <defs id="defs13" /> <sodipodi:namedview diff --git a/frontend/src/app/features/administration/administration-area.component.ts b/frontend/src/app/features/administration/administration-area.component.ts index bbfc8eb29..af52b46d1 100644 --- a/frontend/src/app/features/administration/administration-area.component.ts +++ b/frontend/src/app/features/administration/administration-area.component.ts @@ -5,13 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; -import { UIState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { LayoutContainerDirective, TitleComponent, TranslatePipe, UIState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-administration-area', styleUrls: ['./administration-area.component.scss'], templateUrl: './administration-area.component.html', + imports: [ + AsyncPipe, + LayoutContainerDirective, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + TitleComponent, + TranslatePipe, + ], }) export class AdministrationAreaComponent { constructor( diff --git a/frontend/src/app/features/administration/declarations.ts b/frontend/src/app/features/administration/declarations.ts deleted file mode 100644 index 992413f28..000000000 --- a/frontend/src/app/features/administration/declarations.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './administration-area.component'; -export * from './guards/user-must-exist.guard'; -export * from './internal'; -export * from './pages/event-consumers/event-consumer.component'; -export * from './pages/event-consumers/event-consumers-page.component'; -export * from './pages/restore/restore-page.component'; -export * from './pages/users/user-page.component'; -export * from './pages/users/user.component'; -export * from './pages/users/users-page.component'; diff --git a/frontend/src/app/features/administration/guards/user-must-exist.guard.spec.ts b/frontend/src/app/features/administration/guards/user-must-exist.guard.spec.ts index 592548b45..05a8fa644 100644 --- a/frontend/src/app/features/administration/guards/user-must-exist.guard.spec.ts +++ b/frontend/src/app/features/administration/guards/user-must-exist.guard.spec.ts @@ -5,24 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; -import { UserDto, UsersState } from '@app/features/administration/internal'; -import { UserMustExistGuard } from './user-must-exist.guard'; +import { UserDto, UsersState } from '../internal'; +import { userMustExistGuard } from './user-must-exist.guard'; describe('UserMustExistGuard', () => { - let usersState: IMock<UsersState>; let router: IMock<Router>; - let userGuard: UserMustExistGuard; + let usersState: IMock<UsersState>; beforeEach(() => { router = Mock.ofType<Router>(); usersState = Mock.ofType<UsersState>(); - userGuard = new UserMustExistGuard(usersState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: UsersState, + useValue: usersState.object, + }, + ], + }); }); - it('should load user and return true if found', async () => { + bit('should load user and return true if found', async () => { usersState.setup(x => x.select('123')) .returns(() => of(<UserDto>{})); @@ -32,14 +44,14 @@ describe('UserMustExistGuard', () => { }, }; - const result = await firstValueFrom(userGuard.canActivate(route)); + const result = await firstValueFrom(userMustExistGuard(route)); expect(result).toBeTruthy(); usersState.verify(x => x.select('123'), Times.once()); }); - it('should load user and return false if not found', async () => { + bit('should load user and return false if not found', async () => { usersState.setup(x => x.select('123')) .returns(() => of(null)); @@ -49,14 +61,14 @@ describe('UserMustExistGuard', () => { }, }; - const result = await firstValueFrom(userGuard.canActivate(route)); + const result = await firstValueFrom(userMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should unset user if user id is undefined', async () => { + bit('should unset user if user id is undefined', async () => { usersState.setup(x => x.select(null)) .returns(() => of(null)); @@ -66,14 +78,14 @@ describe('UserMustExistGuard', () => { }, }; - const result = await firstValueFrom(userGuard.canActivate(route)); + const result = await firstValueFrom(userMustExistGuard(route)); expect(result).toBeTruthy(); usersState.verify(x => x.select(null), Times.once()); }); - it('should unset user if user id is <new>', async () => { + bit('should unset user if user id is <new>', async () => { usersState.setup(x => x.select(null)) .returns(() => of(null)); @@ -83,10 +95,16 @@ describe('UserMustExistGuard', () => { }, }; - const result = await firstValueFrom(userGuard.canActivate(route)); + const result = await firstValueFrom(userMustExistGuard(route)); expect(result).toBeTruthy(); usersState.verify(x => x.select(null), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/features/administration/guards/user-must-exist.guard.ts b/frontend/src/app/features/administration/guards/user-must-exist.guard.ts index e2cf558d0..92be2fb51 100644 --- a/frontend/src/app/features/administration/guards/user-must-exist.guard.ts +++ b/frontend/src/app/features/administration/guards/user-must-exist.guard.ts @@ -5,37 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; -import { UsersState } from '@app/features/administration/internal'; -import { allParams } from '@app/framework'; +import { allParams } from '@app/shared'; +import { UsersState } from '../internal'; -@Injectable() -export class UserMustExistGuard { - constructor( - private readonly usersState: UsersState, - private readonly router: Router, - ) { - } +export const userMustExistGuard = (route: ActivatedRouteSnapshot) => { + const usersState = inject(UsersState); - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const userId = allParams(route)['userId']; + const userId = allParams(route)['userId']; - if (!userId || userId === 'new') { - return this.usersState.select(null).pipe(map(u => u === null)); - } + if (!userId || userId === 'new') { + return usersState.select(null).pipe(map(u => u === null)); + } - const result = - this.usersState.select(userId).pipe( - tap(dto => { - if (!dto) { - this.router.navigate(['/404']); - } - }), - map(u => !!u)); + const router = inject(Router); + const result = + usersState.select(userId).pipe( + tap(dto => { + if (!dto) { + router.navigate(['/404']); + } + }), + map(u => !!u)); - return result; - } -} + return result; +}; \ No newline at end of file diff --git a/frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts b/frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts index c22d1313e..7907165ef 100644 --- a/frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts +++ b/frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts @@ -7,14 +7,21 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal'; +import { TooltipDirective } from '@app/shared'; +import { EventConsumerDto, EventConsumersState } from '../../internal'; @Component({ + standalone: true, selector: '[sqxEventConsumer]', styleUrls: ['./event-consumer.component.scss'], templateUrl: './event-consumer.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + TooltipDirective, + ], }) export class EventConsumerComponent { @Output() diff --git a/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.ts b/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.ts index 924abb45c..3e06ab0ae 100644 --- a/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.ts +++ b/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.ts @@ -5,16 +5,39 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { timer } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { EventConsumerDto, EventConsumersState } from '@app/features/administration/internal'; -import { DialogModel, Subscriptions } from '@app/shared'; +import { DialogModel, LayoutComponent, ListViewComponent, ModalDialogComponent, ModalDirective, ShortcutDirective, SidebarMenuDirective, Subscriptions, SyncWidthDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { EventConsumerDto, EventConsumersState } from '../../internal'; +import { EventConsumerComponent } from './event-consumer.component'; @Component({ + standalone: true, selector: 'sqx-event-consumers-page', styleUrls: ['./event-consumers-page.component.scss'], templateUrl: './event-consumers-page.component.html', + imports: [ + AsyncPipe, + EventConsumerComponent, + LayoutComponent, + ListViewComponent, + ModalDialogComponent, + ModalDirective, + NgFor, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + SyncWidthDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class EventConsumersPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/administration/pages/restore/restore-page.component.ts b/frontend/src/app/features/administration/pages/restore/restore-page.component.ts index 1ca8662ff..dc61b1864 100644 --- a/frontend/src/app/features/administration/pages/restore/restore-page.component.ts +++ b/frontend/src/app/features/administration/pages/restore/restore-page.component.ts @@ -5,14 +5,37 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { timer } from 'rxjs'; -import { AuthService, BackupsService, DialogService, RestoreForm, switchSafe } from '@app/shared'; +import { AuthService, BackupsService, ControlErrorsComponent, DialogService, ISODatePipe, LayoutComponent, ListViewComponent, RestoreForm, SidebarMenuDirective, switchSafe, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-restore-page', styleUrls: ['./restore-page.component.scss'], templateUrl: './restore-page.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormsModule, + ISODatePipe, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class RestorePageComponent { public restoreForm = new RestoreForm(); diff --git a/frontend/src/app/features/administration/pages/users/user-page.component.ts b/frontend/src/app/features/administration/pages/users/user-page.component.ts index 59168ea12..54301773d 100644 --- a/frontend/src/app/features/administration/pages/users/user-page.component.ts +++ b/frontend/src/app/features/administration/pages/users/user-page.component.ts @@ -5,15 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { UpsertUserDto, UserDto, UserForm, UsersState } from '@app/features/administration/internal'; -import { Subscriptions } from '@app/shared'; +import { ControlErrorsComponent, FormErrorComponent, LayoutComponent, ShortcutDirective, Subscriptions, TitleComponent, TooltipDirective, TranslatePipe } from '@app/shared'; +import { UpsertUserDto, UserDto, UserForm, UsersState } from '../../internal'; @Component({ + standalone: true, selector: 'sqx-user-page', styleUrls: ['./user-page.component.scss'], templateUrl: './user-page.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormErrorComponent, + FormsModule, + LayoutComponent, + NgIf, + ReactiveFormsModule, + ShortcutDirective, + TitleComponent, + TooltipDirective, + TranslatePipe, + ], }) export class UserPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/administration/pages/users/user.component.ts b/frontend/src/app/features/administration/pages/users/user.component.ts index 0bd9c4935..ac02ff3f0 100644 --- a/frontend/src/app/features/administration/pages/users/user.component.ts +++ b/frontend/src/app/features/administration/pages/users/user.component.ts @@ -7,14 +7,27 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { UserDto, UsersState } from '@app/features/administration/internal'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { ConfirmClickDirective, StopClickDirective, TooltipDirective, UserDtoPicture } from '@app/shared'; +import { UserDto, UsersState } from '../../internal'; @Component({ + standalone: true, selector: '[sqxUser]', styleUrls: ['./user.component.scss'], templateUrl: './user.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + NgIf, + RouterLink, + RouterLinkActive, + StopClickDirective, + TooltipDirective, + UserDtoPicture, + ], }) export class UserComponent { @Input('sqxUser') diff --git a/frontend/src/app/features/administration/pages/users/users-page.component.ts b/frontend/src/app/features/administration/pages/users/users-page.component.ts index 550609542..13d2c80a8 100644 --- a/frontend/src/app/features/administration/pages/users/users-page.component.ts +++ b/frontend/src/app/features/administration/pages/users/users-page.component.ts @@ -5,18 +5,43 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { UntypedFormControl } from '@angular/forms'; -import { UserDto, UsersState } from '@app/features/administration/internal'; -import { Router2State, Subscriptions } from '@app/framework'; +import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { LayoutComponent, ListViewComponent, PagerComponent, Router2State, ShortcutDirective, SidebarMenuDirective, Subscriptions, SyncWidthDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { UserDto, UsersState } from '../../internal'; +import { UserComponent } from './user.component'; @Component({ + standalone: true, selector: 'sqx-users-page', styleUrls: ['./users-page.component.scss'], templateUrl: './users-page.component.html', providers: [ Router2State, ], + imports: [ + AsyncPipe, + FormsModule, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + PagerComponent, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + SyncWidthDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + UserComponent, + ], }) export class UsersPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/administration/module.ts b/frontend/src/app/features/administration/routes.ts similarity index 61% rename from frontend/src/app/features/administration/module.ts rename to frontend/src/app/features/administration/routes.ts index 7b185e889..33f3ba8d7 100644 --- a/frontend/src/app/features/administration/module.ts +++ b/frontend/src/app/features/administration/routes.ts @@ -5,12 +5,17 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { HelpComponent, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AdministrationAreaComponent, EventConsumerComponent, EventConsumersPageComponent, EventConsumersService, EventConsumersState, RestorePageComponent, UserComponent, UserMustExistGuard, UserPageComponent, UsersPageComponent, UsersService, UsersState } from './declarations'; +import { Routes } from '@angular/router'; +import { HelpComponent, UsersService } from '@app/shared'; +import { AdministrationAreaComponent } from './administration-area.component'; +import { userMustExistGuard } from './guards/user-must-exist.guard'; +import { EventConsumersService, EventConsumersState, UsersState } from './internal'; +import { EventConsumersPageComponent } from './pages/event-consumers/event-consumers-page.component'; +import { RestorePageComponent } from './pages/restore/restore-page.component'; +import { UserPageComponent } from './pages/users/user-page.component'; +import { UsersPageComponent } from './pages/users/users-page.component'; -const routes: Routes = [ +export const ADMINISTRATION_ROUTES: Routes = [ { path: '', component: AdministrationAreaComponent, @@ -23,6 +28,10 @@ const routes: Routes = [ { path: 'event-consumers', component: EventConsumersPageComponent, + providers: [ + EventConsumersService, + EventConsumersState, + ], children: [ { path: 'help', @@ -49,6 +58,10 @@ const routes: Routes = [ { path: 'users', component: UsersPageComponent, + providers: [ + UsersService, + UsersState, + ], children: [ { path: 'help', @@ -60,35 +73,10 @@ const routes: Routes = [ { path: ':userId', component: UserPageComponent, - canActivate: [UserMustExistGuard], + canActivate: [userMustExistGuard], }, ], }, ], }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AdministrationAreaComponent, - EventConsumerComponent, - EventConsumersPageComponent, - RestorePageComponent, - UserComponent, - UserPageComponent, - UsersPageComponent, - ], - providers: [ - EventConsumersService, - EventConsumersState, - UserMustExistGuard, - UsersService, - UsersState, - ], -}) -export class SqxFeatureAdministrationModule {} +]; \ No newline at end of file diff --git a/frontend/src/app/features/administration/services/event-consumers.service.spec.ts b/frontend/src/app/features/administration/services/event-consumers.service.spec.ts index 7316407f3..e9336a1e1 100644 --- a/frontend/src/app/features/administration/services/event-consumers.service.spec.ts +++ b/frontend/src/app/features/administration/services/event-consumers.service.spec.ts @@ -7,7 +7,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; -import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework'; +import { ApiUrlConfig, Resource, ResourceLinks } from '@app/shared'; import { EventConsumerDto, EventConsumersDto, EventConsumersService } from './event-consumers.service'; describe('EventConsumersService', () => { diff --git a/frontend/src/app/features/administration/services/users.service.spec.ts b/frontend/src/app/features/administration/services/users.service.spec.ts index ae3547d64..1f920eeb8 100644 --- a/frontend/src/app/features/administration/services/users.service.spec.ts +++ b/frontend/src/app/features/administration/services/users.service.spec.ts @@ -7,7 +7,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; -import { ApiUrlConfig, Resource, ResourceLinks } from '@app/framework'; +import { ApiUrlConfig, Resource, ResourceLinks } from '@app/shared'; import { UserDto, UsersDto, UsersService } from './users.service'; describe('UsersService', () => { diff --git a/frontend/src/app/features/administration/state/event-consumers.state.spec.ts b/frontend/src/app/features/administration/state/event-consumers.state.spec.ts index 7ed1a04f2..ad4c897d0 100644 --- a/frontend/src/app/features/administration/state/event-consumers.state.spec.ts +++ b/frontend/src/app/features/administration/state/event-consumers.state.spec.ts @@ -7,9 +7,9 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; -import { EventConsumersService } from '@app/features/administration/internal'; -import { DialogService } from '@app/framework'; -import { createEventConsumer } from './../services/event-consumers.service.spec'; +import { DialogService } from '@app/shared'; +import { EventConsumersService } from '../internal'; +import { createEventConsumer } from '../services/event-consumers.service.spec'; import { EventConsumersState } from './event-consumers.state'; describe('EventConsumersState', () => { diff --git a/frontend/src/app/features/administration/state/event-consumers.state.ts b/frontend/src/app/features/administration/state/event-consumers.state.ts index 94a2f69c2..c0e6b779a 100644 --- a/frontend/src/app/features/administration/state/event-consumers.state.ts +++ b/frontend/src/app/features/administration/state/event-consumers.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State } from '@app/shared'; -import { EventConsumerDto, EventConsumersService } from './../services/event-consumers.service'; +import { EventConsumerDto, EventConsumersService } from '../services/event-consumers.service'; interface Snapshot extends LoadingState { // The list of event consumers. diff --git a/frontend/src/app/features/administration/state/users.forms.ts b/frontend/src/app/features/administration/state/users.forms.ts index b2a3bc54f..061affee8 100644 --- a/frontend/src/app/features/administration/state/users.forms.ts +++ b/frontend/src/app/features/administration/state/users.forms.ts @@ -7,7 +7,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, ValidatorsEx } from '@app/shared'; -import { UpsertUserDto, UserDto } from './../services/users.service'; +import { UpsertUserDto, UserDto } from '../services/users.service'; export class UserForm extends Form<ExtendedFormGroup, UpsertUserDto, UserDto> { constructor() { diff --git a/frontend/src/app/features/administration/state/users.state.spec.ts b/frontend/src/app/features/administration/state/users.state.spec.ts index 266d2dd05..6dedd4bd8 100644 --- a/frontend/src/app/features/administration/state/users.state.spec.ts +++ b/frontend/src/app/features/administration/state/users.state.spec.ts @@ -7,9 +7,9 @@ import { firstValueFrom, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; -import { UpsertUserDto, UsersService } from '@app/features/administration/internal'; import { DialogService } from '@app/shared'; -import { createUser } from './../services/users.service.spec'; +import { UpsertUserDto, UsersService } from '../internal'; +import { createUser } from '../services/users.service.spec'; import { UsersState } from './users.state'; describe('UsersState', () => { diff --git a/frontend/src/app/features/administration/state/users.state.ts b/frontend/src/app/features/administration/state/users.state.ts index 5617c13fe..a3c1605bf 100644 --- a/frontend/src/app/features/administration/state/users.state.ts +++ b/frontend/src/app/features/administration/state/users.state.ts @@ -12,7 +12,7 @@ import '@app/framework/utils/rxjs-extensions'; import { EMPTY, Observable, of } from 'rxjs'; import { catchError, finalize, tap } from 'rxjs/operators'; import { debug, DialogService, getPagingInfo, ListState, shareSubscribed, State } from '@app/shared'; -import { UpsertUserDto, UserDto, UsersService } from './../services/users.service'; +import { UpsertUserDto, UserDto, UsersService } from '../services/users.service'; interface Snapshot extends ListState<string> { // The current users. diff --git a/frontend/src/app/features/api/api-area.component.ts b/frontend/src/app/features/api/api-area.component.ts index 324d236b8..831e64bc6 100644 --- a/frontend/src/app/features/api/api-area.component.ts +++ b/frontend/src/app/features/api/api-area.component.ts @@ -6,12 +6,24 @@ */ import { Component } from '@angular/core'; -import { AppsState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { AppsState, ExternalLinkDirective, LayoutComponent, TitleComponent, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-api-area', styleUrls: ['./api-area.component.scss'], templateUrl: './api-area.component.html', + imports: [ + ExternalLinkDirective, + LayoutComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + TitleComponent, + TourStepDirective, + TranslatePipe, + ], }) export class ApiAreaComponent { constructor( diff --git a/frontend/src/app/features/api/declarations.ts b/frontend/src/app/features/api/declarations.ts deleted file mode 100644 index bcec21790..000000000 --- a/frontend/src/app/features/api/declarations.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './api-area.component'; -export * from './pages/graphql/graphql-page.component'; diff --git a/frontend/src/app/features/api/index.ts b/frontend/src/app/features/api/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/api/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/api/module.ts b/frontend/src/app/features/api/module.ts deleted file mode 100644 index 1887d61ae..000000000 --- a/frontend/src/app/features/api/module.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { ApiAreaComponent, GraphQLPageComponent } from './declarations'; - -const routes: Routes = [ - { - path: '', - component: ApiAreaComponent, - children: [ - { - path: 'graphql', - component: GraphQLPageComponent, - }, - ], - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - ApiAreaComponent, - GraphQLPageComponent, - ], -}) -export class SqxFeatureApiModule {} diff --git a/frontend/src/app/features/api/pages/graphql/graphql-page.component.ts b/frontend/src/app/features/api/pages/graphql/graphql-page.component.ts index dfa733701..35b342a7a 100644 --- a/frontend/src/app/features/api/pages/graphql/graphql-page.component.ts +++ b/frontend/src/app/features/api/pages/graphql/graphql-page.component.ts @@ -5,17 +5,34 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; import GraphiQL from 'graphiql'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { ApiUrlConfig, AppsState, AuthService, ClientDto, ClientsService, ClientsState, DialogModel, MessageBus, QueryExecuted, Types } from '@app/shared'; +import { ApiUrlConfig, AppsState, AuthService, ClientDto, ClientsService, ClientsState, DialogModel, FormHintComponent, LayoutComponent, MessageBus, ModalDialogComponent, ModalDirective, QueryExecuted, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, Types } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-graphql-page', styleUrls: ['./graphql-page.component.scss'], templateUrl: './graphql-page.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + FormsModule, + LayoutComponent, + ModalDialogComponent, + ModalDirective, + NgFor, + NgIf, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class GraphQLPageComponent implements AfterViewInit, OnInit { @ViewChild('graphiQLContainer', { static: false }) diff --git a/frontend/src/app/features/api/routes.ts b/frontend/src/app/features/api/routes.ts new file mode 100644 index 000000000..6e14b4003 --- /dev/null +++ b/frontend/src/app/features/api/routes.ts @@ -0,0 +1,23 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { ApiAreaComponent } from './api-area.component'; +import { GraphQLPageComponent } from './pages/graphql/graphql-page.component'; + +export const API_ROUTES: Routes = [ + { + path: '', + component: ApiAreaComponent, + children: [ + { + path: 'graphql', + component: GraphQLPageComponent, + }, + ], + }, +]; diff --git a/frontend/src/app/features/apps/declarations.ts b/frontend/src/app/features/apps/declarations.ts deleted file mode 100644 index e2a6af063..000000000 --- a/frontend/src/app/features/apps/declarations.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/app.component'; -export * from './pages/apps-page.component'; -export * from './pages/news-dialog.component'; -export * from './pages/onboarding-dialog.component'; -export * from './pages/team.component'; \ No newline at end of file diff --git a/frontend/src/app/features/apps/index.ts b/frontend/src/app/features/apps/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/apps/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/apps/module.ts b/frontend/src/app/features/apps/module.ts deleted file mode 100644 index 12a234158..000000000 --- a/frontend/src/app/features/apps/module.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AppComponent, AppsPageComponent, NewsDialogComponent, OnboardingDialogComponent, TeamComponent } from './declarations'; - -const routes: Routes = [ - { - path: '', - component: AppsPageComponent, - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AppComponent, - AppsPageComponent, - NewsDialogComponent, - OnboardingDialogComponent, - TeamComponent, - ], -}) -export class SqxFeatureAppsModule {} diff --git a/frontend/src/app/features/apps/pages/app.component.ts b/frontend/src/app/features/apps/pages/app.component.ts index f4eb6df95..67b840c5e 100644 --- a/frontend/src/app/features/apps/pages/app.component.ts +++ b/frontend/src/app/features/apps/pages/app.component.ts @@ -5,14 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { AppDto, ModalModel } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { AppDto, AvatarComponent, ConfirmClickDirective, DropdownMenuComponent, ModalDirective, ModalModel, ModalPlacementDirective, StopClickDirective, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-app', styleUrls: ['./app.component.scss'], templateUrl: './app.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AvatarComponent, + ConfirmClickDirective, + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgIf, + RouterLink, + StopClickDirective, + TourStepDirective, + TranslatePipe, + ], }) export class AppComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/apps/pages/apps-page.component.ts b/frontend/src/app/features/apps/pages/apps-page.component.ts index 6ded4e811..ccf74f83e 100644 --- a/frontend/src/app/features/apps/pages/apps-page.component.ts +++ b/frontend/src/app/features/apps/pages/apps-page.component.ts @@ -5,18 +5,38 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { combineLatest } from 'rxjs'; import { map, take } from 'rxjs/operators'; -import { AppDto, AppsState, AuthService, DialogModel, FeatureDto, LocalStoreService, NewsService, TeamDto, TeamsState, TemplateDto, TemplatesState, TourState, UIOptions, UIState } from '@app/shared'; -import { Settings } from '@app/shared/state/settings'; +import { AppDto, AppFormComponent, AppsState, AuthService, DialogModel, FeatureDto, FormHintComponent, LocalStoreService, ModalDirective, NewsService, Settings, TeamDto, TeamsState, TemplateDto, TemplatesState, TitleComponent, TourState, TourStepDirective, TranslatePipe, UIOptions, UIState } from '@app/shared'; +import { AppComponent } from './app.component'; +import { NewsDialogComponent } from './news-dialog.component'; +import { OnboardingDialogComponent } from './onboarding-dialog.component'; +import { TeamComponent } from './team.component'; type GroupedApps = { team?: TeamDto; apps: AppDto[] }; @Component({ + standalone: true, selector: 'sqx-apps-page', styleUrls: ['./apps-page.component.scss'], templateUrl: './apps-page.component.html', + imports: [ + AppComponent, + AppFormComponent, + AsyncPipe, + FormHintComponent, + ModalDirective, + NewsDialogComponent, + NgFor, + NgIf, + OnboardingDialogComponent, + TeamComponent, + TitleComponent, + TourStepDirective, + TranslatePipe, + ], }) export class AppsPageComponent implements OnInit { public addAppDialog = new DialogModel(); diff --git a/frontend/src/app/features/apps/pages/news-dialog.component.ts b/frontend/src/app/features/apps/pages/news-dialog.component.ts index 4d754d2b9..bdd2937ed 100644 --- a/frontend/src/app/features/apps/pages/news-dialog.component.ts +++ b/frontend/src/app/features/apps/pages/news-dialog.component.ts @@ -5,13 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FeatureDto } from '@app/shared'; +import { FeatureDto, HelpMarkdownPipe, ModalDialogComponent, TooltipDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-news-dialog', styleUrls: ['./news-dialog.component.scss'], templateUrl: './news-dialog.component.html', + imports: [ + HelpMarkdownPipe, + ModalDialogComponent, + NgFor, + TooltipDirective, + TranslatePipe, + ], }) export class NewsDialogComponent { @Output() diff --git a/frontend/src/app/features/apps/pages/onboarding-dialog.component.ts b/frontend/src/app/features/apps/pages/onboarding-dialog.component.ts index 00f6c5b4d..23e57519c 100644 --- a/frontend/src/app/features/apps/pages/onboarding-dialog.component.ts +++ b/frontend/src/app/features/apps/pages/onboarding-dialog.component.ts @@ -5,18 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { Component, EventEmitter, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { fadeAnimation, slideAnimation } from '@app/framework'; -import { TourState, UsersService } from '@app/shared'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { fadeAnimation, MarkdownPipe, ModalDialogComponent, SafeHtmlPipe, slideAnimation, TourState, TranslatePipe, UsersService } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-onboarding-dialog', styleUrls: ['./onboarding-dialog.component.scss'], templateUrl: './onboarding-dialog.component.html', animations: [ fadeAnimation, slideAnimation, ], + imports: [ + FormsModule, + MarkdownPipe, + ModalDialogComponent, + NgIf, + ReactiveFormsModule, + SafeHtmlPipe, + TranslatePipe, + ], }) export class OnboardingDialogComponent { diff --git a/frontend/src/app/features/apps/pages/team.component.ts b/frontend/src/app/features/apps/pages/team.component.ts index f3757ccf3..76035ebea 100644 --- a/frontend/src/app/features/apps/pages/team.component.ts +++ b/frontend/src/app/features/apps/pages/team.component.ts @@ -6,13 +6,24 @@ */ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { ModalModel, TeamDto } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { ConfirmClickDirective, DropdownMenuComponent, ModalDirective, ModalModel, ModalPlacementDirective, StopClickDirective, TeamDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-team', styleUrls: ['./team.component.scss'], templateUrl: './team.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + RouterLink, + StopClickDirective, + TranslatePipe, + ], }) export class TeamComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/apps/routes.ts b/frontend/src/app/features/apps/routes.ts new file mode 100644 index 000000000..fb8523294 --- /dev/null +++ b/frontend/src/app/features/apps/routes.ts @@ -0,0 +1,16 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { AppsPageComponent } from './pages/apps-page.component'; + +export const APPS_ROUTES: Routes = [ + { + path: '', + component: AppsPageComponent, + }, +]; diff --git a/frontend/src/app/features/assets/declarations.ts b/frontend/src/app/features/assets/declarations.ts deleted file mode 100644 index fafc30a4a..000000000 --- a/frontend/src/app/features/assets/declarations.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/asset-tag-dialog.component'; -export * from './pages/asset-tags.component'; -export * from './pages/assets-filters-page.component'; -export * from './pages/assets-page.component'; diff --git a/frontend/src/app/features/assets/index.ts b/frontend/src/app/features/assets/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/assets/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/assets/module.ts b/frontend/src/app/features/assets/module.ts deleted file mode 100644 index 6cb95cafa..000000000 --- a/frontend/src/app/features/assets/module.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AssetsFiltersPageComponent, AssetsPageComponent, AssetTagDialogComponent, AssetTagsComponent } from './declarations'; - -const routes: Routes = [ - { - path: '', - component: AssetsPageComponent, - children: [ - { - path: 'filters', - component: AssetsFiltersPageComponent, - }, - ], - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AssetsFiltersPageComponent, - AssetsPageComponent, - AssetTagDialogComponent, - AssetTagsComponent, - ], -}) -export class SqxFeatureAssetsModule {} diff --git a/frontend/src/app/features/assets/pages/asset-tag-dialog.component.ts b/frontend/src/app/features/assets/pages/asset-tag-dialog.component.ts index a199f9e1d..e4484407a 100644 --- a/frontend/src/app/features/assets/pages/asset-tag-dialog.component.ts +++ b/frontend/src/app/features/assets/pages/asset-tag-dialog.component.ts @@ -5,13 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ControlErrorsComponent, FocusOnInitDirective, FormErrorComponent, ModalDialogComponent, TooltipDirective, TranslatePipe } from '@app/shared'; import { AssetsState, RenameAssetTagForm } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-tag-dialog', styleUrls: ['./asset-tag-dialog.component.scss'], templateUrl: './asset-tag-dialog.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FocusOnInitDirective, + FormErrorComponent, + FormsModule, + ModalDialogComponent, + ReactiveFormsModule, + TooltipDirective, + TranslatePipe, + ], }) export class AssetTagDialogComponent implements OnInit { @Output() diff --git a/frontend/src/app/features/assets/pages/asset-tags.component.ts b/frontend/src/app/features/assets/pages/asset-tags.component.ts index 2057b658d..3832fd6db 100644 --- a/frontend/src/app/features/assets/pages/asset-tags.component.ts +++ b/frontend/src/app/features/assets/pages/asset-tags.component.ts @@ -7,14 +7,25 @@ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { DialogModel, TagItem, TagsSelected } from '@app/shared'; +import { DialogModel, ModalDirective, StopClickDirective, TagItem, TagsSelected, TranslatePipe } from '@app/shared'; +import { AssetTagDialogComponent } from './asset-tag-dialog.component'; @Component({ + standalone: true, selector: 'sqx-asset-tags', styleUrls: ['./asset-tags.component.scss'], templateUrl: './asset-tags.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetTagDialogComponent, + ModalDirective, + NgFor, + NgIf, + StopClickDirective, + TranslatePipe, + ], }) export class AssetTagsComponent { @Output() diff --git a/frontend/src/app/features/assets/pages/assets-filters-page.component.ts b/frontend/src/app/features/assets/pages/assets-filters-page.component.ts index 3e45ae77c..a156f279f 100644 --- a/frontend/src/app/features/assets/pages/assets-filters-page.component.ts +++ b/frontend/src/app/features/assets/pages/assets-filters-page.component.ts @@ -5,13 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component } from '@angular/core'; -import { AssetsState, Queries, Query, UIState } from '@app/shared'; +import { AssetsState, LayoutComponent, Queries, Query, SavedQueriesComponent, TranslatePipe, UIState } from '@app/shared'; +import { AssetTagsComponent } from './asset-tags.component'; @Component({ + standalone: true, selector: 'sqx-assets-filters-page', styleUrls: ['./assets-filters-page.component.scss'], templateUrl: './assets-filters-page.component.html', + imports: [ + AssetTagsComponent, + AsyncPipe, + LayoutComponent, + SavedQueriesComponent, + TranslatePipe, + ], }) export class AssetsFiltersPageComponent { public assetsQueries: Queries; diff --git a/frontend/src/app/features/assets/pages/assets-page.component.ts b/frontend/src/app/features/assets/pages/assets-page.component.ts index 588651a3a..5522cd429 100644 --- a/frontend/src/app/features/assets/pages/assets-page.component.ts +++ b/frontend/src/app/features/assets/pages/assets-page.component.ts @@ -5,17 +5,44 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { AssetDto, AssetsState, DialogModel, LocalStoreService, MathHelper, Queries, Query, QueryFullTextSynchronizer, Router2State, UIState } from '@app/shared'; -import { Settings } from '@app/shared/state/settings'; +import { FormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { AssetDialogComponent, AssetDto, AssetFolderDialogComponent, AssetPathComponent, AssetsListComponent, AssetsState, DialogModel, LayoutComponent, ListViewComponent, LocalStoreService, MathHelper, ModalDirective, PagerComponent, Queries, Query, QueryFullTextSynchronizer, Router2State, SearchFormComponent, Settings, ShortcutDirective, SidebarMenuDirective, TagEditorComponent, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, UIState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-assets-page', styleUrls: ['./assets-page.component.scss'], templateUrl: './assets-page.component.html', providers: [ Router2State, ], + imports: [ + AssetDialogComponent, + AssetFolderDialogComponent, + AssetPathComponent, + AssetsListComponent, + AsyncPipe, + FormsModule, + LayoutComponent, + ListViewComponent, + ModalDirective, + NgIf, + PagerComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + SearchFormComponent, + ShortcutDirective, + SidebarMenuDirective, + TagEditorComponent, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class AssetsPageComponent implements OnInit { public editAsset?: AssetDto; diff --git a/frontend/src/app/features/assets/routes.ts b/frontend/src/app/features/assets/routes.ts new file mode 100644 index 000000000..d05a42338 --- /dev/null +++ b/frontend/src/app/features/assets/routes.ts @@ -0,0 +1,23 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { AssetsFiltersPageComponent } from './pages/assets-filters-page.component'; +import { AssetsPageComponent } from './pages/assets-page.component'; + +export const ASSETS_ROUTES: Routes = [ + { + path: '', + component: AssetsPageComponent, + children: [ + { + path: 'filters', + component: AssetsFiltersPageComponent, + }, + ], + }, +]; \ No newline at end of file diff --git a/frontend/src/app/features/content/declarations.ts b/frontend/src/app/features/content/declarations.ts deleted file mode 100644 index 0862ab669..000000000 --- a/frontend/src/app/features/content/declarations.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/calendar/calendar-page.component'; -export * from './pages/comments/comments-page.component'; -export * from './pages/content/content-event.component'; -export * from './pages/content/content-history-page.component'; -export * from './pages/content/content-page.component'; -export * from './pages/content/editor/content-editor.component'; -export * from './pages/content/editor/content-field.component'; -export * from './pages/content/editor/content-section.component'; -export * from './pages/content/editor/field-copy-button.component'; -export * from './pages/content/editor/field-languages.component'; -export * from './pages/content/inspecting/content-inspection.component'; -export * from './pages/content/references/content-references.component'; -export * from './pages/contents/contents-filters-page.component'; -export * from './pages/contents/contents-page.component'; -export * from './pages/contents/custom-view-editor.component'; -export * from './pages/schemas/schemas-page.component'; -export * from './pages/sidebar/sidebar-page.component'; -export * from './pages/references/references-page.component'; -export * from './shared/content-extension.component'; -export * from './shared/due-time-selector.component'; -export * from './shared/forms/array-editor.component'; -export * from './shared/forms/array-item.component'; -export * from './shared/forms/assets-editor.component'; -export * from './shared/forms/component-section.component'; -export * from './shared/forms/component.component'; -export * from './shared/forms/field-editor.component'; -export * from './shared/forms/iframe-editor.component'; -export * from './shared/forms/stock-photo-editor.component'; -export * from './shared/list/content.component'; -export * from './shared/preview-button.component'; -export * from './shared/references/content-creator.component'; -export * from './shared/references/reference-dropdown.component'; -export * from './shared/references/reference-item.component'; -export * from './shared/references/references-checkboxes.component'; -export * from './shared/references/references-editor.component'; -export * from './shared/references/references-tags.component'; diff --git a/frontend/src/app/features/content/index.ts b/frontend/src/app/features/content/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/content/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/content/module.ts b/frontend/src/app/features/content/module.ts deleted file mode 100644 index 19fc38168..000000000 --- a/frontend/src/app/features/content/module.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { VirtualScrollerModule } from '@iharbeck/ngx-virtual-scroller'; -import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, LoadSchemasGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { ArrayEditorComponent, ArrayItemComponent, AssetsEditorComponent, CalendarPageComponent, CommentsPageComponent, ComponentComponent, ComponentSectionComponent, ContentComponent, ContentCreatorComponent, ContentEditorComponent, ContentEventComponent, ContentExtensionComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentInspectionComponent, ContentPageComponent, ContentReferencesComponent, ContentSectionComponent, ContentsFiltersPageComponent, ContentsPageComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldCopyButtonComponent, FieldEditorComponent, FieldLanguagesComponent, IFrameEditorComponent, PreviewButtonComponent, ReferenceDropdownComponent, ReferenceItemComponent, ReferencesCheckboxesComponent, ReferencesEditorComponent, ReferencesPageComponent, ReferencesTagsComponent, SchemasPageComponent, SidebarPageComponent, StockPhotoEditorComponent } from './declarations'; - -const routes: Routes = [ - { - path: '', - component: SchemasPageComponent, - canActivate: [LoadLanguagesGuard, LoadSchemasGuard], - children: [ - { - path: '__calendar', - component: CalendarPageComponent, - }, - { - path: '__references/:referenceId', - component: ReferencesPageComponent, - }, - { - path: ':schemaName', - canActivate: [SchemaMustExistPublishedGuard], - children: [ - { - path: '', - component: ContentsPageComponent, - canActivate: [SchemaMustNotBeSingletonGuard, ContentMustExistGuard], - canDeactivate: [CanDeactivateGuard], - children: [ - { - path: 'filters', - component: ContentsFiltersPageComponent, - }, - { - path: 'sidebar', - component: SidebarPageComponent, - }, - ], - }, - { - path: 'new', - component: ContentPageComponent, - canActivate: [SchemaMustNotBeSingletonGuard, ContentMustExistGuard], - canDeactivate: [CanDeactivateGuard], - data: { - reuseId: 'contentPage', - }, - }, - { - path: ':contentId', - component: ContentPageComponent, - canActivate: [ContentMustExistGuard], - canDeactivate: [CanDeactivateGuard], - data: { - reuseId: 'contentPage', - }, - children: [ - { - path: 'history', - component: ContentHistoryPageComponent, - data: { - channel: 'contents.{contentId}', - }, - }, - { - path: 'comments', - component: CommentsPageComponent, - }, - { - path: 'sidebar', - component: SidebarPageComponent, - }, - ], - }, - ], - }], - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - VirtualScrollerModule, - ], - declarations: [ - ArrayEditorComponent, - ArrayItemComponent, - AssetsEditorComponent, - CalendarPageComponent, - CommentsPageComponent, - ComponentComponent, - ComponentSectionComponent, - ContentComponent, - ContentCreatorComponent, - ContentEditorComponent, - ContentEventComponent, - ContentExtensionComponent, - ContentFieldComponent, - ContentHistoryPageComponent, - ContentInspectionComponent, - ContentPageComponent, - ContentReferencesComponent, - ContentSectionComponent, - ContentsFiltersPageComponent, - ContentsPageComponent, - CustomViewEditorComponent, - DueTimeSelectorComponent, - FieldCopyButtonComponent, - FieldEditorComponent, - FieldLanguagesComponent, - IFrameEditorComponent, - PreviewButtonComponent, - ReferenceDropdownComponent, - ReferenceItemComponent, - ReferencesCheckboxesComponent, - ReferencesEditorComponent, - ReferencesEditorComponent, - ReferencesPageComponent, - ReferencesTagsComponent, - SchemasPageComponent, - SidebarPageComponent, - StockPhotoEditorComponent, - ], -}) -export class SqxFeatureContentModule {} diff --git a/frontend/src/app/features/content/pages/calendar/calendar-page.component.ts b/frontend/src/app/features/content/pages/calendar/calendar-page.component.ts index cbf0f05f4..ee9e5987b 100644 --- a/frontend/src/app/features/content/pages/calendar/calendar-page.component.ts +++ b/frontend/src/app/features/content/pages/calendar/calendar-page.component.ts @@ -5,17 +5,38 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { AppsState, ContentDto, ContentsService, DateTime, DialogModel, getContentValue, LanguageDto, LanguagesState, LocalizerService, ResourceLoaderService } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { AppsState, ConfirmClickDirective, ContentDto, ContentsService, ContentStatusComponent, CopyDirective, DateTime, DialogModel, FullDateTimePipe, getContentValue, LanguageDto, LanguagesState, LayoutComponent, LocalizerService, ModalDialogComponent, ModalDirective, ResourceLoaderService, TitleComponent, TooltipDirective, TranslatePipe, UserNameRefPipe, UserPictureRefPipe } from '@app/shared'; declare const tui: any; type ViewMode = 'day' | 'week' | 'month'; @Component({ + standalone: true, selector: 'sqx-calendar-page', styleUrls: ['./calendar-page.component.scss'], templateUrl: './calendar-page.component.html', + imports: [ + ConfirmClickDirective, + ContentStatusComponent, + CopyDirective, + FormsModule, + FullDateTimePipe, + LayoutComponent, + ModalDialogComponent, + ModalDirective, + NgIf, + RouterLink, + TitleComponent, + TooltipDirective, + TranslatePipe, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class CalendarPageComponent implements AfterViewInit, OnDestroy, OnInit { private calendar: any; diff --git a/frontend/src/app/features/content/pages/comments/comments-page.component.ts b/frontend/src/app/features/content/pages/comments/comments-page.component.ts index 750f6c2b9..ffab265ba 100644 --- a/frontend/src/app/features/content/pages/comments/comments-page.component.ts +++ b/frontend/src/app/features/content/pages/comments/comments-page.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map } from 'rxjs/operators'; +import { CommentsComponent, LayoutComponent } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-comments-page', styleUrls: ['./comments-page.component.scss'], templateUrl: './comments-page.component.html', + imports: [ + AsyncPipe, + CommentsComponent, + LayoutComponent, + ], }) export class CommentsPageComponent { public commentsId = this.route.parent!.params.pipe(map(x => x['contentId'])); diff --git a/frontend/src/app/features/content/pages/content/content-event.component.ts b/frontend/src/app/features/content/pages/content/content-event.component.ts index 40397972d..b44e10978 100644 --- a/frontend/src/app/features/content/pages/content/content-event.component.ts +++ b/frontend/src/app/features/content/pages/content/content-event.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { ContentDto, HistoryEventDto, TypedSimpleChanges } from '@app/shared'; +import { ContentDto, FromNowPipe, HistoryEventDto, HistoryMessagePipe, TooltipDirective, TranslatePipe, TypedSimpleChanges, UserNameRefPipe, UserPictureRefPipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-content-event', styleUrls: ['./content-event.component.scss'], templateUrl: './content-event.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FromNowPipe, + HistoryMessagePipe, + NgIf, + TooltipDirective, + TranslatePipe, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class ContentEventComponent { @Output() diff --git a/frontend/src/app/features/content/pages/content/content-history-page.component.ts b/frontend/src/app/features/content/pages/content/content-history-page.component.ts index a345736ec..147ee166c 100644 --- a/frontend/src/app/features/content/pages/content/content-history-page.component.ts +++ b/frontend/src/app/features/content/pages/content/content-history-page.component.ts @@ -5,17 +5,38 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit, ViewChild } from '@angular/core'; import { Observable, timer } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppsState, ContentDto, ContentsState, defined, HistoryEventDto, HistoryService, ModalModel, SchemasState, Subscriptions, switchSafe } from '@app/shared'; -import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; +import { AppsState, ConfirmClickDirective, ContentDto, ContentsState, ContentStatusComponent, CopyDirective, defined, DropdownMenuComponent, FormHintComponent, FromNowPipe, HistoryEventDto, HistoryService, LayoutComponent, ModalDirective, ModalModel, ModalPlacementDirective, SchemasState, Subscriptions, switchSafe, TourStepDirective, TranslatePipe } from '@app/shared'; +import { DueTimeSelectorComponent } from '../../shared/due-time-selector.component'; +import { ContentEventComponent } from './content-event.component'; import { ContentPageComponent } from './content-page.component'; @Component({ + standalone: true, selector: 'sqx-history', styleUrls: ['./content-history-page.component.scss'], templateUrl: './content-history-page.component.html', + imports: [ + AsyncPipe, + ConfirmClickDirective, + ContentEventComponent, + ContentStatusComponent, + CopyDirective, + DropdownMenuComponent, + DueTimeSelectorComponent, + FormHintComponent, + FromNowPipe, + LayoutComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TourStepDirective, + TranslatePipe, + ], }) export class ContentHistoryPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/pages/content/content-page.component.ts b/frontend/src/app/features/content/pages/content/content-page.component.ts index 7fd5e7145..6fd23efde 100644 --- a/frontend/src/app/features/content/pages/content/content-page.component.ts +++ b/frontend/src/app/features/content/pages/content/content-page.component.ts @@ -5,13 +5,21 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { Observable, of } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; -import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, CollaborationService, ContentDto, ContentsState, defined, DialogService, EditContentForm, LanguagesState, LocalStoreService, ModalModel, ResolveAssets, ResolveContents, SchemaDto, SchemasState, Settings, Subscriptions, TempService, ToolbarService, Types, Version } from '@app/shared'; +import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, CollaborationService, ConfirmClickDirective, ContentDto, ContentsState, defined, DialogService, DropdownMenuComponent, EditContentForm, LanguageSelectorComponent, LanguagesState, LayoutComponent, LocalStoreService, ModalDirective, ModalModel, ModalPlacementDirective, NotifoComponent, ResolveAssets, ResolveContents, SchemaDto, SchemasState, Settings, ShortcutDirective, SidebarMenuDirective, Subscriptions, TempService, TitleComponent, ToolbarComponent, ToolbarService, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe, Types, Version, WatchingUsersComponent } from '@app/shared'; +import { ContentExtensionComponent } from '../../shared/content-extension.component'; +import { PreviewButtonComponent } from '../../shared/preview-button.component'; +import { ContentEditorComponent } from './editor/content-editor.component'; +import { ContentInspectionComponent } from './inspecting/content-inspection.component'; +import { ContentReferencesComponent } from './references/content-references.component'; @Component({ + standalone: true, selector: 'sqx-content-page', styleUrls: ['./content-page.component.scss'], templateUrl: './content-page.component.html', @@ -21,6 +29,38 @@ import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, Auto ResolveContents, ToolbarService, ], + imports: [ + AsyncPipe, + ConfirmClickDirective, + ContentEditorComponent, + ContentExtensionComponent, + ContentInspectionComponent, + ContentReferencesComponent, + DropdownMenuComponent, + FormsModule, + LanguageSelectorComponent, + LayoutComponent, + ModalDirective, + ModalPlacementDirective, + NgIf, + NgSwitch, + NgSwitchCase, + NotifoComponent, + PreviewButtonComponent, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + ToolbarComponent, + TooltipDirective, + TourHintDirective, + TourStepDirective, + TranslatePipe, + WatchingUsersComponent, + ], }) export class ContentPageComponent implements CanComponentDeactivate, OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/pages/content/editor/content-editor.component.ts b/frontend/src/app/features/content/pages/content/editor/content-editor.component.ts index cd2ff3a0d..527bc623d 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-editor.component.ts +++ b/frontend/src/app/features/content/pages/content/editor/content-editor.component.ts @@ -5,13 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; -import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, RootFieldDto, SchemaDto, Version } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { AppLanguageDto, CursorsComponent, CursorsDirective, EditContentForm, FieldForm, FieldSection, FormErrorComponent, ListViewComponent, MarkdownInlinePipe, RootFieldDto, SafeHtmlPipe, SchemaDto, TranslatePipe, Version } from '@app/shared'; +import { ContentSectionComponent } from '../../../shared/forms/content-section.component'; @Component({ + standalone: true, selector: 'sqx-content-editor', styleUrls: ['./content-editor.component.scss'], templateUrl: './content-editor.component.html', + imports: [ + AsyncPipe, + ContentSectionComponent, + CursorsComponent, + CursorsDirective, + FormErrorComponent, + FormsModule, + ListViewComponent, + MarkdownInlinePipe, + NgFor, + NgIf, + SafeHtmlPipe, + TranslatePipe, + ], }) export class ContentEditorComponent { @Output() diff --git a/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.ts b/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.ts index 95efdc75e..86eb72232 100644 --- a/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.ts +++ b/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.ts @@ -5,17 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { BehaviorSubject, combineLatest, of } from 'rxjs'; import { filter, map, switchMap } from 'rxjs/operators'; -import { AppLanguageDto, ContentDto, ContentsService, ContentsState, ErrorDto, ToolbarService, TypedSimpleChanges } from '@app/shared'; +import { AppLanguageDto, CodeEditorComponent, ContentDto, ContentsService, ContentsState, ErrorDto, FormErrorComponent, ToolbarService, TranslatePipe, TypedSimpleChanges } from '@app/shared'; type Mode = 'Content' | 'Data' | 'FlatData'; @Component({ + standalone: true, selector: 'sqx-content-inspection', styleUrls: ['./content-inspection.component.scss'], templateUrl: './content-inspection.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + FormErrorComponent, + FormsModule, + NgIf, + TranslatePipe, + ], }) export class ContentInspectionComponent implements OnDestroy { private languageChanges$ = new BehaviorSubject<AppLanguageDto | null>(null); diff --git a/frontend/src/app/features/content/pages/content/references/content-references.component.ts b/frontend/src/app/features/content/pages/content/references/content-references.component.ts index 3255fb678..4c1b922b0 100644 --- a/frontend/src/app/features/content/pages/content/references/content-references.component.ts +++ b/frontend/src/app/features/content/pages/content/references/content-references.component.ts @@ -5,16 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { AppLanguageDto, ComponentContentsState, ContentDto, QuerySynchronizer, Router2State, ToolbarService, TypedSimpleChanges } from '@app/shared'; +import { AppLanguageDto, ComponentContentsState, ContentDto, ContentsColumnsPipe, ListViewComponent, PagerComponent, QuerySynchronizer, Router2State, ToolbarService, TranslatePipe, TypedSimpleChanges } from '@app/shared'; +import { ReferenceItemComponent } from '../../../shared/references/reference-item.component'; @Component({ + standalone: true, selector: 'sqx-content-references', styleUrls: ['./content-references.component.scss'], templateUrl: './content-references.component.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ - Router2State, ComponentContentsState, + ComponentContentsState, + Router2State, + ], + imports: [ + AsyncPipe, + ContentsColumnsPipe, + ListViewComponent, + NgFor, + NgIf, + PagerComponent, + ReferenceItemComponent, + TranslatePipe, ], }) export class ContentReferencesComponent implements OnInit, OnDestroy { diff --git a/frontend/src/app/features/content/pages/contents/contents-filters-page.component.ts b/frontend/src/app/features/content/pages/contents/contents-filters-page.component.ts index 13afe1bce..4443dcebf 100644 --- a/frontend/src/app/features/content/pages/contents/contents-filters-page.component.ts +++ b/frontend/src/app/features/content/pages/contents/contents-filters-page.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; import { map } from 'rxjs/operators'; -import { ContentsState, defined, Queries, Query, SchemasState, UIState } from '@app/shared'; +import { ContentsState, defined, LayoutComponent, Queries, Query, QueryListComponent, SavedQueriesComponent, SchemasState, TranslatePipe, UIState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-contents-filters-page', styleUrls: ['./contents-filters-page.component.scss'], templateUrl: './contents-filters-page.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + NgIf, + QueryListComponent, + SavedQueriesComponent, + TranslatePipe, + ], }) export class ContentsFiltersPageComponent { public schemaQueries = diff --git a/frontend/src/app/features/content/pages/contents/contents-page.component.ts b/frontend/src/app/features/content/pages/contents/contents-page.component.ts index 17d4536b0..2ede27337 100644 --- a/frontend/src/app/features/content/pages/contents/contents-page.component.ts +++ b/frontend/src/app/features/content/pages/contents/contents-page.component.ts @@ -7,20 +7,60 @@ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map, shareReplay, switchMap, take, tap } from 'rxjs/operators'; -import { AppLanguageDto, AppsState, ContentDto, ContentsService, ContentsState, contentsTranslationStatus, ContributorsState, defined, getTableConfig, LanguagesState, LocalStoreService, ModalModel, Queries, Query, QuerySynchronizer, Router2State, SchemaDto, SchemasService, SchemasState, Settings, Subscriptions, switchSafe, TableSettings, TempService, TranslationStatus, UIState } from '@app/shared'; -import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; +import { AppLanguageDto, AppsState, ConfirmClickDirective, ContentDto, ContentListCellDirective, ContentListCellResizeDirective, ContentListHeaderComponent, ContentListWidthDirective, ContentsService, ContentsState, ContentStatusComponent, contentsTranslationStatus, ContributorsState, defined, DropdownMenuComponent, getTableConfig, KeysPipe, LanguageSelectorComponent, LanguagesState, LayoutComponent, ListViewComponent, LocalStoreService, ModalDirective, ModalModel, ModalPlacementDirective, NotifoComponent, PagerComponent, Queries, Query, QuerySynchronizer, Router2State, SchemaDto, SchemasService, SchemasState, SearchFormComponent, Settings, ShortcutDirective, SidebarMenuDirective, Subscriptions, switchSafe, SyncWidthDirective, TableSettings, TempService, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, TranslationStatus, UIState } from '@app/shared'; +import { DueTimeSelectorComponent } from '../../shared/due-time-selector.component'; +import { ContentComponent } from '../../shared/list/content.component'; +import { CustomViewEditorComponent } from './custom-view-editor.component'; @Component({ + standalone: true, selector: 'sqx-contents-page', styleUrls: ['./contents-page.component.scss'], templateUrl: './contents-page.component.html', providers: [ Router2State, ], + imports: [ + AsyncPipe, + ConfirmClickDirective, + ContentComponent, + ContentListCellDirective, + ContentListCellResizeDirective, + ContentListHeaderComponent, + ContentListWidthDirective, + ContentStatusComponent, + CustomViewEditorComponent, + DropdownMenuComponent, + DueTimeSelectorComponent, + FormsModule, + KeysPipe, + LanguageSelectorComponent, + LayoutComponent, + ListViewComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + NotifoComponent, + PagerComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + SearchFormComponent, + ShortcutDirective, + SidebarMenuDirective, + SyncWidthDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class ContentsPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/pages/contents/custom-view-editor.component.ts b/frontend/src/app/features/content/pages/contents/custom-view-editor.component.ts index 77ab587f2..af493dae2 100644 --- a/frontend/src/app/features/content/pages/contents/custom-view-editor.component.ts +++ b/frontend/src/app/features/content/pages/contents/custom-view-editor.component.ts @@ -5,15 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { TableField } from '@app/shared'; +import { TableField, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-custom-view-editor', styleUrls: ['./custom-view-editor.component.scss'], templateUrl: './custom-view-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CdkDrag, + CdkDropList, + NgFor, + NgIf, + TranslatePipe, + ], }) export class CustomViewEditorComponent { @Output() @@ -55,4 +64,4 @@ export class CustomViewEditorComponent { public resetDefault() { this.listFieldsReset.emit(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/content/pages/references/references-page.component.ts b/frontend/src/app/features/content/pages/references/references-page.component.ts index 29248f64b..2fe21f6e0 100644 --- a/frontend/src/app/features/content/pages/references/references-page.component.ts +++ b/frontend/src/app/features/content/pages/references/references-page.component.ts @@ -5,18 +5,37 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { distinctUntilChanged, map } from 'rxjs/operators'; -import { AppLanguageDto, ComponentContentsState, ContentDto, LanguagesState, QuerySynchronizer, Router2State, Subscriptions } from '@app/shared'; +import { AppLanguageDto, ComponentContentsState, ContentDto, ContentsColumnsPipe, LanguageSelectorComponent, LanguagesState, LayoutComponent, ListViewComponent, PagerComponent, QuerySynchronizer, Router2State, ShortcutDirective, Subscriptions, TitleComponent, TooltipDirective, TranslatePipe } from '@app/shared'; +import { ReferenceItemComponent } from '../../shared/references/reference-item.component'; @Component({ + standalone: true, selector: 'sqx-references-page', styleUrls: ['./references-page.component.scss'], templateUrl: './references-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ - Router2State, ComponentContentsState, + ComponentContentsState, + Router2State, + ], + imports: [ + AsyncPipe, + ContentsColumnsPipe, + LanguageSelectorComponent, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + PagerComponent, + ReferenceItemComponent, + ShortcutDirective, + TitleComponent, + TooltipDirective, + TranslatePipe, ], }) export class ReferencesPageComponent implements OnInit { @@ -76,4 +95,4 @@ export class ReferencesPageComponent implements OnInit { function getReferenceId(route: ActivatedRoute) { return route.params.pipe(map(x => x['referenceId'] as string), distinctUntilChanged()); -} \ No newline at end of file +} diff --git a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts index 7b1502e17..13b15fcc2 100644 --- a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts +++ b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts @@ -5,16 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, inject } from '@angular/core'; -import { UntypedFormControl } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppsState, getCategoryTree, SchemaCategory, SchemasState, Settings, UIOptions, value$ } from '@app/shared'; +import { AppsState, getCategoryTree, LayoutComponent, SchemaCategory, SchemaCategoryComponent, SchemasState, Settings, TitleComponent, TranslatePipe, UIOptions, value$ } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schemas-page', styleUrls: ['./schemas-page.component.scss'], templateUrl: './schemas-page.component.html', + imports: [ + AsyncPipe, + FormsModule, + LayoutComponent, + NgFor, + NgIf, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + SchemaCategoryComponent, + TitleComponent, + TranslatePipe, + ], }) export class SchemasPageComponent { public schemasFilter = new UntypedFormControl(); diff --git a/frontend/src/app/features/content/pages/sidebar/sidebar-page.component.ts b/frontend/src/app/features/content/pages/sidebar/sidebar-page.component.ts index d13f8d2b8..c7153a2c8 100644 --- a/frontend/src/app/features/content/pages/sidebar/sidebar-page.component.ts +++ b/frontend/src/app/features/content/pages/sidebar/sidebar-page.component.ts @@ -5,17 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { defined } from '@app/framework/internal'; -import { ContentsState, SchemasState } from '@app/shared'; +import { ContentsState, defined, LayoutComponent, SchemasState } from '@app/shared'; +import { ContentExtensionComponent } from '../../shared/content-extension.component'; @Component({ + standalone: true, selector: 'sqx-sidebar-page', styleUrls: ['./sidebar-page.component.scss'], templateUrl: './sidebar-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ContentExtensionComponent, + LayoutComponent, + ], }) export class SidebarPageComponent { public url = combineLatest([ diff --git a/frontend/src/app/features/content/routes.ts b/frontend/src/app/features/content/routes.ts new file mode 100644 index 000000000..fc8968e06 --- /dev/null +++ b/frontend/src/app/features/content/routes.ts @@ -0,0 +1,92 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { canDeactivateGuard, contentMustExistGuard, loadLanguagesGuard, loadSchemasGuard, schemaMustExistPublishedGuard, schemaMustNotBeSingletonGuard } from '@app/shared'; +import { CalendarPageComponent } from './pages/calendar/calendar-page.component'; +import { CommentsPageComponent } from './pages/comments/comments-page.component'; +import { ContentHistoryPageComponent } from './pages/content/content-history-page.component'; +import { ContentPageComponent } from './pages/content/content-page.component'; +import { ContentsFiltersPageComponent } from './pages/contents/contents-filters-page.component'; +import { ContentsPageComponent } from './pages/contents/contents-page.component'; +import { ReferencesPageComponent } from './pages/references/references-page.component'; +import { SchemasPageComponent } from './pages/schemas/schemas-page.component'; +import { SidebarPageComponent } from './pages/sidebar/sidebar-page.component'; + +export const CONTENT_ROUTES: Routes = [ + { + path: '', + component: SchemasPageComponent, + canActivate: [loadLanguagesGuard, loadSchemasGuard], + children: [ + { + path: '__calendar', + component: CalendarPageComponent, + }, + { + path: '__references/:referenceId', + component: ReferencesPageComponent, + }, + { + path: ':schemaName', + canActivate: [schemaMustExistPublishedGuard], + children: [ + { + path: '', + component: ContentsPageComponent, + canActivate: [schemaMustNotBeSingletonGuard, contentMustExistGuard], + canDeactivate: [canDeactivateGuard], + children: [ + { + path: 'filters', + component: ContentsFiltersPageComponent, + }, + { + path: 'sidebar', + component: SidebarPageComponent, + }, + ], + }, + { + path: 'new', + component: ContentPageComponent, + canActivate: [schemaMustNotBeSingletonGuard, contentMustExistGuard], + canDeactivate: [canDeactivateGuard], + data: { + reuseId: 'contentPage', + }, + }, + { + path: ':contentId', + component: ContentPageComponent, + canActivate: [contentMustExistGuard], + canDeactivate: [canDeactivateGuard], + data: { + reuseId: 'contentPage', + }, + children: [ + { + path: 'history', + component: ContentHistoryPageComponent, + data: { + channel: 'contents.{contentId}', + }, + }, + { + path: 'comments', + component: CommentsPageComponent, + }, + { + path: 'sidebar', + component: SidebarPageComponent, + }, + ], + }, + ], + }], + }, +]; \ No newline at end of file diff --git a/frontend/src/app/features/content/shared/content-extension.component.ts b/frontend/src/app/features/content/shared/content-extension.component.ts index f0b42784a..042a6318b 100644 --- a/frontend/src/app/features/content/shared/content-extension.component.ts +++ b/frontend/src/app/features/content/shared/content-extension.component.ts @@ -7,14 +7,17 @@ import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; -import { ApiUrlConfig, Types } from '@app/framework/internal'; -import { AppsState, AuthService, computeEditorUrl, ContentDto, SchemaDto, TypedSimpleChanges } from '@app/shared'; +import { ApiUrlConfig, AppsState, AuthService, computeEditorUrl, ContentDto, SafeResourceUrlPipe, SchemaDto, TypedSimpleChanges, Types } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-content-extension', styleUrls: ['./content-extension.component.scss'], templateUrl: './content-extension.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + SafeResourceUrlPipe, + ], }) export class ContentExtensionComponent { private readonly context: any; diff --git a/frontend/src/app/features/content/shared/due-time-selector.component.ts b/frontend/src/app/features/content/shared/due-time-selector.component.ts index c00264d2a..e1eadaf1d 100644 --- a/frontend/src/app/features/content/shared/due-time-selector.component.ts +++ b/frontend/src/app/features/content/shared/due-time-selector.component.ts @@ -6,15 +6,26 @@ */ import { booleanAttribute, Component, Input } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { Observable, of, Subject } from 'rxjs'; -import { DialogModel } from '@app/shared'; +import { DateTimeEditorComponent, DialogModel, FocusOnInitDirective, ModalDialogComponent, ModalDirective, TooltipDirective, TranslatePipe } from '@app/shared'; const OPTION_IMMEDIATELY = 'Immediately'; @Component({ + standalone: true, selector: 'sqx-due-time-selector', styleUrls: ['./due-time-selector.component.scss'], templateUrl: './due-time-selector.component.html', + imports: [ + DateTimeEditorComponent, + FocusOnInitDirective, + FormsModule, + ModalDialogComponent, + ModalDirective, + TooltipDirective, + TranslatePipe, + ], }) export class DueTimeSelectorComponent { private dueTimeResult?: Subject<string | null>; diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.ts b/frontend/src/app/features/content/shared/forms/array-editor.component.ts index 5272f2edc..c7e8608e5 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.ts @@ -5,19 +5,38 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input, numberAttribute, QueryList, ViewChildren } from '@angular/core'; -import { VirtualScrollerComponent } from '@iharbeck/ngx-virtual-scroller'; +import { VirtualScrollerComponent, VirtualScrollerModule } from '@iharbeck/ngx-virtual-scroller'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppLanguageDto, ComponentsFieldPropertiesDto, disabled$, EditContentForm, FieldArrayForm, LocalStoreService, ModalModel, ObjectFormBase, SchemaDto, Settings, sorted, TypedSimpleChanges, Types } from '@app/shared'; +import { AppLanguageDto, ComponentsFieldPropertiesDto, ConfirmClickDirective, disabled$, DropdownMenuComponent, EditContentForm, FieldArrayForm, FormHintComponent, LocalStoreService, ModalDirective, ModalModel, ModalPlacementDirective, ObjectFormBase, SchemaDto, Settings, sorted, TooltipDirective, TranslatePipe, TypedSimpleChanges, Types } from '@app/shared'; import { ArrayItemComponent } from './array-item.component'; @Component({ + standalone: true, selector: 'sqx-array-editor', styleUrls: ['./array-editor.component.scss'], templateUrl: './array-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ArrayItemComponent, + AsyncPipe, + CdkDrag, + CdkDragHandle, + CdkDropList, + ConfirmClickDirective, + DropdownMenuComponent, + FormHintComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + VirtualScrollerModule, + ], }) export class ArrayEditorComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/content/shared/forms/array-item.component.ts b/frontend/src/app/features/content/shared/forms/array-item.component.ts index 2043fcee1..8a1dbf7b4 100644 --- a/frontend/src/app/features/content/shared/forms/array-item.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-item.component.ts @@ -5,17 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output, QueryList, ViewChildren } from '@angular/core'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppLanguageDto, ComponentForm, EditContentForm, FieldDto, FieldFormatter, FieldSection, invalid$, ObjectFormBase, RootFieldDto, TypedSimpleChanges, Types, valueProjection$ } from '@app/shared'; +import { AppLanguageDto, ComponentForm, EditContentForm, FieldDto, FieldFormatter, FieldSection, FormHintComponent, IfOnceDirective, invalid$, ObjectFormBase, RootFieldDto, TooltipDirective, TranslatePipe, TypedSimpleChanges, Types, valueProjection$ } from '@app/shared'; import { ComponentSectionComponent } from './component-section.component'; @Component({ + standalone: true, selector: 'sqx-array-item', styleUrls: ['./array-item.component.scss'], templateUrl: './array-item.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ComponentSectionComponent, + FormHintComponent, + IfOnceDirective, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class ArrayItemComponent { @Output() diff --git a/frontend/src/app/features/content/shared/forms/assets-editor.component.ts b/frontend/src/app/features/content/shared/forms/assets-editor.component.ts index 55916b639..52ee4bc6c 100644 --- a/frontend/src/app/features/content/shared/forms/assets-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/assets-editor.component.ts @@ -5,10 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input, OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { AssetDto, DialogModel, LocalStoreService, MessageBus, ResolveAssets, Settings, sorted, StatefulControlComponent, Subscriptions, Types } from '@app/shared'; +import { AssetComponent, AssetDialogComponent, AssetDto, AssetSelectorComponent, DialogModel, FileDropDirective, LocalStoreService, MessageBus, ModalDirective, ResizedDirective, ResolveAssets, Settings, sorted, StatefulControlComponent, Subscriptions, TranslatePipe, Types } from '@app/shared'; export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AssetsEditorComponent), multi: true, @@ -40,6 +41,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-assets-editor', styleUrls: ['./assets-editor.component.scss'], templateUrl: './assets-editor.component.html', @@ -47,6 +49,19 @@ interface State { SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetComponent, + AssetDialogComponent, + AssetSelectorComponent, + CdkDrag, + CdkDropList, + FileDropDirective, + ModalDirective, + NgFor, + NgIf, + ResizedDirective, + TranslatePipe, + ], }) export class AssetsEditorComponent extends StatefulControlComponent<State, ReadonlyArray<string>> implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/shared/forms/component-section.component.ts b/frontend/src/app/features/content/shared/forms/component-section.component.ts index 4089067a7..ae1bf2cf3 100644 --- a/frontend/src/app/features/content/shared/forms/component-section.component.ts +++ b/frontend/src/app/features/content/shared/forms/component-section.component.ts @@ -5,15 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, ChangeDetectionStrategy, Component, Input, numberAttribute, QueryList, ViewChildren } from '@angular/core'; -import { AbstractContentForm, AppLanguageDto, EditContentForm, FieldDto, FieldSection } from '@app/shared'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; +import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input, numberAttribute, QueryList, ViewChildren } from '@angular/core'; +import { AbstractContentForm, AppLanguageDto, EditContentForm, FieldDto, FieldSection, FormHintComponent, MarkdownDirective } from '@app/shared'; import { FieldEditorComponent } from './field-editor.component'; @Component({ + standalone: true, selector: 'sqx-component-section', styleUrls: ['./component-section.component.scss'], templateUrl: './component-section.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + FormHintComponent, + MarkdownDirective, + NgFor, + NgIf, + forwardRef(() => FieldEditorComponent), + ], }) export class ComponentSectionComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/content/shared/forms/component.component.ts b/frontend/src/app/features/content/shared/forms/component.component.ts index 954252b80..339647a3b 100644 --- a/frontend/src/app/features/content/shared/forms/component.component.ts +++ b/frontend/src/app/features/content/shared/forms/component.component.ts @@ -5,16 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, numberAttribute, QueryList, ViewChildren } from '@angular/core'; import { Observable } from 'rxjs'; -import { AppLanguageDto, ComponentFieldPropertiesDto, ComponentForm, disabled$, EditContentForm, FieldDto, FieldSection, ModalModel, SchemaDto, Subscriptions, TypedSimpleChanges, Types } from '@app/shared'; +import { AppLanguageDto, ComponentFieldPropertiesDto, ComponentForm, disabled$, DropdownMenuComponent, EditContentForm, FieldDto, FieldSection, FormHintComponent, ModalDirective, ModalModel, ModalPlacementDirective, SchemaDto, Subscriptions, TranslatePipe, TypedSimpleChanges, Types } from '@app/shared'; import { ComponentSectionComponent } from './component-section.component'; @Component({ + standalone: true, selector: 'sqx-component', styleUrls: ['./component.component.scss'], templateUrl: './component.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ComponentSectionComponent, + DropdownMenuComponent, + FormHintComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TranslatePipe, + ], }) export class ComponentComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.html b/frontend/src/app/features/content/shared/forms/content-field.component.html similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/content-field.component.html rename to frontend/src/app/features/content/shared/forms/content-field.component.html diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.scss b/frontend/src/app/features/content/shared/forms/content-field.component.scss similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/content-field.component.scss rename to frontend/src/app/features/content/shared/forms/content-field.component.scss diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts b/frontend/src/app/features/content/shared/forms/content-field.component.ts similarity index 88% rename from frontend/src/app/features/content/pages/content/editor/content-field.component.ts rename to frontend/src/app/features/content/shared/forms/content-field.component.ts index de4754e9e..9c8b59535 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts +++ b/frontend/src/app/features/content/shared/forms/content-field.component.ts @@ -5,14 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, HostBinding, inject, Input, numberAttribute, Output } from '@angular/core'; import { Observable } from 'rxjs'; -import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared'; +import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, FocusMarkerComponent, invalid$, LocalStoreService, SchemaDto, Settings, TooltipDirective, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared'; +import { FieldCopyButtonComponent } from './field-copy-button.component'; +import { FieldEditorComponent } from './field-editor.component'; +import { FieldLanguagesComponent } from './field-languages.component'; @Component({ + standalone: true, selector: 'sqx-content-field', styleUrls: ['./content-field.component.scss'], templateUrl: './content-field.component.html', + imports: [ + AsyncPipe, + FieldCopyButtonComponent, + FieldEditorComponent, + FieldLanguagesComponent, + FocusMarkerComponent, + NgFor, + NgIf, + TooltipDirective, + ], }) export class ContentFieldComponent { @Output() diff --git a/frontend/src/app/features/content/pages/content/editor/content-section.component.html b/frontend/src/app/features/content/shared/forms/content-section.component.html similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/content-section.component.html rename to frontend/src/app/features/content/shared/forms/content-section.component.html diff --git a/frontend/src/app/features/content/pages/content/editor/content-section.component.scss b/frontend/src/app/features/content/shared/forms/content-section.component.scss similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/content-section.component.scss rename to frontend/src/app/features/content/shared/forms/content-section.component.scss diff --git a/frontend/src/app/features/content/pages/content/editor/content-section.component.ts b/frontend/src/app/features/content/shared/forms/content-section.component.ts similarity index 82% rename from frontend/src/app/features/content/pages/content/editor/content-section.component.ts rename to frontend/src/app/features/content/shared/forms/content-section.component.ts index 12844ea76..211d15ebd 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-section.component.ts +++ b/frontend/src/app/features/content/shared/forms/content-section.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; -import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, LocalStoreService, RootFieldDto, SchemaDto, Settings, StatefulComponent, TypedSimpleChanges } from '@app/shared'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; +import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, numberAttribute, Output } from '@angular/core'; +import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, FormHintComponent, LocalStoreService, MarkdownDirective, RootFieldDto, SchemaDto, Settings, StatefulComponent, TypedSimpleChanges } from '@app/shared'; +import { ContentFieldComponent } from './content-field.component'; interface State { // The when the section is collapsed. @@ -14,10 +16,19 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-content-section', styleUrls: ['./content-section.component.scss'], templateUrl: './content-section.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + FormHintComponent, + MarkdownDirective, + NgFor, + NgIf, + forwardRef(() => ContentFieldComponent), + ], }) export class ContentSectionComponent extends StatefulComponent<State> { @Output() diff --git a/frontend/src/app/features/content/pages/content/editor/field-copy-button.component.html b/frontend/src/app/features/content/shared/forms/field-copy-button.component.html similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/field-copy-button.component.html rename to frontend/src/app/features/content/shared/forms/field-copy-button.component.html diff --git a/frontend/src/app/features/content/pages/content/editor/field-copy-button.component.scss b/frontend/src/app/features/content/shared/forms/field-copy-button.component.scss similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/field-copy-button.component.scss rename to frontend/src/app/features/content/shared/forms/field-copy-button.component.scss diff --git a/frontend/src/app/features/content/pages/content/editor/field-copy-button.component.ts b/frontend/src/app/features/content/shared/forms/field-copy-button.component.ts similarity index 73% rename from frontend/src/app/features/content/pages/content/editor/field-copy-button.component.ts rename to frontend/src/app/features/content/shared/forms/field-copy-button.component.ts index 99ffd05d8..4c655a416 100644 --- a/frontend/src/app/features/content/pages/content/editor/field-copy-button.component.ts +++ b/frontend/src/app/features/content/shared/forms/field-copy-button.component.ts @@ -5,13 +5,27 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { AppLanguageDto, FieldForm, ModalModel, TypedSimpleChanges } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { AppLanguageDto, CheckboxGroupComponent, DropdownMenuComponent, FieldForm, ModalDirective, ModalModel, ModalPlacementDirective, TooltipDirective, TranslatePipe, TypedSimpleChanges } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-field-copy-button', styleUrls: ['./field-copy-button.component.scss'], templateUrl: './field-copy-button.component.html', + imports: [ + CheckboxGroupComponent, + DropdownMenuComponent, + FormsModule, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class FieldCopyButtonComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/content/shared/forms/field-editor.component.ts b/frontend/src/app/features/content/shared/forms/field-editor.component.ts index 3cd6c2358..08184c57e 100644 --- a/frontend/src/app/features/content/shared/forms/field-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/field-editor.component.ts @@ -5,15 +5,64 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { booleanAttribute, Component, ElementRef, EventEmitter, Input, numberAttribute, Output, ViewChild } from '@angular/core'; -import { AbstractControl } from '@angular/forms'; +import { AbstractControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable } from 'rxjs'; -import { AbstractContentForm, AppLanguageDto, DialogModel, EditContentForm, FieldDto, hasNoValue$, MathHelper, TypedSimpleChanges, Types } from '@app/shared'; +import { AbstractContentForm, AppLanguageDto, ChatDialogComponent, CheckboxGroupComponent, CodeEditorComponent, ColorPickerComponent, ConfirmClickDirective, ControlErrorsComponent, DateTimeEditorComponent, DialogModel, EditContentForm, FieldDto, FormHintComponent, GeolocationEditorComponent, hasNoValue$, IndeterminateValueDirective, MarkdownDirective, MathHelper, ModalDirective, RadioGroupComponent, ReferenceInputComponent, RichEditorComponent, StarsComponent, TagEditorComponent, ToggleComponent, TooltipDirective, TransformInputDirective, TypedSimpleChanges, Types } from '@app/shared'; +import { ReferenceDropdownComponent } from '../references/reference-dropdown.component'; +import { ReferencesCheckboxesComponent } from '../references/references-checkboxes.component'; +import { ReferencesEditorComponent } from '../references/references-editor.component'; +import { ReferencesTagsComponent } from '../references/references-tags.component'; +import { ArrayEditorComponent } from './array-editor.component'; +import { AssetsEditorComponent } from './assets-editor.component'; +import { ComponentComponent } from './component.component'; +import { IFrameEditorComponent } from './iframe-editor.component'; +import { StockPhotoEditorComponent } from './stock-photo-editor.component'; @Component({ + standalone: true, selector: 'sqx-field-editor', styleUrls: ['./field-editor.component.scss'], templateUrl: './field-editor.component.html', + imports: [ + ArrayEditorComponent, + AssetsEditorComponent, + AsyncPipe, + ChatDialogComponent, + CheckboxGroupComponent, + CodeEditorComponent, + ColorPickerComponent, + ComponentComponent, + ConfirmClickDirective, + ControlErrorsComponent, + DateTimeEditorComponent, + FormHintComponent, + FormsModule, + GeolocationEditorComponent, + IFrameEditorComponent, + IndeterminateValueDirective, + MarkdownDirective, + ModalDirective, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + RadioGroupComponent, + ReactiveFormsModule, + ReferenceDropdownComponent, + ReferenceInputComponent, + ReferencesCheckboxesComponent, + ReferencesEditorComponent, + ReferencesTagsComponent, + RichEditorComponent, + StarsComponent, + StockPhotoEditorComponent, + TagEditorComponent, + ToggleComponent, + TooltipDirective, + TransformInputDirective, + ], }) export class FieldEditorComponent { public readonly uniqueId = MathHelper.guid(); diff --git a/frontend/src/app/features/content/pages/content/editor/field-languages.component.html b/frontend/src/app/features/content/shared/forms/field-languages.component.html similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/field-languages.component.html rename to frontend/src/app/features/content/shared/forms/field-languages.component.html diff --git a/frontend/src/app/features/content/pages/content/editor/field-languages.component.scss b/frontend/src/app/features/content/shared/forms/field-languages.component.scss similarity index 100% rename from frontend/src/app/features/content/pages/content/editor/field-languages.component.scss rename to frontend/src/app/features/content/shared/forms/field-languages.component.scss diff --git a/frontend/src/app/features/content/pages/content/editor/field-languages.component.ts b/frontend/src/app/features/content/shared/forms/field-languages.component.ts similarity index 73% rename from frontend/src/app/features/content/pages/content/editor/field-languages.component.ts rename to frontend/src/app/features/content/shared/forms/field-languages.component.ts index 7249a1a0d..02b2a5bab 100644 --- a/frontend/src/app/features/content/pages/content/editor/field-languages.component.ts +++ b/frontend/src/app/features/content/shared/forms/field-languages.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { AppLanguageDto, FieldForm } from '@app/shared'; +import { AppLanguageDto, FieldForm, LanguageSelectorComponent, TourHintDirective, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-field-languages', styleUrls: ['./field-languages.component.scss'], templateUrl: './field-languages.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + LanguageSelectorComponent, + NgIf, + TourHintDirective, + TourStepDirective, + TranslatePipe, + ], }) export class FieldLanguagesComponent { @Output() diff --git a/frontend/src/app/features/content/shared/forms/iframe-editor.component.ts b/frontend/src/app/features/content/shared/forms/iframe-editor.component.ts index a362ca54a..996946967 100644 --- a/frontend/src/app/features/content/shared/forms/iframe-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/iframe-editor.component.ts @@ -8,8 +8,7 @@ import { booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Input, numberAttribute, OnDestroy, Output, Renderer2, ViewChild } from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { Router } from '@angular/router'; -import { DialogModel, DialogService, disabled$, StatefulComponent, Subscriptions, TypedSimpleChanges, Types, value$ } from '@app/framework'; -import { AppLanguageDto, AppsState, AssetDto, computeEditorUrl, ContentDto } from '@app/shared'; +import { AppLanguageDto, AppsState, AssetDto, AssetSelectorComponent, computeEditorUrl, ContentDto, ContentSelectorComponent, DialogModel, DialogService, disabled$, ModalDirective, SafeResourceUrlPipe, StatefulComponent, Subscriptions, TypedSimpleChanges, Types, value$ } from '@app/shared'; interface State { // True, when the editor is shown as fullscreen. @@ -17,10 +16,17 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-iframe-editor', styleUrls: ['./iframe-editor.component.scss'], templateUrl: './iframe-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetSelectorComponent, + ContentSelectorComponent, + ModalDirective, + SafeResourceUrlPipe, + ], }) export class IFrameEditorComponent extends StatefulComponent<State> implements OnDestroy { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.ts b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.ts index 4fcbbf9b1..91538c424 100644 --- a/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/stock-photo-editor.component.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input, OnInit } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { BehaviorSubject, of } from 'rxjs'; import { debounceTime, map, switchMap, tap } from 'rxjs/operators'; -import { DialogModel, StatefulControlComponent, StockPhotoDto, StockPhotoService, Subscriptions, thumbnail, Types, value$, valueProjection$ } from '@app/shared'; +import { DialogModel, ExternalLinkDirective, FocusOnInitDirective, LoaderComponent, ModalDialogComponent, ModalDirective, StatefulControlComponent, StockPhotoDto, StockPhotoService, StopClickDirective, Subscriptions, thumbnail, TooltipDirective, TranslatePipe, Types, value$, valueProjection$ } from '@app/shared'; export const SQX_STOCK_PHOTO_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StockPhotoEditorComponent), multi: true, @@ -32,6 +33,7 @@ interface State { type Request = { search?: string; page: number }; @Component({ + standalone: true, selector: 'sqx-stock-photo-editor', styleUrls: ['./stock-photo-editor.component.scss'], templateUrl: './stock-photo-editor.component.html', @@ -39,6 +41,21 @@ type Request = { search?: string; page: number }; SQX_STOCK_PHOTO_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ExternalLinkDirective, + FocusOnInitDirective, + FormsModule, + LoaderComponent, + ModalDialogComponent, + ModalDirective, + NgFor, + NgIf, + ReactiveFormsModule, + StopClickDirective, + TooltipDirective, + TranslatePipe, + ], }) export class StockPhotoEditorComponent extends StatefulControlComponent<State, string> implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/shared/list/content.component.ts b/frontend/src/app/features/content/shared/list/content.component.ts index 49a7655c2..a553423fa 100644 --- a/frontend/src/app/features/content/shared/list/content.component.ts +++ b/frontend/src/app/features/content/shared/list/content.component.ts @@ -8,14 +8,33 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, QueryList, ViewChildren } from '@angular/core'; -import { AppLanguageDto, ContentDto, ContentListFieldComponent, ContentsState, ModalModel, PatchContentForm, SchemaDto, TableField, TableSettings, TypedSimpleChanges } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { AppLanguageDto, ConfirmClickDirective, ContentDto, ContentListCellDirective, ContentListCellResizeDirective, ContentListFieldComponent, ContentsState, ContentStatusComponent, DropdownMenuComponent, ModalDirective, ModalModel, ModalPlacementDirective, PatchContentForm, SchemaDto, StopClickDirective, TableField, TableSettings, TabRouterlinkDirective, TranslatePipe, TypedSimpleChanges } from '@app/shared'; @Component({ + standalone: true, selector: '[sqxContent][language][languages][tableFields][schema][tableSettings]', styleUrls: ['./content.component.scss'], templateUrl: './content.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + ContentListCellDirective, + ContentListCellResizeDirective, + ContentListFieldComponent, + ContentStatusComponent, + DropdownMenuComponent, + FormsModule, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + StopClickDirective, + TabRouterlinkDirective, + TranslatePipe, + ], }) export class ContentComponent { @Output() diff --git a/frontend/src/app/features/content/shared/preview-button.component.ts b/frontend/src/app/features/content/shared/preview-button.component.ts index 461a0e213..2c8180f1b 100644 --- a/frontend/src/app/features/content/shared/preview-button.component.ts +++ b/frontend/src/app/features/content/shared/preview-button.component.ts @@ -5,10 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; -import { AuthService, ContentDto, interpolate, LocalStoreService, ModalModel, SchemaDto, Settings, StatefulComponent } from '@app/shared'; +import { AuthService, ContentDto, DropdownMenuComponent, interpolate, LocalStoreService, ModalDirective, ModalModel, ModalPlacementDirective, SchemaDto, Settings, StatefulComponent, TranslatePipe } from '@app/shared'; interface State { // The name of the selected preview config. @@ -19,10 +20,19 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-preview-button', styleUrls: ['./preview-button.component.scss'], templateUrl: './preview-button.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TranslatePipe, + ], }) export class PreviewButtonComponent extends StatefulComponent<State> implements OnInit { @Input() diff --git a/frontend/src/app/features/content/shared/references/content-creator.component.ts b/frontend/src/app/features/content/shared/references/content-creator.component.ts index 7cc1e7a98..a9430a184 100644 --- a/frontend/src/app/features/content/shared/references/content-creator.component.ts +++ b/frontend/src/app/features/content/shared/references/content-creator.component.ts @@ -5,10 +5,14 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AppLanguageDto, ComponentContentsState, ContentDto, EditContentForm, ResolveAssets, ResolveContents, SchemaDto, SchemasState } from '@app/shared'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AppLanguageDto, ComponentContentsState, ContentDto, EditContentForm, FormErrorComponent, LanguageSelectorComponent, ModalDialogComponent, ResolveAssets, ResolveContents, SchemaDto, SchemasState, TooltipDirective, TranslatePipe } from '@app/shared'; +import { ContentSectionComponent } from '../forms/content-section.component'; @Component({ + standalone: true, selector: 'sqx-content-creator', styleUrls: ['./content-creator.component.scss'], templateUrl: './content-creator.component.html', @@ -17,6 +21,19 @@ import { AppLanguageDto, ComponentContentsState, ContentDto, EditContentForm, Re ResolveContents, ComponentContentsState, ], + imports: [ + AsyncPipe, + FormErrorComponent, + FormsModule, + LanguageSelectorComponent, + ModalDialogComponent, + NgFor, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TranslatePipe, + forwardRef(() => ContentSectionComponent), + ], }) export class ContentCreatorComponent implements OnInit { @Output() diff --git a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts index 088d2b9c6..1e3b7acf0 100644 --- a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts +++ b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts @@ -6,10 +6,9 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { Observable } from 'rxjs'; -import { ContentsDto } from '@app/shared'; -import { ContentDto, getContentValue, LanguageDto, LocalizerService, ResolveContents, StatefulControlComponent, Subscriptions, TypedSimpleChanges, Types, value$ } from '@app/shared/internal'; +import { ContentDto, ContentsDto, DropdownComponent, getContentValue, HighlightPipe, LanguageDto, LocalizerService, ResolveContents, SafeHtmlPipe, StatefulControlComponent, Subscriptions, TypedSimpleChanges, Types, value$ } from '@app/shared'; export const SQX_REFERENCE_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferenceDropdownComponent), multi: true, @@ -31,6 +30,7 @@ type ContentName = { name: string; id?: string }; const NO_EMIT = { emitEvent: false }; @Component({ + standalone: true, selector: 'sqx-reference-dropdown', styleUrls: ['./reference-dropdown.component.scss'], templateUrl: './reference-dropdown.component.html', @@ -38,6 +38,13 @@ const NO_EMIT = { emitEvent: false }; SQX_REFERENCE_DROPDOWN_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownComponent, + FormsModule, + HighlightPipe, + ReactiveFormsModule, + SafeHtmlPipe, + ], }) export class ReferenceDropdownComponent extends StatefulControlComponent<State, ReadonlyArray<string> | string> { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/shared/references/reference-item.component.ts b/frontend/src/app/features/content/shared/references/reference-item.component.ts index 0928ad436..64ae82695 100644 --- a/frontend/src/app/features/content/shared/references/reference-item.component.ts +++ b/frontend/src/app/features/content/shared/references/reference-item.component.ts @@ -7,14 +7,28 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; -import { ContentDto, getContentValue, LanguageDto, META_FIELDS } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { ConfirmClickDirective, ContentDto, ContentListCellDirective, ContentListFieldComponent, ContentValueComponent, getContentValue, LanguageDto, META_FIELDS, TooltipDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: '[sqxReferenceItem][language][languages]', styleUrls: ['./reference-item.component.scss'], templateUrl: './reference-item.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + ContentListCellDirective, + ContentListFieldComponent, + ContentValueComponent, + NgFor, + NgIf, + RouterLink, + TooltipDirective, + TranslatePipe, + ], }) export class ReferenceItemComponent { public readonly metaFields = META_FIELDS; diff --git a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts index bcc4eba1c..c424f9bbd 100644 --- a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts +++ b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts @@ -6,7 +6,8 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, inject, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { CheckboxGroupComponent } from '@app/shared'; import { AppsState, ContentDto, ContentsService, LanguageDto, LocalizerService, StatefulControlComponent, Subscriptions, TypedSimpleChanges, UIOptions } from '@app/shared/internal'; import { ReferencesTagsConverter } from './references-tag-converter'; @@ -22,6 +23,7 @@ interface State { const NO_EMIT = { emitEvent: false }; @Component({ + standalone: true, selector: 'sqx-references-checkboxes', styleUrls: ['./references-checkboxes.component.scss'], templateUrl: './references-checkboxes.component.html', @@ -29,6 +31,11 @@ const NO_EMIT = { emitEvent: false }; SQX_REFERENCES_CHECKBOXES_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CheckboxGroupComponent, + FormsModule, + ReactiveFormsModule, + ], }) export class ReferencesCheckboxesComponent extends StatefulControlComponent<State, ReadonlyArray<string>> { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/content/shared/references/references-editor.component.ts b/frontend/src/app/features/content/shared/references/references-editor.component.ts index 31a1b902d..bfd2379bb 100644 --- a/frontend/src/app/features/content/shared/references/references-editor.component.ts +++ b/frontend/src/app/features/content/shared/references/references-editor.component.ts @@ -5,10 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { AppLanguageDto, ContentDto, DialogModel, ResolveContents, sorted, StatefulControlComponent, Types } from '@app/shared'; +import { AppLanguageDto, ContentDto, ContentsColumnsPipe, ContentSelectorComponent, DialogModel, ModalDirective, ResizedDirective, ResolveContents, sorted, StatefulControlComponent, TranslatePipe, Types } from '@app/shared'; +import { ContentCreatorComponent } from './content-creator.component'; +import { ReferenceItemComponent } from './reference-item.component'; export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferencesEditorComponent), multi: true, @@ -23,6 +26,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-references-editor', styleUrls: ['./references-editor.component.scss'], templateUrl: './references-editor.component.html', @@ -30,6 +34,20 @@ interface State { SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CdkDrag, + CdkDragHandle, + CdkDropList, + ContentSelectorComponent, + ContentsColumnsPipe, + ModalDirective, + NgFor, + NgIf, + ReferenceItemComponent, + ResizedDirective, + TranslatePipe, + forwardRef(() => ContentCreatorComponent), + ], }) export class ReferencesEditorComponent extends StatefulControlComponent<State, ReadonlyArray<string>> { @Input({ required: true }) diff --git a/frontend/src/app/features/content/shared/references/references-tags.component.ts b/frontend/src/app/features/content/shared/references/references-tags.component.ts index 0f20b3a87..e097a95c9 100644 --- a/frontend/src/app/features/content/shared/references/references-tags.component.ts +++ b/frontend/src/app/features/content/shared/references/references-tags.component.ts @@ -6,10 +6,9 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { Observable } from 'rxjs'; -import { Subscriptions, TypedSimpleChanges, Types } from '@app/framework'; -import { ContentDto, ContentsDto, LanguageDto, LocalizerService, ResolveContents, StatefulControlComponent } from '@app/shared/internal'; +import { ContentDto, ContentsDto, LanguageDto, LocalizerService, ResolveContents, StatefulControlComponent, Subscriptions, TagEditorComponent, TranslatePipe, TypedSimpleChanges, Types } from '@app/shared'; import { ReferencesTagsConverter } from './references-tag-converter'; export const SQX_REFERENCES_TAGS_CONTROL_VALUE_ACCESSOR: any = { @@ -27,6 +26,7 @@ interface State { const NO_EMIT = { emitEvent: false }; @Component({ + standalone: true, selector: 'sqx-references-tags', styleUrls: ['./references-tags.component.scss'], templateUrl: './references-tags.component.html', @@ -34,6 +34,12 @@ const NO_EMIT = { emitEvent: false }; SQX_REFERENCES_TAGS_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class ReferencesTagsComponent extends StatefulControlComponent<State, ReadonlyArray<string>> { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/dashboard/declarations.ts b/frontend/src/app/features/dashboard/declarations.ts deleted file mode 100644 index 227fec6c3..000000000 --- a/frontend/src/app/features/dashboard/declarations.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -export * from './pages/cards/api-card.component'; -export * from './pages/cards/content-summary-card.component'; -export * from './pages/cards/github-card.component'; -export * from './pages/cards/history-card.component'; -export * from './pages/cards/schema-card.component'; -export * from './pages/dashboard-config.component'; -export * from './pages/dashboard-page.component'; diff --git a/frontend/src/app/features/dashboard/index.ts b/frontend/src/app/features/dashboard/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/dashboard/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/dashboard/module.ts b/frontend/src/app/features/dashboard/module.ts deleted file mode 100644 index c3eef9af0..000000000 --- a/frontend/src/app/features/dashboard/module.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { GridsterModule } from 'angular-gridster2'; -import { NgChartsModule } from 'ng2-charts'; -import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { ApiCardComponent, ContentSummaryCardComponent, DashboardConfigComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, SchemaCardComponent } from './declarations'; - -const routes: Routes = [ - { - path: '', - component: DashboardPageComponent, - }, -]; - -@NgModule({ - imports: [ - GridsterModule, - NgChartsModule, - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - ApiCardComponent, - ContentSummaryCardComponent, - DashboardConfigComponent, - DashboardPageComponent, - GithubCardComponent, - HistoryCardComponent, - SchemaCardComponent, - ], -}) -export class SqxFeatureDashboardModule {} diff --git a/frontend/src/app/features/dashboard/pages/cards/api-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/api-card.component.ts index 96a0f6e73..e72eaa369 100644 --- a/frontend/src/app/features/dashboard/pages/cards/api-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/api-card.component.ts @@ -6,13 +6,18 @@ */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { AppDto } from '@app/shared'; +import { AppDto, ExternalLinkDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-api-card', styleUrls: ['./api-card.component.scss'], templateUrl: './api-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ExternalLinkDirective, + TranslatePipe, + ], }) export class ApiCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/dashboard/pages/cards/content-summary-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/content-summary-card.component.ts index 4d559e0b0..fbe4de42b 100644 --- a/frontend/src/app/features/dashboard/pages/cards/content-summary-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/content-summary-card.component.ts @@ -6,7 +6,7 @@ */ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { AppDto, ContentsService, StatefulComponent, Types } from '@app/shared'; +import { AppDto, ContentsService, StatefulComponent, TranslatePipe, Types } from '@app/shared'; interface State { // The number of items. @@ -14,10 +14,14 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-content-summary-card', styleUrls: ['./content-summary-card.component.scss'], templateUrl: './content-summary-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TranslatePipe, + ], }) export class ContentSummaryCardComponent extends StatefulComponent<State> implements OnInit { @Input({ required: true }) diff --git a/frontend/src/app/features/dashboard/pages/cards/github-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/github-card.component.ts index 13c868953..13c166e68 100644 --- a/frontend/src/app/features/dashboard/pages/cards/github-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/github-card.component.ts @@ -6,12 +6,18 @@ */ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ExternalLinkDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-github-card', styleUrls: ['./github-card.component.scss'], templateUrl: './github-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ExternalLinkDirective, + TranslatePipe, + ], }) export class GithubCardComponent { } diff --git a/frontend/src/app/features/dashboard/pages/cards/history-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/history-card.component.ts index 7778d06a8..ffbb4b663 100644 --- a/frontend/src/app/features/dashboard/pages/cards/history-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/history-card.component.ts @@ -5,15 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Observable } from 'rxjs'; -import { AppDto, HistoryEventDto, HistoryService } from '@app/shared'; +import { AppDto, HistoryEventDto, HistoryListComponent, HistoryService, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-history-card', styleUrls: ['./history-card.component.scss'], templateUrl: './history-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + HistoryListComponent, + TranslatePipe, + ], }) export class HistoryCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/dashboard/pages/cards/schema-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/schema-card.component.ts index 2dfd7df52..095e3e293 100644 --- a/frontend/src/app/features/dashboard/pages/cards/schema-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/schema-card.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { AppDto } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { AppDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-card', styleUrls: ['./schema-card.component.scss'], templateUrl: './schema-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + RouterLink, + TranslatePipe, + ], }) export class SchemaCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/dashboard/pages/dashboard-config.component.ts b/frontend/src/app/features/dashboard/pages/dashboard-config.component.ts index f5e57df2e..909bf7e0c 100644 --- a/frontend/src/app/features/dashboard/pages/dashboard-config.component.ts +++ b/frontend/src/app/features/dashboard/pages/dashboard-config.component.ts @@ -5,15 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { GridsterItem } from 'angular-gridster2'; import { take } from 'rxjs/operators'; -import { AppDto, AppsState, AuthService, DialogModel, DialogService, LocalizerService, ModalModel, TypedSimpleChanges, Types, UIState } from '@app/shared'; +import { AppDto, AppsState, AuthService, CodeEditorComponent, ConfirmClickDirective, DialogModel, DialogService, DropdownMenuComponent, LocalizerService, ModalDialogComponent, ModalDirective, ModalModel, ModalPlacementDirective, TooltipDirective, TranslatePipe, TypedSimpleChanges, Types, UIState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-dashboard-config', styleUrls: ['./dashboard-config.component.scss'], templateUrl: './dashboard-config.component.html', + imports: [ + CodeEditorComponent, + ConfirmClickDirective, + DropdownMenuComponent, + FormsModule, + ModalDialogComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class DashboardConfigComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/dashboard/pages/dashboard-page.component.ts b/frontend/src/app/features/dashboard/pages/dashboard-page.component.ts index 01d402e34..dd8175144 100644 --- a/frontend/src/app/features/dashboard/pages/dashboard-page.component.ts +++ b/frontend/src/app/features/dashboard/pages/dashboard-page.component.ts @@ -5,17 +5,57 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { AfterViewInit, Component, NgZone, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { GridsterComponent, GridsterConfig, GridsterItem, GridType } from 'angular-gridster2'; -import { AppsState, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, defined, fadeAnimation, LocalStoreService, Settings, StorageUsagePerDateDto, Subscriptions, switchSafe, UsagesService } from '@app/shared'; +import { GridsterComponent, GridsterConfig, GridsterItem, GridsterItemComponent, GridType } from 'angular-gridster2'; +import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, ApiTrafficSummaryCardComponent, AppsState, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, defined, fadeAnimation, IFrameCardComponent, LocalStoreService, MarkdownPipe, RandomCatCardComponent, RandomDogCardComponent, SafeHtmlPipe, Settings, StorageUsagePerDateDto, Subscriptions, SupportCardComponent, switchSafe, TitleComponent, TourStepDirective, TranslatePipe, UsagesService } from '@app/shared'; +import { ApiCardComponent } from './cards/api-card.component'; +import { ContentSummaryCardComponent } from './cards/content-summary-card.component'; +import { GithubCardComponent } from './cards/github-card.component'; +import { HistoryCardComponent } from './cards/history-card.component'; +import { SchemaCardComponent } from './cards/schema-card.component'; +import { DashboardConfigComponent } from './dashboard-config.component'; @Component({ + standalone: true, selector: 'sqx-dashboard-page', styleUrls: ['./dashboard-page.component.scss'], templateUrl: './dashboard-page.component.html', animations: [ fadeAnimation, ], + imports: [ + ApiCallsCardComponent, + ApiCallsSummaryCardComponent, + ApiCardComponent, + ApiPerformanceCardComponent, + ApiTrafficCardComponent, + ApiTrafficSummaryCardComponent, + AssetUploadsCountCardComponent, + AssetUploadsSizeCardComponent, + AssetUploadsSizeSummaryCardComponent, + AsyncPipe, + ContentSummaryCardComponent, + DashboardConfigComponent, + GithubCardComponent, + GridsterComponent, + GridsterItemComponent, + HistoryCardComponent, + IFrameCardComponent, + MarkdownPipe, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + RandomCatCardComponent, + RandomDogCardComponent, + SafeHtmlPipe, + SchemaCardComponent, + SupportCardComponent, + TitleComponent, + TourStepDirective, + TranslatePipe, + ], }) export class DashboardPageComponent implements AfterViewInit, OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/dashboard/routes.ts b/frontend/src/app/features/dashboard/routes.ts new file mode 100644 index 000000000..bf2b7d53e --- /dev/null +++ b/frontend/src/app/features/dashboard/routes.ts @@ -0,0 +1,16 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { DashboardPageComponent } from './pages/dashboard-page.component'; + +export const DASHBOARD_ROUTES: Routes = [ + { + path: '', + component: DashboardPageComponent, + }, +]; \ No newline at end of file diff --git a/frontend/src/app/features/rules/declarations.ts b/frontend/src/app/features/rules/declarations.ts deleted file mode 100644 index ee608d8ee..000000000 --- a/frontend/src/app/features/rules/declarations.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/events/rule-event.component'; -export * from './pages/events/rule-events-page.component'; -export * from './pages/rule/rule-page.component'; -export * from './pages/rules/rule.component'; -export * from './pages/rules/rules-page.component'; -export * from './pages/simulator/rule-simulator-page.component'; -export * from './pages/simulator/rule-transition.component'; -export * from './pages/simulator/simulated-rule-event.component'; -export * from './shared/actions/formattable-input.component'; -export * from './shared/actions/generic-action.component'; -export * from './shared/rule-element.component'; -export * from './shared/rule-icon.component'; -export * from './shared/pipes'; -export * from './shared/triggers/asset-changed-trigger.component'; -export * from './shared/triggers/comment-trigger.component'; -export * from './shared/triggers/content-changed-schema.component'; -export * from './shared/triggers/content-changed-trigger.component'; -export * from './shared/triggers/schema-changed-trigger.component'; -export * from './shared/triggers/usage-trigger.component'; diff --git a/frontend/src/app/features/rules/index.ts b/frontend/src/app/features/rules/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/rules/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/rules/module.ts b/frontend/src/app/features/rules/module.ts deleted file mode 100644 index 04fde209a..000000000 --- a/frontend/src/app/features/rules/module.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { HelpComponent, RuleMustExistGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AssetChangedTriggerComponent, CommentTriggerComponent, ContentChangedSchemaComponent, ContentChangedTriggerComponent, GenericActionComponent, RuleClassPipe, RuleComponent, RuleElementComponent, RuleEventsPageComponent, RuleIconComponent, RuleSimulatorPageComponent, RulesPageComponent, RuleTransitionComponent, SchemaChangedTriggerComponent, SimulatedRuleEventStatusPipe, UsageTriggerComponent } from './declarations'; -import { RuleEventComponent } from './pages/events/rule-event.component'; -import { RulePageComponent } from './pages/rule/rule-page.component'; -import { SimulatedRuleEventComponent } from './pages/simulator/simulated-rule-event.component'; -import { FormattableInputComponent } from './shared/actions/formattable-input.component'; - -const routes: Routes = [ - { - path: '', - component: RulesPageComponent, - children: [ - { - path: 'events', - component: RuleEventsPageComponent, - }, - { - path: 'simulator', - component: RuleSimulatorPageComponent, - }, - { - path: 'help', - component: HelpComponent, - data: { - helpPage: '05-integrated/rules', - }, - }, - ], - }, { - path: ':ruleId', - component: RulePageComponent, - canActivate: [RuleMustExistGuard], - children: [ - { - path: 'events', - component: RuleEventsPageComponent, - }, - { - path: 'simulator', - component: RuleSimulatorPageComponent, - }, - { - path: 'help', - component: HelpComponent, - data: { - helpPage: '05-integrated/rules', - }, - }, - ], - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AssetChangedTriggerComponent, - CommentTriggerComponent, - ContentChangedSchemaComponent, - ContentChangedTriggerComponent, - FormattableInputComponent, - GenericActionComponent, - RuleClassPipe, - RuleComponent, - RuleElementComponent, - RuleEventComponent, - RuleEventsPageComponent, - RuleIconComponent, - RulePageComponent, - RuleSimulatorPageComponent, - RuleTransitionComponent, - RulesPageComponent, - SchemaChangedTriggerComponent, - SimulatedRuleEventComponent, - SimulatedRuleEventStatusPipe, - UsageTriggerComponent, - ], -}) -export class SqxFeatureRulesModule {} diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.ts b/frontend/src/app/features/rules/pages/events/rule-event.component.ts index 440394e7e..8759f648e 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.ts +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.ts @@ -7,14 +7,27 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { RuleEventDto } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { CodeEditorComponent, ConfirmClickDirective, FromNowPipe, RuleEventDto, TranslatePipe } from '@app/shared'; +import { RuleClassPipe } from '../../shared/pipes'; @Component({ + standalone: true, selector: '[sqxRuleEvent]', styleUrls: ['./rule-event.component.scss'], templateUrl: './rule-event.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CodeEditorComponent, + ConfirmClickDirective, + FormsModule, + FromNowPipe, + NgIf, + RuleClassPipe, + TranslatePipe, + ], }) export class RuleEventComponent { @Input('sqxRuleEvent') diff --git a/frontend/src/app/features/rules/pages/events/rule-events-page.component.ts b/frontend/src/app/features/rules/pages/events/rule-events-page.component.ts index a8231e7c3..5ea85c042 100644 --- a/frontend/src/app/features/rules/pages/events/rule-events-page.component.ts +++ b/frontend/src/app/features/rules/pages/events/rule-events-page.component.ts @@ -5,17 +5,34 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Router2State, RuleEventDto, RuleEventsState, Subscriptions } from '@app/shared'; +import { ConfirmClickDirective, LayoutComponent, ListViewComponent, PagerComponent, Router2State, RuleEventDto, RuleEventsState, ShortcutDirective, Subscriptions, TitleComponent, TooltipDirective, TranslatePipe } from '@app/shared'; +import { RuleEventComponent } from './rule-event.component'; @Component({ + standalone: true, selector: 'sqx-rule-events-page', styleUrls: ['./rule-events-page.component.scss'], templateUrl: './rule-events-page.component.html', providers: [ Router2State, ], + imports: [ + AsyncPipe, + ConfirmClickDirective, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + PagerComponent, + RuleEventComponent, + ShortcutDirective, + TitleComponent, + TooltipDirective, + TranslatePipe, + ], }) export class RuleEventsPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/rules/pages/rule/rule-page.component.ts b/frontend/src/app/features/rules/pages/rule/rule-page.component.ts index b528199f0..abc7afdd8 100644 --- a/frontend/src/app/features/rules/pages/rule/rule-page.component.ts +++ b/frontend/src/app/features/rules/pages/rule/rule-page.component.ts @@ -5,17 +5,56 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { AbstractControl } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { AbstractControl, FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { debounceTime, Subscription } from 'rxjs'; -import { ActionForm, ALL_TRIGGERS, MessageBus, RuleDto, RuleElementDto, RulesService, RulesState, SchemasState, Subscriptions, TriggerForm, TriggerType, value$ } from '@app/shared'; -import { RuleConfigured } from './../messages'; +import { ActionForm, ALL_TRIGGERS, ConfirmClickDirective, FormAlertComponent, KeysPipe, LayoutComponent, ListViewComponent, MessageBus, RuleDto, RuleElementDto, RulesService, RulesState, SchemasState, SidebarMenuDirective, Subscriptions, TitleComponent, ToggleComponent, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe, TriggerForm, TriggerType, value$ } from '@app/shared'; +import { GenericActionComponent } from '../../shared/actions/generic-action.component'; +import { RuleElementComponent } from '../../shared/rule-element.component'; +import { AssetChangedTriggerComponent } from '../../shared/triggers/asset-changed-trigger.component'; +import { CommentTriggerComponent } from '../../shared/triggers/comment-trigger.component'; +import { ContentChangedTriggerComponent } from '../../shared/triggers/content-changed-trigger.component'; +import { SchemaChangedTriggerComponent } from '../../shared/triggers/schema-changed-trigger.component'; +import { UsageTriggerComponent } from '../../shared/triggers/usage-trigger.component'; +import { RuleConfigured } from '../messages'; @Component({ + standalone: true, selector: 'sqx-rule-page', styleUrls: ['./rule-page.component.scss'], templateUrl: './rule-page.component.html', + imports: [ + AssetChangedTriggerComponent, + AsyncPipe, + CommentTriggerComponent, + ConfirmClickDirective, + ContentChangedTriggerComponent, + FormAlertComponent, + FormsModule, + GenericActionComponent, + KeysPipe, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + RouterLink, + RouterLinkActive, + RouterOutlet, + RuleElementComponent, + SchemaChangedTriggerComponent, + SidebarMenuDirective, + TitleComponent, + ToggleComponent, + TooltipDirective, + TourHintDirective, + TourStepDirective, + TranslatePipe, + UsageTriggerComponent, + ], }) export class RulePageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/rules/pages/rules/rule.component.ts b/frontend/src/app/features/rules/pages/rules/rule.component.ts index d009c12cb..ded3621fd 100644 --- a/frontend/src/app/features/rules/pages/rules/rule.component.ts +++ b/frontend/src/app/features/rules/pages/rules/rule.component.ts @@ -5,14 +5,32 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { ActionsDto, ModalModel, RuleDto, RulesState, TriggersDto } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { ActionsDto, ConfirmClickDirective, DropdownMenuComponent, EditableTitleComponent, ModalDirective, ModalModel, ModalPlacementDirective, RuleDto, RulesState, ToggleComponent, TranslatePipe, TriggersDto } from '@app/shared'; +import { RuleElementComponent } from '../../shared/rule-element.component'; @Component({ + standalone: true, selector: 'sqx-rule', styleUrls: ['./rule.component.scss'], templateUrl: './rule.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + DropdownMenuComponent, + EditableTitleComponent, + FormsModule, + ModalDirective, + ModalPlacementDirective, + NgIf, + RouterLink, + RuleElementComponent, + ToggleComponent, + TranslatePipe, + ], }) export class RuleComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/pages/rules/rules-page.component.ts b/frontend/src/app/features/rules/pages/rules/rules-page.component.ts index 484fbe785..ff11dca6e 100644 --- a/frontend/src/app/features/rules/pages/rules/rules-page.component.ts +++ b/frontend/src/app/features/rules/pages/rules/rules-page.component.ts @@ -5,13 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ALL_TRIGGERS, RuleDto, RuleElementDto, RulesService, RulesState, SchemasState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ALL_TRIGGERS, LayoutComponent, ListViewComponent, RuleDto, RuleElementDto, RulesService, RulesState, SchemasState, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { RuleComponent } from './rule.component'; @Component({ + standalone: true, selector: 'sqx-rules-page', styleUrls: ['./rules-page.component.scss'], templateUrl: './rules-page.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + RuleComponent, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourHintDirective, + TourStepDirective, + TranslatePipe, + ], }) export class RulesPageComponent implements OnInit { public supportedActions?: { [name: string]: RuleElementDto }; diff --git a/frontend/src/app/features/rules/pages/simulator/rule-simulator-page.component.ts b/frontend/src/app/features/rules/pages/simulator/rule-simulator-page.component.ts index 3b63a3496..07ac8c419 100644 --- a/frontend/src/app/features/rules/pages/simulator/rule-simulator-page.component.ts +++ b/frontend/src/app/features/rules/pages/simulator/rule-simulator-page.component.ts @@ -5,15 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { MessageBus, RuleSimulatorState, SimulatedRuleEventDto, Subscriptions } from '@app/shared'; -import { RuleConfigured } from './../messages'; +import { LayoutComponent, ListViewComponent, MessageBus, RuleSimulatorState, SimulatedRuleEventDto, Subscriptions, TitleComponent, TooltipDirective, TranslatePipe } from '@app/shared'; +import { RuleConfigured } from '../messages'; +import { SimulatedRuleEventComponent } from './simulated-rule-event.component'; @Component({ + standalone: true, selector: 'sqx-simulator-events-page', styleUrls: ['./rule-simulator-page.component.scss'], templateUrl: './rule-simulator-page.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + ListViewComponent, + NgFor, + SimulatedRuleEventComponent, + TitleComponent, + TooltipDirective, + TranslatePipe, + ], }) export class RuleSimulatorPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts index 672450328..c1f879aeb 100644 --- a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts +++ b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts @@ -5,14 +5,21 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { SimulatedRuleEventDto, TypedSimpleChanges } from '@app/shared'; +import { SimulatedRuleEventDto, TranslatePipe, TypedSimpleChanges } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-rule-transition', styleUrls: ['./rule-transition.component.scss'], templateUrl: './rule-transition.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + TranslatePipe, + ], }) export class RuleTransitionComponent { @Input() diff --git a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.ts b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.ts index ae5ddb7f3..3fbbbd2d0 100644 --- a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.ts +++ b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.ts @@ -7,8 +7,12 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { SimulatedRuleEventDto } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { CodeEditorComponent, JoinPipe, SimulatedRuleEventDto, TranslatePipe } from '@app/shared'; +import { RuleClassPipe, SimulatedRuleEventStatusPipe } from '../../shared/pipes'; +import { RuleTransitionComponent } from './rule-transition.component'; const ERRORS_AFTER_EVENT = [ 'ConditionPrecheckDoesNotMatch', @@ -30,10 +34,21 @@ const ERRORS_FAILED = [ ]; @Component({ + standalone: true, selector: '[sqxSimulatedRuleEvent]', styleUrls: ['./simulated-rule-event.component.scss'], templateUrl: './simulated-rule-event.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CodeEditorComponent, + FormsModule, + JoinPipe, + NgIf, + RuleClassPipe, + RuleTransitionComponent, + SimulatedRuleEventStatusPipe, + TranslatePipe, + ], }) export class SimulatedRuleEventComponent { @Input('sqxSimulatedRuleEvent') diff --git a/frontend/src/app/features/rules/routes.ts b/frontend/src/app/features/rules/routes.ts new file mode 100644 index 000000000..dee8e4f3f --- /dev/null +++ b/frontend/src/app/features/rules/routes.ts @@ -0,0 +1,58 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { HelpComponent, ruleMustExistGuard } from '@app/shared'; +import { RuleEventsPageComponent } from './pages/events/rule-events-page.component'; +import { RulePageComponent } from './pages/rule/rule-page.component'; +import { RulesPageComponent } from './pages/rules/rules-page.component'; +import { RuleSimulatorPageComponent } from './pages/simulator/rule-simulator-page.component'; + +export const RULES_ROUTES: Routes = [ + { + path: '', + component: RulesPageComponent, + children: [ + { + path: 'events', + component: RuleEventsPageComponent, + }, + { + path: 'simulator', + component: RuleSimulatorPageComponent, + }, + { + path: 'help', + component: HelpComponent, + data: { + helpPage: '05-integrated/rules', + }, + }, + ], + }, { + path: ':ruleId', + component: RulePageComponent, + canActivate: [ruleMustExistGuard], + children: [ + { + path: 'events', + component: RuleEventsPageComponent, + }, + { + path: 'simulator', + component: RuleSimulatorPageComponent, + }, + { + path: 'help', + component: HelpComponent, + data: { + helpPage: '05-integrated/rules', + }, + }, + ], + }, +]; diff --git a/frontend/src/app/features/rules/shared/actions/formattable-input.component.ts b/frontend/src/app/features/rules/shared/actions/formattable-input.component.ts index a24db7160..0087b219a 100644 --- a/frontend/src/app/features/rules/shared/actions/formattable-input.component.ts +++ b/frontend/src/app/features/rules/shared/actions/formattable-input.component.ts @@ -5,9 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, Component, forwardRef, Input, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { CodeEditorComponent, ScriptCompletions, Types } from '@app/framework'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { CodeEditorComponent, ScriptCompletions, Types } from '@app/shared'; type TemplateMode = 'Text' | 'Script' | 'Liquid'; @@ -18,6 +19,7 @@ export const SQX_FORMATTABLE_INPUT_CONTROL_VALUE_ACCESSOR: any = { }; @Component({ + standalone: true, selector: 'sqx-formattable-input', styleUrls: ['./formattable-input.component.scss'], templateUrl: './formattable-input.component.html', @@ -25,6 +27,12 @@ export const SQX_FORMATTABLE_INPUT_CONTROL_VALUE_ACCESSOR: any = { SQX_FORMATTABLE_INPUT_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CodeEditorComponent, + FormsModule, + NgFor, + NgIf, + ], }) export class FormattableInputComponent implements ControlValueAccessor, AfterViewInit { private fnChanged = (_: any) => { /* NOOP */ }; diff --git a/frontend/src/app/features/rules/shared/actions/generic-action.component.ts b/frontend/src/app/features/rules/shared/actions/generic-action.component.ts index 4d061aa83..fc3af5845 100644 --- a/frontend/src/app/features/rules/shared/actions/generic-action.component.ts +++ b/frontend/src/app/features/rules/shared/actions/generic-action.component.ts @@ -5,15 +5,37 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, LowerCasePipe, NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, Observable, shareReplay } from 'rxjs'; -import { ActionForm, RulesService, ScriptCompletions, TypedSimpleChanges } from '@app/shared'; +import { ActionForm, CodeEditorComponent, ControlErrorsComponent, ExternalLinkDirective, FormHintComponent, MarkdownDirective, RulesService, ScriptCompletions, TranslatePipe, TypedSimpleChanges } from '@app/shared'; +import { FormattableInputComponent } from './formattable-input.component'; @Component({ + standalone: true, selector: 'sqx-generic-action', styleUrls: ['./generic-action.component.scss'], templateUrl: './generic-action.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + CodeEditorComponent, + ControlErrorsComponent, + ExternalLinkDirective, + FormHintComponent, + FormattableInputComponent, + FormsModule, + LowerCasePipe, + MarkdownDirective, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + ReactiveFormsModule, + TranslatePipe, + ], }) export class GenericActionComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/pipes.ts b/frontend/src/app/features/rules/shared/pipes.ts index f74334325..f55530502 100644 --- a/frontend/src/app/features/rules/shared/pipes.ts +++ b/frontend/src/app/features/rules/shared/pipes.ts @@ -9,6 +9,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { SimulatedRuleEventDto } from '@app/shared'; @Pipe({ + standalone: true, name: 'sqxRuleClass', pure: true, }) @@ -27,6 +28,7 @@ export class RuleClassPipe implements PipeTransform { } @Pipe({ + standalone: true, name: 'sqxSimulatedRuleEventStatus', pure: true, }) diff --git a/frontend/src/app/features/rules/shared/rule-element.component.ts b/frontend/src/app/features/rules/shared/rule-element.component.ts index 792979bf9..6823e43a4 100644 --- a/frontend/src/app/features/rules/shared/rule-element.component.ts +++ b/frontend/src/app/features/rules/shared/rule-element.component.ts @@ -5,14 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { RuleElementMetadataDto } from '@app/shared'; +import { DarkenPipe, ExternalLinkDirective, HoverBackgroundDirective, RuleElementMetadataDto, StopClickDirective, TranslatePipe } from '@app/shared'; +import { RuleIconComponent } from './rule-icon.component'; @Component({ + standalone: true, selector: 'sqx-rule-element', styleUrls: ['./rule-element.component.scss'], templateUrl: './rule-element.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DarkenPipe, + ExternalLinkDirective, + HoverBackgroundDirective, + NgIf, + RuleIconComponent, + StopClickDirective, + TranslatePipe, + ], }) export class RuleElementComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/rule-icon.component.ts b/frontend/src/app/features/rules/shared/rule-icon.component.ts index 31824c100..4a5af27a0 100644 --- a/frontend/src/app/features/rules/shared/rule-icon.component.ts +++ b/frontend/src/app/features/rules/shared/rule-icon.component.ts @@ -5,14 +5,20 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { RuleElementMetadataDto } from '@app/shared'; +import { RuleElementMetadataDto, SafeHtmlPipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-rule-icon', styleUrls: ['./rule-icon.component.scss'], templateUrl: './rule-icon.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + SafeHtmlPipe, + ], }) export class RuleIconComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.ts b/frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.ts index f037cd962..00467443d 100644 --- a/frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.ts @@ -6,12 +6,22 @@ */ import { Component, Input } from '@angular/core'; -import { TriggerForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CodeComponent, ControlErrorsComponent, FormHintComponent, TranslatePipe, TriggerForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-asset-changed-trigger', styleUrls: ['./asset-changed-trigger.component.scss'], templateUrl: './asset-changed-trigger.component.html', + imports: [ + CodeComponent, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class AssetChangedTriggerComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/triggers/comment-trigger.component.ts b/frontend/src/app/features/rules/shared/triggers/comment-trigger.component.ts index a3a28efbc..392144d69 100644 --- a/frontend/src/app/features/rules/shared/triggers/comment-trigger.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/comment-trigger.component.ts @@ -6,12 +6,22 @@ */ import { Component, Input } from '@angular/core'; -import { TriggerForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CodeComponent, ControlErrorsComponent, FormHintComponent, TranslatePipe, TriggerForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-comment-trigger', styleUrls: ['./comment-trigger.component.scss'], templateUrl: './comment-trigger.component.html', + imports: [ + CodeComponent, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class CommentTriggerComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.ts b/frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.ts index 5a4ae30bb..d9532d22a 100644 --- a/frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.ts @@ -5,16 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, Observable, switchMap } from 'rxjs'; -import { SchemaDto, ScriptCompletions, TypedSimpleChanges, value$ } from '@app/shared'; +import { CodeEditorComponent, ConfirmClickDirective, ControlErrorsComponent, SchemaDto, ScriptCompletions, TranslatePipe, TypedSimpleChanges, value$ } from '@app/shared'; import { CompletionsCache } from './completions-cache'; @Component({ + standalone: true, selector: 'sqx-content-changed-schema', styleUrls: ['./content-changed-schema.component.scss'], templateUrl: './content-changed-schema.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + ConfirmClickDirective, + ControlErrorsComponent, + FormsModule, + NgFor, + ReactiveFormsModule, + TranslatePipe, + ], }) export class ContentChangedSchemaComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.ts b/frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.ts index 396f8a39c..1badcba9a 100644 --- a/frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.ts @@ -5,17 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { SchemaDto, TemplatedFormArray, TriggerForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CodeComponent, FormHintComponent, SchemaDto, TemplatedFormArray, TranslatePipe, TriggerForm } from '@app/shared'; import { CompletionsCache } from './completions-cache'; +import { ContentChangedSchemaComponent } from './content-changed-schema.component'; @Component({ + standalone: true, selector: 'sqx-content-changed-trigger', styleUrls: ['./content-changed-trigger.component.scss'], templateUrl: './content-changed-trigger.component.html', providers: [ CompletionsCache, ], + imports: [ + CodeComponent, + ContentChangedSchemaComponent, + FormHintComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class ContentChangedTriggerComponent { @Input() diff --git a/frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.ts b/frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.ts index 660ed9c13..d31ffc9a4 100644 --- a/frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.ts @@ -6,12 +6,22 @@ */ import { Component, Input } from '@angular/core'; -import { TriggerForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CodeComponent, ControlErrorsComponent, FormHintComponent, TranslatePipe, TriggerForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-changed-trigger', styleUrls: ['./schema-changed-trigger.component.scss'], templateUrl: './schema-changed-trigger.component.html', + imports: [ + CodeComponent, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class SchemaChangedTriggerComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/rules/shared/triggers/usage-trigger.component.ts b/frontend/src/app/features/rules/shared/triggers/usage-trigger.component.ts index 8ea6013c7..57531eda4 100644 --- a/frontend/src/app/features/rules/shared/triggers/usage-trigger.component.ts +++ b/frontend/src/app/features/rules/shared/triggers/usage-trigger.component.ts @@ -6,12 +6,21 @@ */ import { Component, Input } from '@angular/core'; -import { TriggerForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ControlErrorsComponent, FormHintComponent, TranslatePipe, TriggerForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-usage-trigger', styleUrls: ['./usage-trigger.component.scss'], templateUrl: './usage-trigger.component.html', + imports: [ + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class UsageTriggerComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/declarations.ts b/frontend/src/app/features/schemas/declarations.ts deleted file mode 100644 index f9a6638c4..000000000 --- a/frontend/src/app/features/schemas/declarations.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/schema/common/schema-edit-form.component'; -export * from './pages/schema/export/schema-export-form.component'; -export * from './pages/schema/fields/field-group.component'; -export * from './pages/schema/fields/field-wizard.component'; -export * from './pages/schema/fields/field.component'; -export * from './pages/schema/fields/forms/field-form-common.component'; -export * from './pages/schema/fields/forms/field-form-ui.component'; -export * from './pages/schema/fields/forms/field-form-validation.component'; -export * from './pages/schema/fields/forms/field-form.component'; -export * from './pages/schema/fields/schema-fields.component'; -export * from './pages/schema/fields/sortable-field-list.component'; -export * from './pages/schema/fields/types/array-validation.component'; -export * from './pages/schema/fields/types/assets-ui.component'; -export * from './pages/schema/fields/types/assets-validation.component'; -export * from './pages/schema/fields/types/boolean-ui.component'; -export * from './pages/schema/fields/types/boolean-validation.component'; -export * from './pages/schema/fields/types/component-ui.component'; -export * from './pages/schema/fields/types/component-validation.component'; -export * from './pages/schema/fields/types/components-ui.component'; -export * from './pages/schema/fields/types/components-validation.component'; -export * from './pages/schema/fields/types/date-time-ui.component'; -export * from './pages/schema/fields/types/date-time-validation.component'; -export * from './pages/schema/fields/types/geolocation-ui.component'; -export * from './pages/schema/fields/types/geolocation-validation.component'; -export * from './pages/schema/fields/types/json-more.component'; -export * from './pages/schema/fields/types/json-ui.component'; -export * from './pages/schema/fields/types/json-validation.component'; -export * from './pages/schema/fields/types/number-ui.component'; -export * from './pages/schema/fields/types/number-validation.component'; -export * from './pages/schema/fields/types/references-ui.component'; -export * from './pages/schema/fields/types/references-validation.component'; -export * from './pages/schema/fields/types/string-ui.component'; -export * from './pages/schema/fields/types/string-validation.component'; -export * from './pages/schema/fields/types/tags-ui.component'; -export * from './pages/schema/fields/types/tags-validation.component'; -export * from './pages/schema/preview/schema-preview-urls-form.component'; -export * from './pages/schema/rules/schema-field-rules-form.component'; -export * from './pages/schema/schema-page.component'; -export * from './pages/schema/scripts/schema-scripts-form.component'; -export * from './pages/schema/ui/field-list.component'; -export * from './pages/schema/ui/schema-ui-form.component'; -export * from './pages/schemas/schema-form.component'; -export * from './pages/schemas/schemas-page.component'; diff --git a/frontend/src/app/features/schemas/index.ts b/frontend/src/app/features/schemas/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/schemas/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/schemas/module.ts b/frontend/src/app/features/schemas/module.ts deleted file mode 100644 index 861285a68..000000000 --- a/frontend/src/app/features/schemas/module.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { HelpComponent, HistoryComponent, LoadSchemasGuard, SchemaMustExistGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { ArrayValidationComponent, AssetsUIComponent, AssetsValidationComponent, BooleanUIComponent, BooleanValidationComponent, ComponentsUIComponent, ComponentsValidationComponent, ComponentUIComponent, ComponentValidationComponent, DateTimeUIComponent, DateTimeValidationComponent, FieldComponent, FieldFormCommonComponent, FieldFormComponent, FieldFormUIComponent, FieldFormValidationComponent, FieldGroupComponent, FieldListComponent, FieldWizardComponent, GeolocationUIComponent, GeolocationValidationComponent, JsonMoreComponent, JsonUIComponent, JsonValidationComponent, NumberUIComponent, NumberValidationComponent, ReferencesUIComponent, ReferencesValidationComponent, SchemaEditFormComponent, SchemaExportFormComponent, SchemaFieldRulesFormComponent, SchemaFieldsComponent, SchemaFormComponent, SchemaPageComponent, SchemaPreviewUrlsFormComponent, SchemaScriptsFormComponent, SchemasPageComponent, SchemaUIFormComponent, SortableFieldListComponent, StringUIComponent, StringValidationComponent, TagsUIComponent, TagsValidationComponent } from './declarations'; -import { ArrayUIComponent } from './pages/schema/fields/types/array-ui.component'; - -const routes: Routes = [ - { - path: '', - component: SchemasPageComponent, - canActivate: [LoadSchemasGuard], - children: [ - { - path: ':schemaName', - component: SchemaPageComponent, - canActivate: [SchemaMustExistGuard], - children: [ - { - path: 'help', - component: HelpComponent, - data: { - helpPage: '05-integrated/schemas', - }, - }, - { - path: 'history', - component: HistoryComponent, - data: { - channel: 'schemas.{schemaId}', - }, - }, - ], - }, - ], - }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - providers: [ - SchemaMustExistGuard, - ], - declarations: [ - ArrayUIComponent, - ArrayValidationComponent, - AssetsUIComponent, - AssetsValidationComponent, - BooleanUIComponent, - BooleanValidationComponent, - ComponentUIComponent, - ComponentValidationComponent, - ComponentsUIComponent, - ComponentsValidationComponent, - DateTimeUIComponent, - DateTimeValidationComponent, - FieldComponent, - FieldGroupComponent, - FieldFormCommonComponent, - FieldFormComponent, - FieldFormUIComponent, - FieldFormValidationComponent, - FieldListComponent, - FieldWizardComponent, - GeolocationUIComponent, - GeolocationValidationComponent, - JsonMoreComponent, - JsonUIComponent, - JsonValidationComponent, - NumberUIComponent, - NumberValidationComponent, - ReferencesUIComponent, - ReferencesValidationComponent, - SchemaEditFormComponent, - SchemaExportFormComponent, - SchemaFieldRulesFormComponent, - SchemaFieldsComponent, - SchemaFormComponent, - SchemaPageComponent, - SchemaPreviewUrlsFormComponent, - SchemaScriptsFormComponent, - SchemasPageComponent, - SchemaUIFormComponent, - SortableFieldListComponent, - StringUIComponent, - StringValidationComponent, - TagsUIComponent, - TagsValidationComponent, - ], -}) -export class SqxFeatureSchemasModule {} diff --git a/frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.ts b/frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.ts index 4d1006935..877693baf 100644 --- a/frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.ts @@ -5,13 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { EditSchemaForm, SchemaDto, SchemasState } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ControlErrorsComponent, EditSchemaForm, FormAlertComponent, FormHintComponent, SchemaDto, SchemasState, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-edit-form', styleUrls: ['./schema-edit-form.component.scss'], templateUrl: './schema-edit-form.component.html', + imports: [ + ControlErrorsComponent, + FormAlertComponent, + FormHintComponent, + FormsModule, + NgIf, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class SchemaEditFormComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/export/schema-export-form.component.ts b/frontend/src/app/features/schemas/pages/schema/export/schema-export-form.component.ts index 945592b5c..249248b7d 100644 --- a/frontend/src/app/features/schemas/pages/schema/export/schema-export-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/export/schema-export-form.component.ts @@ -5,13 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { SchemaDto, SchemasState, SynchronizeSchemaForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CodeEditorComponent, SchemaDto, SchemasState, SynchronizeSchemaForm, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-export-form', styleUrls: ['./schema-export-form.component.scss'], templateUrl: './schema-export-form.component.html', + imports: [ + CodeEditorComponent, + FormsModule, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class SchemaExportFormComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field-group.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/field-group.component.ts index f507b5d5c..987a67436 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field-group.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/field-group.component.ts @@ -5,9 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; import { AppSettingsDto, FieldDto, FieldGroup, LanguageDto, LocalStoreService, RootFieldDto, SchemaDto, Settings, StatefulComponent } from '@app/shared'; +import { FieldComponent } from './field.component'; interface State { // The when the section is collapsed. @@ -15,9 +17,18 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-field-group', styleUrls: ['./field-group.component.scss'], templateUrl: './field-group.component.html', + imports: [ + CdkDrag, + CdkDragHandle, + CdkDropList, + FieldComponent, + NgFor, + NgIf, + ], }) export class FieldGroupComponent extends StatefulComponent<State> { @Output() diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.ts index bf221f70b..024bf2281 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.ts @@ -5,15 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { AddFieldForm, AppSettingsDto, createProperties, EditFieldForm, FieldDto, fieldTypes, LanguagesState, RootFieldDto, SchemaDto, SchemasState, Types } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AddFieldForm, AppSettingsDto, ControlErrorsComponent, createProperties, EditFieldForm, FieldDto, fieldTypes, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, LanguagesState, ModalDialogComponent, RootFieldDto, SchemaDto, SchemasState, TooltipDirective, TranslatePipe, Types } from '@app/shared'; +import { FieldFormComponent } from './forms/field-form.component'; const DEFAULT_FIELD = { name: '', partitioning: 'invariant', properties: createProperties('String') }; @Component({ + standalone: true, selector: 'sqx-field-wizard', styleUrls: ['./field-wizard.component.scss'], templateUrl: './field-wizard.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FieldFormComponent, + FocusOnInitDirective, + FormAlertComponent, + FormErrorComponent, + FormHintComponent, + FormsModule, + ModalDialogComponent, + NgFor, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TranslatePipe, + ], }) export class FieldWizardComponent implements OnInit { @ViewChild('nameInput', { static: false }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts index 6ae8bdde1..edf8725fe 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts @@ -5,13 +5,34 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, Component, Input } from '@angular/core'; -import { AppSettingsDto, createProperties, DialogModel, EditFieldForm, FieldDto, LanguageDto, ModalModel, NestedFieldDto, RootFieldDto, SchemaDto, SchemasState, TypedSimpleChanges } from '@app/shared'; +import { NgIf } from '@angular/common'; +import { booleanAttribute, Component, forwardRef, Input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AppSettingsDto, ConfirmClickDirective, createProperties, DialogModel, DropdownMenuComponent, EditFieldForm, FieldDto, LanguageDto, ModalDirective, ModalModel, ModalPlacementDirective, NestedFieldDto, RootFieldDto, SchemaDto, SchemasState, TooltipDirective, TourStepDirective, TranslatePipe, TypedSimpleChanges } from '@app/shared'; +import { FieldWizardComponent } from './field-wizard.component'; +import { FieldFormComponent } from './forms/field-form.component'; +import { SortableFieldListComponent } from './sortable-field-list.component'; @Component({ + standalone: true, selector: 'sqx-field', styleUrls: ['./field.component.scss'], templateUrl: './field.component.html', + imports: [ + ConfirmClickDirective, + DropdownMenuComponent, + FieldFormComponent, + FieldWizardComponent, + FormsModule, + ModalDirective, + ModalPlacementDirective, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TourStepDirective, + TranslatePipe, + forwardRef(() => SortableFieldListComponent), + ], }) export class FieldComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-common.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-common.component.ts index 70efded13..752077241 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-common.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-common.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, SchemaDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { ControlErrorsComponent, FieldDto, FormHintComponent, SchemaDto, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-field-form-common', styleUrls: ['./field-form-common.component.scss'], templateUrl: './field-form-common.component.html', + imports: [ + ControlErrorsComponent, + FormHintComponent, + FormsModule, + NgIf, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class FieldFormCommonComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.ts index f38111b48..67058b930 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.ts @@ -5,14 +5,48 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgSwitch, NgSwitchCase } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, SchemaDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, SchemaDto, TranslatePipe } from '@app/shared'; +import { ArrayUIComponent } from '../types/array-ui.component'; +import { AssetsUIComponent } from '../types/assets-ui.component'; +import { BooleanUIComponent } from '../types/boolean-ui.component'; +import { ComponentUIComponent } from '../types/component-ui.component'; +import { ComponentsUIComponent } from '../types/components-ui.component'; +import { DateTimeUIComponent } from '../types/date-time-ui.component'; +import { GeolocationUIComponent } from '../types/geolocation-ui.component'; +import { JsonUIComponent } from '../types/json-ui.component'; +import { NumberUIComponent } from '../types/number-ui.component'; +import { ReferencesUIComponent } from '../types/references-ui.component'; +import { StringUIComponent } from '../types/string-ui.component'; +import { TagsUIComponent } from '../types/tags-ui.component'; @Component({ + standalone: true, selector: 'sqx-field-form-ui', styleUrls: ['./field-form-ui.component.scss'], templateUrl: './field-form-ui.component.html', + imports: [ + ArrayUIComponent, + AssetsUIComponent, + BooleanUIComponent, + ComponentUIComponent, + ComponentsUIComponent, + DateTimeUIComponent, + FormHintComponent, + FormsModule, + GeolocationUIComponent, + JsonUIComponent, + NgSwitch, + NgSwitchCase, + NumberUIComponent, + ReactiveFormsModule, + ReferencesUIComponent, + StringUIComponent, + TagsUIComponent, + TranslatePipe, + ], }) export class FieldFormUIComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-validation.component.ts index 89bf79bcc..da186d83d 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form-validation.component.ts @@ -5,14 +5,47 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgSwitch, NgSwitchCase } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { AppSettingsDto, FieldDto, LanguageDto, SchemaDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { AppSettingsDto, FieldDto, LanguageDto, SchemaDto, TranslatePipe } from '@app/shared'; +import { ArrayValidationComponent } from '../types/array-validation.component'; +import { AssetsValidationComponent } from '../types/assets-validation.component'; +import { BooleanValidationComponent } from '../types/boolean-validation.component'; +import { ComponentValidationComponent } from '../types/component-validation.component'; +import { ComponentsValidationComponent } from '../types/components-validation.component'; +import { DateTimeValidationComponent } from '../types/date-time-validation.component'; +import { GeolocationValidationComponent } from '../types/geolocation-validation.component'; +import { JsonValidationComponent } from '../types/json-validation.component'; +import { NumberValidationComponent } from '../types/number-validation.component'; +import { ReferencesValidationComponent } from '../types/references-validation.component'; +import { StringValidationComponent } from '../types/string-validation.component'; +import { TagsValidationComponent } from '../types/tags-validation.component'; @Component({ + standalone: true, selector: 'sqx-field-form-validation', styleUrls: ['./field-form-validation.component.scss'], templateUrl: './field-form-validation.component.html', + imports: [ + ArrayValidationComponent, + AssetsValidationComponent, + BooleanValidationComponent, + ComponentValidationComponent, + ComponentsValidationComponent, + DateTimeValidationComponent, + FormsModule, + GeolocationValidationComponent, + JsonValidationComponent, + NgSwitch, + NgSwitchCase, + NumberValidationComponent, + ReactiveFormsModule, + ReferencesValidationComponent, + StringValidationComponent, + TagsValidationComponent, + TranslatePipe, + ], }) export class FieldFormValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.ts index 036305deb..4b19ffdd0 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.ts @@ -5,14 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterViewInit, booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { AppSettingsDto, FieldDto, LanguageDto, SchemaDto } from '@app/shared'; +import { AppSettingsDto, FieldDto, LanguageDto, SchemaDto, TranslatePipe } from '@app/shared'; +import { JsonMoreComponent } from '../types/json-more.component'; +import { FieldFormCommonComponent } from './field-form-common.component'; +import { FieldFormUIComponent } from './field-form-ui.component'; +import { FieldFormValidationComponent } from './field-form-validation.component'; @Component({ + standalone: true, selector: 'sqx-field-form', styleUrls: ['./field-form.component.scss'], templateUrl: './field-form.component.html', + imports: [ + FieldFormCommonComponent, + FieldFormUIComponent, + FieldFormValidationComponent, + JsonMoreComponent, + NgIf, + TranslatePipe, + ], }) export class FieldFormComponent implements AfterViewInit { @Input({ transform: booleanAttribute }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts index 68ef7d6bb..8f6960e3a 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts @@ -5,13 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, Input, OnInit } from '@angular/core'; -import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, SchemaDto, SchemasState } from '@app/shared'; +import { AsyncPipe, NgIf } from '@angular/common'; +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, ModalDirective, SchemaDto, SchemasState, TourStepDirective, TranslatePipe } from '@app/shared'; +import { FieldWizardComponent } from './field-wizard.component'; +import { SortableFieldListComponent } from './sortable-field-list.component'; @Component({ + standalone: true, selector: 'sqx-schema-fields', styleUrls: ['./schema-fields.component.scss'], templateUrl: './schema-fields.component.html', + imports: [ + AsyncPipe, + FieldWizardComponent, + ModalDirective, + NgIf, + TourStepDirective, + TranslatePipe, + forwardRef(() => SortableFieldListComponent), + ], }) export class SchemaFieldsComponent implements OnInit { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/sortable-field-list.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/sortable-field-list.component.ts index 9d3b72045..cb1e15da0 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/sortable-field-list.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/sortable-field-list.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; -import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, CdkDropListGroup, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { NgFor } from '@angular/common'; +import { booleanAttribute, Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; import { AppSettingsDto, FieldDto, FieldGroup, groupFields, LanguageDto, RootFieldDto, SchemaDto } from '@app/shared'; +import { FieldGroupComponent } from './field-group.component'; @Component({ + standalone: true, selector: 'sqx-sortable-field-list', styleUrls: ['./sortable-field-list.component.scss'], templateUrl: './sortable-field-list.component.html', + imports: [ + CdkDrag, + CdkDragHandle, + CdkDropList, + CdkDropListGroup, + NgFor, + forwardRef(() => FieldGroupComponent), + ], }) export class SortableFieldListComponent { @Output() diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/array-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/array-ui.component.ts index cd10bf685..9f6e33dfd 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/array-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/array-ui.component.ts @@ -5,16 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { ArrayFieldPropertiesDto, FieldDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { ArrayFieldPropertiesDto, FieldDto, TranslatePipe } from '@app/shared'; const CALCULATED_DEFAULT_VALUES: ReadonlyArray<string> = ['EmptyArray', 'Null']; @Component({ + standalone: true, selector: 'sqx-array-ui', styleUrls: ['array-ui.component.scss'], templateUrl: 'array-ui.component.html', + imports: [ + FormsModule, + NgFor, + ReactiveFormsModule, + TranslatePipe, + ], }) export class ArrayUIComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/array-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/array-validation.component.ts index f4dc1240c..4dee5fe4c 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/array-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/array-validation.component.ts @@ -6,13 +6,20 @@ */ import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { ArrayFieldPropertiesDto, FieldDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { ArrayFieldPropertiesDto, FieldDto, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-array-validation', styleUrls: ['array-validation.component.scss'], templateUrl: 'array-validation.component.html', + imports: [ + FormsModule, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class ArrayValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts index 02fc2120a..d2b4500b1 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts @@ -6,13 +6,23 @@ */ import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { AssetsFieldPropertiesDto, FieldDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { AssetFolderDropdownComponent, AssetsFieldPropertiesDto, FieldDto, FormHintComponent, MarkdownInlinePipe, SafeHtmlPipe, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-assets-ui', styleUrls: ['assets-ui.component.scss'], templateUrl: 'assets-ui.component.html', + imports: [ + AssetFolderDropdownComponent, + FormHintComponent, + FormsModule, + MarkdownInlinePipe, + ReactiveFormsModule, + SafeHtmlPipe, + TranslatePipe, + ], }) export class AssetsUIComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/assets-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/assets-validation.component.ts index 9c5f2c722..bd9398b4d 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/assets-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/assets-validation.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { AssetsFieldPropertiesDto, FieldDto, LanguageDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { AssetsFieldPropertiesDto, FieldDto, FormHintComponent, LanguageDto, LocalizedInputComponent, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-assets-validation', styleUrls: ['assets-validation.component.scss'], templateUrl: 'assets-validation.component.html', + imports: [ + FormHintComponent, + FormsModule, + LocalizedInputComponent, + NgIf, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class AssetsValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-ui.component.ts index c720d8078..ec695d987 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-ui.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { BOOLEAN_FIELD_EDITORS, BooleanFieldPropertiesDto, FieldDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { BOOLEAN_FIELD_EDITORS, BooleanFieldPropertiesDto, FieldDto, FormHintComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-boolean-ui', styleUrls: ['boolean-ui.component.scss'], templateUrl: 'boolean-ui.component.html', + imports: [ + FormHintComponent, + FormsModule, + NgFor, + ReactiveFormsModule, + TranslatePipe, + ], }) export class BooleanUIComponent { public readonly editors = BOOLEAN_FIELD_EDITORS; diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-validation.component.ts index 9357b0c71..ec116c699 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/boolean-validation.component.ts @@ -5,15 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; -import { BooleanFieldPropertiesDto, FieldDto, hasNoValue$, LanguageDto, TypedSimpleChanges } from '@app/shared'; +import { BooleanFieldPropertiesDto, FieldDto, FormHintComponent, hasNoValue$, IndeterminateValueDirective, LanguageDto, LocalizedInputComponent, TranslatePipe, TypedSimpleChanges } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-boolean-validation', styleUrls: ['boolean-validation.component.scss'], templateUrl: 'boolean-validation.component.html', + imports: [ + FormHintComponent, + FormsModule, + IndeterminateValueDirective, + LocalizedInputComponent, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class BooleanValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/component-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/component-ui.component.ts index 5234bbc7c..b2e949aef 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/component-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/component-ui.component.ts @@ -10,6 +10,7 @@ import { UntypedFormGroup } from '@angular/forms'; import { FieldDto, ReferencesFieldPropertiesDto } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-component-ui', styleUrls: ['component-ui.component.scss'], templateUrl: 'component-ui.component.html', diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/component-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/component-validation.component.ts index 183d9266c..b1c534477 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/component-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/component-validation.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, ReferencesFieldPropertiesDto, SchemaTagSource } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, ReferencesFieldPropertiesDto, SchemaTagSource, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-component-validation', styleUrls: ['component-validation.component.scss'], templateUrl: 'component-validation.component.html', + imports: [ + AsyncPipe, + FormsModule, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class ComponentValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/components-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/components-ui.component.ts index 508417b4d..16cb7f298 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/components-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/components-ui.component.ts @@ -5,16 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, ReferencesFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, ReferencesFieldPropertiesDto, TranslatePipe } from '@app/shared'; const CALCULATED_DEFAULT_VALUES: ReadonlyArray<string> = ['EmptyArray', 'Null']; @Component({ + standalone: true, selector: 'sqx-components-ui', styleUrls: ['components-ui.component.scss'], templateUrl: 'components-ui.component.html', + imports: [ + FormsModule, + NgFor, + ReactiveFormsModule, + TranslatePipe, + ], }) export class ComponentsUIComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/components-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/components-validation.component.ts index bc06b76d2..355b044eb 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/components-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/components-validation.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, ReferencesFieldPropertiesDto, SchemaTagSource } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, ReferencesFieldPropertiesDto, SchemaTagSource, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-components-validation', styleUrls: ['components-validation.component.scss'], templateUrl: 'components-validation.component.html', + imports: [ + AsyncPipe, + FormsModule, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class ComponentsValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-ui.component.ts index 830333d6f..a09860a3d 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-ui.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { DATETIME_FIELD_EDITORS, DateTimeFieldPropertiesDto, FieldDto, FloatConverter } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { DATETIME_FIELD_EDITORS, DateTimeFieldPropertiesDto, FieldDto, FloatConverter, FormHintComponent, MarkdownInlinePipe, SafeHtmlPipe, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-date-time-ui', styleUrls: ['date-time-ui.component.scss'], templateUrl: 'date-time-ui.component.html', + imports: [ + FormHintComponent, + FormsModule, + MarkdownInlinePipe, + NgFor, + ReactiveFormsModule, + SafeHtmlPipe, + TranslatePipe, + ], }) export class DateTimeUIComponent { public readonly converter = FloatConverter.INSTANCE; diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-validation.component.ts index e5f8c0b2f..65d016e31 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/date-time-validation.component.ts @@ -5,17 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; -import { DateTimeFieldPropertiesDto, FieldDto, hasNoValue$, LanguageDto, TypedSimpleChanges } from '@app/shared'; +import { DateTimeEditorComponent, DateTimeFieldPropertiesDto, FieldDto, FormHintComponent, hasNoValue$, LanguageDto, LocalizedInputComponent, TranslatePipe, TypedSimpleChanges } from '@app/shared'; const CALCULATED_DEFAULT_VALUES: ReadonlyArray<string> = ['Now', 'Today']; @Component({ + standalone: true, selector: 'sqx-date-time-validation', styleUrls: ['date-time-validation.component.scss'], templateUrl: 'date-time-validation.component.html', + imports: [ + AsyncPipe, + DateTimeEditorComponent, + FormHintComponent, + FormsModule, + LocalizedInputComponent, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class DateTimeValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-ui.component.ts index 1b6e06ad6..f40187feb 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-ui.component.ts @@ -6,13 +6,19 @@ */ import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, GeolocationFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, GeolocationFieldPropertiesDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-geolocation-ui', styleUrls: ['geolocation-ui.component.scss'], templateUrl: 'geolocation-ui.component.html', + imports: [ + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class GeolocationUIComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-validation.component.ts index 25d98d341..0eab35d87 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/geolocation-validation.component.ts @@ -10,6 +10,7 @@ import { UntypedFormGroup } from '@angular/forms'; import { FieldDto, GeolocationFieldPropertiesDto } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-geolocation-validation', styleUrls: ['geolocation-validation.component.scss'], templateUrl: 'geolocation-validation.component.html', diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.ts index 6c7dbc842..7b1b80bc9 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.ts @@ -6,13 +6,21 @@ */ import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, JsonFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { CodeEditorComponent, FieldDto, FormHintComponent, JsonFieldPropertiesDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-json-more', styleUrls: ['json-more.component.scss'], templateUrl: 'json-more.component.html', + imports: [ + CodeEditorComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class JsonMoreComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/json-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/json-ui.component.ts index e8696f91c..cc85e1a11 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/json-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/json-ui.component.ts @@ -10,6 +10,7 @@ import { UntypedFormGroup } from '@angular/forms'; import { FieldDto, JsonFieldPropertiesDto } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-json-ui', styleUrls: ['json-ui.component.scss'], templateUrl: 'json-ui.component.html', diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/json-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/json-validation.component.ts index b835c9dc0..39e7ffde9 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/json-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/json-validation.component.ts @@ -10,6 +10,7 @@ import { UntypedFormGroup } from '@angular/forms'; import { FieldDto, JsonFieldPropertiesDto } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-json-validation', styleUrls: ['json-validation.component.scss'], templateUrl: 'json-validation.component.html', diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/number-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/number-ui.component.ts index db6b07065..48ef1cf83 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/number-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/number-ui.component.ts @@ -5,15 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; -import { FieldDto, FloatConverter, NUMBER_FIELD_EDITORS, NumberFieldPropertiesDto, Subscriptions, TypedSimpleChanges, valueProjection$ } from '@app/shared'; +import { FieldDto, FloatConverter, FormHintComponent, NUMBER_FIELD_EDITORS, NumberFieldPropertiesDto, Subscriptions, TagEditorComponent, TranslatePipe, TypedSimpleChanges, valueProjection$ } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-number-ui', styleUrls: ['number-ui.component.scss'], templateUrl: 'number-ui.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + FormsModule, + NgFor, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class NumberUIComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/number-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/number-validation.component.ts index 46424ebd2..beb82393c 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/number-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/number-validation.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, LanguageDto, NumberFieldPropertiesDto, RootFieldDto, SchemaDto, Types } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, LanguageDto, LocalizedInputComponent, NumberFieldPropertiesDto, RootFieldDto, SchemaDto, TranslatePipe, Types } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-number-validation', styleUrls: ['number-validation.component.scss'], templateUrl: 'number-validation.component.html', + imports: [ + FormHintComponent, + FormsModule, + LocalizedInputComponent, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class NumberValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/references-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/references-ui.component.ts index 8d38696bd..fc5308a8d 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/references-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/references-ui.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, REFERENCES_FIELD_EDITORS, ReferencesFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, REFERENCES_FIELD_EDITORS, ReferencesFieldPropertiesDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-references-ui', styleUrls: ['references-ui.component.scss'], templateUrl: 'references-ui.component.html', + imports: [ + FormHintComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class ReferencesUIComponent { public readonly editors = REFERENCES_FIELD_EDITORS; diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.ts index dcbb635d0..5851fd9b1 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.ts @@ -5,14 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, LanguageDto, ReferencesFieldPropertiesDto, SchemaTagSource } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, LanguageDto, LocalizedInputComponent, ReferencesFieldPropertiesDto, SchemaTagSource, TagEditorComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-references-validation', styleUrls: ['references-validation.component.scss'], templateUrl: 'references-validation.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + FormsModule, + LocalizedInputComponent, + NgIf, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class ReferencesValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts index 24f89b1e4..fcad980b2 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts @@ -5,15 +5,27 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; -import { FieldDto, SchemaTagSource, STRING_FIELD_EDITORS, StringFieldPropertiesDto, Subscriptions, TypedSimpleChanges, valueProjection$ } from '@app/shared'; +import { AssetFolderDropdownComponent, FieldDto, FormHintComponent, SchemaTagSource, STRING_FIELD_EDITORS, StringFieldPropertiesDto, Subscriptions, TagEditorComponent, TranslatePipe, TypedSimpleChanges, valueProjection$ } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-string-ui', styleUrls: ['string-ui.component.scss'], templateUrl: 'string-ui.component.html', + imports: [ + AssetFolderDropdownComponent, + AsyncPipe, + FormHintComponent, + FormsModule, + NgFor, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class StringUIComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/string-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/string-validation.component.ts index d1973b740..a34b033c8 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/string-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/string-validation.component.ts @@ -5,15 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; -import { AppSettingsDto, FieldDto, hasNoValue$, hasValue$, LanguageDto, ModalModel, PatternDto, RootFieldDto, SchemaDto, STRING_CONTENT_TYPES, StringFieldPropertiesDto, Subscriptions, TypedSimpleChanges, Types, value$ } from '@app/shared'; +import { AppSettingsDto, DropdownMenuComponent, FieldDto, FormHintComponent, hasNoValue$, hasValue$, LanguageDto, LocalizedInputComponent, ModalDirective, ModalModel, ModalPlacementDirective, PatternDto, RootFieldDto, SchemaDto, STRING_CONTENT_TYPES, StringFieldPropertiesDto, Subscriptions, TranslatePipe, TypedSimpleChanges, Types, value$ } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-string-validation', styleUrls: ['string-validation.component.scss'], templateUrl: 'string-validation.component.html', + imports: [ + AsyncPipe, + DropdownMenuComponent, + FormHintComponent, + FormsModule, + LocalizedInputComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class StringValidationComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/tags-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/tags-ui.component.ts index 59453779c..93823133f 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/tags-ui.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/tags-ui.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, TAGS_FIELD_EDITORS, TagsFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, TagEditorComponent, TAGS_FIELD_EDITORS, TagsFieldPropertiesDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-tags-ui', styleUrls: ['tags-ui.component.scss'], templateUrl: 'tags-ui.component.html', + imports: [ + FormHintComponent, + FormsModule, + NgFor, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class TagsUIComponent { public readonly editors = TAGS_FIELD_EDITORS; diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/tags-validation.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/tags-validation.component.ts index 5dccec6df..51fec1a86 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/tags-validation.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/tags-validation.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { FieldDto, LanguageDto, TagsFieldPropertiesDto } from '@app/shared'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { FieldDto, FormHintComponent, LanguageDto, LocalizedInputComponent, TagEditorComponent, TagsFieldPropertiesDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-tags-validation', styleUrls: ['tags-validation.component.scss'], templateUrl: 'tags-validation.component.html', + imports: [ + FormHintComponent, + FormsModule, + LocalizedInputComponent, + NgIf, + ReactiveFormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class TagsValidationComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts b/frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts index 6cf190d8b..ce2eee6a0 100644 --- a/frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts @@ -5,14 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, Input, OnInit } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, Observable, shareReplay } from 'rxjs'; -import { ConfigurePreviewUrlsForm, SchemaDto, SchemasService, SchemasState, ScriptCompletions } from '@app/shared'; +import { CodeEditorComponent, ConfigurePreviewUrlsForm, ConfirmClickDirective, ControlErrorsComponent, FormAlertComponent, SchemaDto, SchemasService, SchemasState, ScriptCompletions, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-preview-urls-form', styleUrls: ['./schema-preview-urls-form.component.scss'], templateUrl: './schema-preview-urls-form.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + ConfirmClickDirective, + ControlErrorsComponent, + FormAlertComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class SchemaPreviewUrlsFormComponent implements OnInit { @Input() diff --git a/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts b/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts index 7f983acd3..3e49bc3a4 100644 --- a/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts @@ -5,14 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, Input, OnInit } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, Observable, shareReplay } from 'rxjs'; -import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDto, SchemasService, SchemasState, ScriptCompletions } from '@app/shared'; +import { TranslatePipe } from '@app/framework'; +import { CodeEditorComponent, ConfigureFieldRulesForm, ConfirmClickDirective, ControlErrorsComponent, FIELD_RULE_ACTIONS, SchemaDto, SchemasService, SchemasState, ScriptCompletions } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-field-rules-form', styleUrls: ['./schema-field-rules-form.component.scss'], templateUrl: './schema-field-rules-form.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + ConfirmClickDirective, + ControlErrorsComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class SchemaFieldRulesFormComponent implements OnInit { @Input() diff --git a/frontend/src/app/features/schemas/pages/schema/schema-page.component.ts b/frontend/src/app/features/schemas/pages/schema/schema-page.component.ts index 99b56c5c3..d655d5ecb 100644 --- a/frontend/src/app/features/schemas/pages/schema/schema-page.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/schema-page.component.ts @@ -5,16 +5,53 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { map } from 'rxjs/operators'; -import { defined, MessageBus, ModalModel, SchemaDto, SchemasState, Subscriptions } from '@app/shared'; -import { SchemaCloning } from './../messages'; +import { ConfirmClickDirective, defined, DropdownMenuComponent, LayoutComponent, ListViewComponent, MessageBus, ModalDirective, ModalModel, ModalPlacementDirective, SchemaDto, SchemasState, SidebarMenuDirective, Subscriptions, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { SchemaCloning } from '../messages'; +import { SchemaEditFormComponent } from './common/schema-edit-form.component'; +import { SchemaExportFormComponent } from './export/schema-export-form.component'; +import { SchemaFieldsComponent } from './fields/schema-fields.component'; +import { SchemaPreviewUrlsFormComponent } from './preview/schema-preview-urls-form.component'; +import { SchemaFieldRulesFormComponent } from './rules/schema-field-rules-form.component'; +import { SchemaScriptsFormComponent } from './scripts/schema-scripts-form.component'; +import { SchemaUIFormComponent } from './ui/schema-ui-form.component'; @Component({ + standalone: true, selector: 'sqx-schema-page', styleUrls: ['./schema-page.component.scss'], templateUrl: './schema-page.component.html', + imports: [ + AsyncPipe, + ConfirmClickDirective, + DropdownMenuComponent, + LayoutComponent, + ListViewComponent, + ModalDirective, + ModalPlacementDirective, + NgIf, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + RouterLink, + RouterLinkActive, + RouterOutlet, + SchemaEditFormComponent, + SchemaExportFormComponent, + SchemaFieldRulesFormComponent, + SchemaFieldsComponent, + SchemaPreviewUrlsFormComponent, + SchemaScriptsFormComponent, + SchemaUIFormComponent, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class SchemaPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts index 3e3805f04..1c02d09de 100644 --- a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts @@ -5,14 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, Observable, shareReplay } from 'rxjs'; -import { AppsState, EditSchemaScriptsForm, SchemaDto, SchemasService, SchemasState, ScriptCompletions } from '@app/shared'; +import { AppsState, CodeEditorComponent, EditSchemaScriptsForm, KeysPipe, SchemaDto, SchemasService, SchemasState, ScriptCompletions, ScriptNamePipe, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-scripts-form', styleUrls: ['./schema-scripts-form.component.scss'], templateUrl: './schema-scripts-form.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + FormsModule, + KeysPipe, + NgFor, + NgIf, + ReactiveFormsModule, + ScriptNamePipe, + TranslatePipe, + ], }) export class SchemaScriptsFormComponent { @Input() diff --git a/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts b/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts index 74db189b6..3e78a121c 100644 --- a/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts @@ -5,17 +5,27 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { META_FIELDS, SchemaDto, TableField } from '@app/shared'; +import { FormAlertComponent, META_FIELDS, SchemaDto, TableField, TranslatePipe } from '@app/shared'; const META_FIELD_NAMES = Object.values(META_FIELDS).filter(x => x !== META_FIELDS.empty); @Component({ + standalone: true, selector: 'sqx-field-list', styleUrls: ['./field-list.component.scss'], templateUrl: './field-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CdkDrag, + CdkDropList, + FormAlertComponent, + NgFor, + NgIf, + TranslatePipe, + ], }) export class FieldListComponent { @Input() diff --git a/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts b/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts index f352bfaf4..7d55f1742 100644 --- a/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts @@ -6,12 +6,20 @@ */ import { Component, Input } from '@angular/core'; -import { SchemaDto, SchemasState } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { SchemaDto, SchemasState, TranslatePipe } from '@app/shared'; +import { FieldListComponent } from './field-list.component'; @Component({ + standalone: true, selector: 'sqx-schema-ui-form', styleUrls: ['./schema-ui-form.component.scss'], templateUrl: './schema-ui-form.component.html', + imports: [ + FieldListComponent, + FormsModule, + TranslatePipe, + ], }) export class SchemaUIFormComponent { @Input() diff --git a/frontend/src/app/features/schemas/pages/schemas/schema-form.component.ts b/frontend/src/app/features/schemas/pages/schemas/schema-form.component.ts index 47fb70479..a8a2fbd6e 100644 --- a/frontend/src/app/features/schemas/pages/schemas/schema-form.component.ts +++ b/frontend/src/app/features/schemas/pages/schemas/schema-form.component.ts @@ -5,13 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ApiUrlConfig, AppsState, CreateSchemaForm, SchemaDto, SchemasState } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ApiUrlConfig, AppsState, CodeEditorComponent, ControlErrorsComponent, CreateSchemaForm, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, ModalDialogComponent, SchemaDto, SchemasState, TooltipDirective, TransformInputDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-schema-form', styleUrls: ['./schema-form.component.scss'], templateUrl: './schema-form.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + ControlErrorsComponent, + FocusOnInitDirective, + FormAlertComponent, + FormErrorComponent, + FormHintComponent, + FormsModule, + ModalDialogComponent, + NgFor, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TransformInputDirective, + TranslatePipe, + ], }) export class SchemaFormComponent implements OnInit { @Output() diff --git a/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.ts b/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.ts index 4fb24bfdf..7cf4267e8 100644 --- a/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.ts +++ b/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.ts @@ -5,18 +5,40 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { CdkDropListGroup } from '@angular/cdk/drag-drop'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { UntypedFormControl } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { CreateCategoryForm, DialogModel, getCategoryTree, MessageBus, SchemaCategory, SchemaDto, SchemasState, Subscriptions, value$ } from '@app/shared'; -import { SchemaCloning } from './../messages'; +import { CreateCategoryForm, DialogModel, getCategoryTree, LayoutComponent, MessageBus, ModalDirective, SchemaCategory, SchemaCategoryComponent, SchemaDto, SchemasState, ShortcutDirective, Subscriptions, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, value$ } from '@app/shared'; +import { SchemaCloning } from '../messages'; +import { SchemaFormComponent } from './schema-form.component'; @Component({ + standalone: true, selector: 'sqx-schemas-page', styleUrls: ['./schemas-page.component.scss'], templateUrl: './schemas-page.component.html', + imports: [ + AsyncPipe, + CdkDropListGroup, + FormsModule, + LayoutComponent, + ModalDirective, + NgFor, + NgIf, + ReactiveFormsModule, + RouterOutlet, + SchemaCategoryComponent, + SchemaFormComponent, + ShortcutDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class SchemasPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/schemas/routes.ts b/frontend/src/app/features/schemas/routes.ts new file mode 100644 index 000000000..117dd4289 --- /dev/null +++ b/frontend/src/app/features/schemas/routes.ts @@ -0,0 +1,42 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Routes } from '@angular/router'; +import { HelpComponent, HistoryComponent, loadSchemasGuard, schemaMustExistGuard } from '@app/shared'; +import { SchemaPageComponent } from './pages/schema/schema-page.component'; +import { SchemasPageComponent } from './pages/schemas/schemas-page.component'; + +export const SCHEMAS_ROUTES: Routes = [ + { + path: '', + component: SchemasPageComponent, + canActivate: [loadSchemasGuard], + children: [ + { + path: ':schemaName', + component: SchemaPageComponent, + canActivate: [schemaMustExistGuard], + children: [ + { + path: 'help', + component: HelpComponent, + data: { + helpPage: '05-integrated/schemas', + }, + }, + { + path: 'history', + component: HistoryComponent, + data: { + channel: 'schemas.{schemaId}', + }, + }, + ], + }, + ], + }, +]; \ No newline at end of file diff --git a/frontend/src/app/features/settings/declarations.ts b/frontend/src/app/features/settings/declarations.ts deleted file mode 100644 index 96279d79e..000000000 --- a/frontend/src/app/features/settings/declarations.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/asset-scripts/asset-scripts-page.component'; -export * from './pages/backups/backup.component'; -export * from './pages/backups/backups-page.component'; -export * from './pages/clients/client-add-form.component'; -export * from './pages/clients/client-connect-form.component'; -export * from './pages/clients/client.component'; -export * from './pages/clients/clients-page.component'; -export * from './pages/contributors/contributor-add-form.component'; -export * from './pages/contributors/contributor.component'; -export * from './pages/contributors/contributors-page.component'; -export * from './pages/contributors/import-contributors-dialog.component'; -export * from './pages/languages/language-add-form.component'; -export * from './pages/languages/language.component'; -export * from './pages/languages/languages-page.component'; -export * from './pages/more/more-page.component'; -export * from './pages/plans/plan.component'; -export * from './pages/plans/plans-page.component'; -export * from './pages/roles/role-add-form.component'; -export * from './pages/roles/role.component'; -export * from './pages/roles/roles-page.component'; -export * from './pages/settings/settings-page.component'; -export * from './pages/templates/template.component'; -export * from './pages/templates/templates-page.component'; -export * from './pages/workflows/workflow-add-form.component'; -export * from './pages/workflows/workflow-diagram.component'; -export * from './pages/workflows/workflow-step.component'; -export * from './pages/workflows/workflow-transition.component'; -export * from './pages/workflows/workflow.component'; -export * from './pages/workflows/workflows-page.component'; -export * from './settings-area.component'; -export * from './settings-menu.component'; diff --git a/frontend/src/app/features/settings/index.ts b/frontend/src/app/features/settings/index.ts deleted file mode 100644 index 898be9a7c..000000000 --- a/frontend/src/app/features/settings/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './declarations'; -export * from './module'; diff --git a/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.ts b/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.ts index 6117db245..c71d5fe13 100644 --- a/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.ts +++ b/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.ts @@ -5,14 +5,39 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { EMPTY, Observable, shareReplay } from 'rxjs'; -import { AppsState, AssetScriptsState, AssetsService, EditAssetScriptsForm, ScriptCompletions } from '@app/shared'; +import { AppsState, AssetScriptsState, AssetsService, CodeEditorComponent, EditAssetScriptsForm, KeysPipe, LayoutComponent, ScriptCompletions, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { ScriptNamePipe } from '@app/shared/components/pipes'; @Component({ + standalone: true, selector: 'sqx-asset-scripts-page', styleUrls: ['./asset-scripts-page.component.scss'], templateUrl: './asset-scripts-page.component.html', + imports: [ + AsyncPipe, + CodeEditorComponent, + FormsModule, + KeysPipe, + LayoutComponent, + NgFor, + NgIf, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + ScriptNamePipe, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class AssetScriptsPageComponent implements OnInit { public assetScript = 'query'; diff --git a/frontend/src/app/features/settings/pages/backups/backup.component.ts b/frontend/src/app/features/settings/pages/backups/backup.component.ts index 26e415615..afa9f246c 100644 --- a/frontend/src/app/features/settings/pages/backups/backup.component.ts +++ b/frontend/src/app/features/settings/pages/backups/backup.component.ts @@ -5,14 +5,27 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf, NgSwitch } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { ApiUrlConfig, BackupDto, BackupsState, Duration, TypedSimpleChanges } from '@app/shared'; +import { ApiUrlConfig, BackupDto, BackupsState, ConfirmClickDirective, Duration, ExternalLinkDirective, FromNowPipe, KNumberPipe, StatusIconComponent, TooltipDirective, TranslatePipe, TypedSimpleChanges } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-backup', styleUrls: ['./backup.component.scss'], templateUrl: './backup.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + ExternalLinkDirective, + FromNowPipe, + KNumberPipe, + NgIf, + NgSwitch, + StatusIconComponent, + TooltipDirective, + TranslatePipe, + ], }) export class BackupComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/backups/backups-page.component.ts b/frontend/src/app/features/settings/pages/backups/backups-page.component.ts index 531c4c69c..e6e3ad059 100644 --- a/frontend/src/app/features/settings/pages/backups/backups-page.component.ts +++ b/frontend/src/app/features/settings/pages/backups/backups-page.component.ts @@ -5,15 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { timer } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { ApiUrlConfig, BackupDto, BackupsState, Subscriptions } from '@app/shared'; +import { ApiUrlConfig, BackupDto, BackupsState, LayoutComponent, ListViewComponent, ShortcutDirective, SidebarMenuDirective, Subscriptions, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { BackupComponent } from './backup.component'; @Component({ + standalone: true, selector: 'sqx-backups-page', styleUrls: ['./backups-page.component.scss'], templateUrl: './backups-page.component.html', + imports: [ + AsyncPipe, + BackupComponent, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class BackupsPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/settings/pages/clients/client-add-form.component.ts b/frontend/src/app/features/settings/pages/clients/client-add-form.component.ts index 2283b9971..07b6c5e09 100644 --- a/frontend/src/app/features/settings/pages/clients/client-add-form.component.ts +++ b/frontend/src/app/features/settings/pages/clients/client-add-form.component.ts @@ -5,14 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { AddClientForm, ClientsState } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AddClientForm, ClientsState, ControlErrorsComponent, FormHintComponent, TransformInputDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-client-add-form', styleUrls: ['./client-add-form.component.scss'], templateUrl: './client-add-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TransformInputDirective, + TranslatePipe, + ], }) export class ClientAddFormComponent { public addClientForm = new AddClientForm(); diff --git a/frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts b/frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts index 0760a0aa8..71c78b4e3 100644 --- a/frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts +++ b/frontend/src/app/features/settings/pages/clients/client-connect-form.component.ts @@ -5,13 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, KeyValuePipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AccessTokenDto, ApiUrlConfig, AppsState, ClientDto, ClientsService, ClientTourStated, DialogService, HelpService, MessageBus, SDKEntry } from '@app/shared'; +import { AccessTokenDto, ApiUrlConfig, AppsState, ClientDto, ClientsService, ClientTourStated, CodeComponent, DialogService, ExternalLinkDirective, FormHintComponent, HelpService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, SafeHtmlPipe, SDKEntry, TooltipDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-client-connect-form', styleUrls: ['./client-connect-form.component.scss'], templateUrl: './client-connect-form.component.html', + imports: [ + AsyncPipe, + CodeComponent, + ExternalLinkDirective, + FormHintComponent, + KeyValuePipe, + MarkdownDirective, + MarkdownInlinePipe, + MarkdownPipe, + ModalDialogComponent, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + SafeHtmlPipe, + TooltipDirective, + TranslatePipe, + ], }) export class ClientConnectFormComponent implements OnInit { @Output() diff --git a/frontend/src/app/features/settings/pages/clients/client.component.ts b/frontend/src/app/features/settings/pages/clients/client.component.ts index c356f014a..ffffef312 100644 --- a/frontend/src/app/features/settings/pages/clients/client.component.ts +++ b/frontend/src/app/features/settings/pages/clients/client.component.ts @@ -5,14 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { AppsState, ClientDto, ClientsState, DialogModel, RoleDto, TypedSimpleChanges } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { AppsState, ClientDto, ClientsState, ConfirmClickDirective, CopyDirective, DialogModel, EditableTitleComponent, FormHintComponent, ModalDirective, RoleDto, TourStepDirective, TranslatePipe, TypedSimpleChanges } from '@app/shared'; +import { ClientConnectFormComponent } from './client-connect-form.component'; @Component({ + standalone: true, selector: 'sqx-client', styleUrls: ['./client.component.scss'], templateUrl: './client.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ClientConnectFormComponent, + ConfirmClickDirective, + CopyDirective, + EditableTitleComponent, + FormHintComponent, + FormsModule, + ModalDirective, + NgFor, + NgIf, + TourStepDirective, + TranslatePipe, + ], }) export class ClientComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/clients/clients-page.component.ts b/frontend/src/app/features/settings/pages/clients/clients-page.component.ts index e158984de..1c1004597 100644 --- a/frontend/src/app/features/settings/pages/clients/clients-page.component.ts +++ b/frontend/src/app/features/settings/pages/clients/clients-page.component.ts @@ -5,13 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ClientDto, ClientsState, RolesState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ClientDto, ClientsState, LayoutComponent, ListViewComponent, RolesState, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { ClientAddFormComponent } from './client-add-form.component'; +import { ClientComponent } from './client.component'; @Component({ + standalone: true, selector: 'sqx-clients-page', styleUrls: ['./clients-page.component.scss'], templateUrl: './clients-page.component.html', + imports: [ + AsyncPipe, + ClientAddFormComponent, + ClientComponent, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class ClientsPageComponent implements OnInit { constructor( diff --git a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts index 55bc3ae9b..c7c2719a1 100644 --- a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts +++ b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts @@ -5,10 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { Component, Injectable, Input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { withLatestFrom } from 'rxjs/operators'; -import { AssignContributorForm, AutocompleteSource, ContributorsState, DialogModel, DialogService, RoleDto, UsersService } from '@app/shared'; +import { AssignContributorForm, AutocompleteComponent, AutocompleteSource, ContributorsState, DialogModel, DialogService, FormHintComponent, ModalDirective, RoleDto, TranslatePipe, UserDtoPicture, UsersService } from '@app/shared'; +import { ImportContributorsDialogComponent } from './import-contributors-dialog.component'; @Injectable() export class UsersDataSource implements AutocompleteSource { @@ -38,12 +41,25 @@ export class UsersDataSource implements AutocompleteSource { } @Component({ + standalone: true, selector: 'sqx-contributor-add-form', styleUrls: ['./contributor-add-form.component.scss'], templateUrl: './contributor-add-form.component.html', providers: [ UsersDataSource, ], + imports: [ + AsyncPipe, + AutocompleteComponent, + FormHintComponent, + FormsModule, + ImportContributorsDialogComponent, + ModalDirective, + NgFor, + ReactiveFormsModule, + TranslatePipe, + UserDtoPicture, + ], }) export class ContributorAddFormComponent { private defaultValue: any; diff --git a/frontend/src/app/features/settings/pages/contributors/contributor.component.ts b/frontend/src/app/features/settings/pages/contributors/contributor.component.ts index 37cf91988..d2bccd77c 100644 --- a/frontend/src/app/features/settings/pages/contributors/contributor.component.ts +++ b/frontend/src/app/features/settings/pages/contributors/contributor.component.ts @@ -7,14 +7,25 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgFor } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { ContributorDto, ContributorsState, RoleDto } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { ConfirmClickDirective, ContributorDto, ContributorsState, HighlightPipe, RoleDto, TooltipDirective, UserPicturePipe } from '@app/shared'; @Component({ + standalone: true, selector: '[sqxContributor][roles]', styleUrls: ['./contributor.component.scss'], templateUrl: './contributor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + FormsModule, + HighlightPipe, + NgFor, + TooltipDirective, + UserPicturePipe, + ], }) export class ContributorComponent { @Input() diff --git a/frontend/src/app/features/settings/pages/contributors/contributors-page.component.ts b/frontend/src/app/features/settings/pages/contributors/contributors-page.component.ts index 5d8617634..0549c0ac1 100644 --- a/frontend/src/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/frontend/src/app/features/settings/pages/contributors/contributors-page.component.ts @@ -5,16 +5,43 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ContributorDto, ContributorsState, RolesState, Router2State } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ContributorDto, ContributorsState, LayoutComponent, ListViewComponent, NotifoComponent, PagerComponent, RolesState, Router2State, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { ContributorAddFormComponent } from './contributor-add-form.component'; +import { ContributorComponent } from './contributor.component'; @Component({ + standalone: true, selector: 'sqx-contributors-page', styleUrls: ['./contributors-page.component.scss'], templateUrl: './contributors-page.component.html', providers: [ Router2State, ], + imports: [ + AsyncPipe, + ContributorAddFormComponent, + ContributorComponent, + FormsModule, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + NotifoComponent, + PagerComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class ContributorsPageComponent implements OnInit { constructor( diff --git a/frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.ts b/frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.ts index f454160c3..c6436e8bf 100644 --- a/frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.ts +++ b/frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.ts @@ -5,10 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, of } from 'rxjs'; import { catchError, mergeMap, tap } from 'rxjs/operators'; -import { ContributorsState, ErrorDto, ImportContributorsForm, RoleDto } from '@app/shared'; +import { ContributorsState, ErrorDto, FormHintComponent, ImportContributorsForm, ModalDialogComponent, RoleDto, StatusIconComponent, TooltipDirective, TranslatePipe } from '@app/shared'; type ImportStatus = { email: string; @@ -18,9 +20,24 @@ type ImportStatus = { }; @Component({ + standalone: true, selector: 'sqx-import-contributors-dialog', styleUrls: ['./import-contributors-dialog.component.scss'], templateUrl: './import-contributors-dialog.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + FormsModule, + ModalDialogComponent, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + ReactiveFormsModule, + StatusIconComponent, + TooltipDirective, + TranslatePipe, + ], }) export class ImportContributorsDialogComponent { @Output() diff --git a/frontend/src/app/features/settings/pages/languages/language-add-form.component.ts b/frontend/src/app/features/settings/pages/languages/language-add-form.component.ts index 0cb51e3cb..223c2820c 100644 --- a/frontend/src/app/features/settings/pages/languages/language-add-form.component.ts +++ b/frontend/src/app/features/settings/pages/languages/language-add-form.component.ts @@ -6,8 +6,9 @@ */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable, of } from 'rxjs'; -import { AddLanguageForm, AutocompleteSource, LanguageDto, LanguagesState } from '@app/shared'; +import { AddLanguageForm, AutocompleteComponent, AutocompleteSource, FormHintComponent, LanguageDto, LanguagesState, TranslatePipe } from '@app/shared'; class LanguageSource implements AutocompleteSource { constructor( @@ -39,10 +40,18 @@ class LanguageSource implements AutocompleteSource { } @Component({ + standalone: true, selector: 'sqx-language-add-form', styleUrls: ['./language-add-form.component.scss'], templateUrl: './language-add-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AutocompleteComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class LanguageAddFormComponent { @Input() diff --git a/frontend/src/app/features/settings/pages/languages/language.component.ts b/frontend/src/app/features/settings/pages/languages/language.component.ts index 07da23afd..8e9ee0d0c 100644 --- a/frontend/src/app/features/settings/pages/languages/language.component.ts +++ b/frontend/src/app/features/settings/pages/languages/language.component.ts @@ -5,14 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { AppLanguageDto, EditLanguageForm, LanguageDto, LanguagesState, sorted } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AppLanguageDto, ConfirmClickDirective, EditLanguageForm, FormHintComponent, LanguageDto, LanguagesState, sorted, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-language', styleUrls: ['./language.component.scss'], templateUrl: './language.component.html', + imports: [ + CdkDrag, + CdkDragHandle, + CdkDropList, + ConfirmClickDirective, + FormHintComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + TranslatePipe, + ], }) export class LanguageComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/languages/languages-page.component.ts b/frontend/src/app/features/settings/pages/languages/languages-page.component.ts index b17621ab6..755f4d6a0 100644 --- a/frontend/src/app/features/settings/pages/languages/languages-page.component.ts +++ b/frontend/src/app/features/settings/pages/languages/languages-page.component.ts @@ -5,13 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { LanguagesState, SnapshotLanguage } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { LanguagesState, LayoutComponent, ListViewComponent, ShortcutDirective, SidebarMenuDirective, SnapshotLanguage, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { LanguageAddFormComponent } from './language-add-form.component'; +import { LanguageComponent } from './language.component'; @Component({ + standalone: true, selector: 'sqx-languages-page', styleUrls: ['./languages-page.component.scss'], templateUrl: './languages-page.component.html', + imports: [ + AsyncPipe, + LanguageAddFormComponent, + LanguageComponent, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class LanguagesPageComponent implements OnInit { constructor( diff --git a/frontend/src/app/features/settings/pages/more/more-page.component.ts b/frontend/src/app/features/settings/pages/more/more-page.component.ts index 694296137..a42dbe780 100644 --- a/frontend/src/app/features/settings/pages/more/more-page.component.ts +++ b/frontend/src/app/features/settings/pages/more/more-page.component.ts @@ -5,14 +5,40 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { AppDto, AppsState, defined, DialogService, Subscriptions, TeamsState, TransferAppForm, Types, UpdateAppForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { AppDto, AppsState, AvatarComponent, ConfirmClickDirective, ControlErrorsComponent, defined, DialogService, FileDropDirective, FormErrorComponent, FormHintComponent, LayoutComponent, ListViewComponent, ProgressBarComponent, SidebarMenuDirective, Subscriptions, TeamsState, TooltipDirective, TourStepDirective, TransferAppForm, TranslatePipe, Types, UpdateAppForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-more-page', styleUrls: ['./more-page.component.scss'], templateUrl: './more-page.component.html', + imports: [ + AsyncPipe, + AvatarComponent, + ConfirmClickDirective, + ControlErrorsComponent, + FileDropDirective, + FormErrorComponent, + FormHintComponent, + FormsModule, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + ProgressBarComponent, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + SidebarMenuDirective, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class MorePageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/settings/pages/plans/plan.component.ts b/frontend/src/app/features/settings/pages/plans/plan.component.ts index 89a50f2f3..ec41464e8 100644 --- a/frontend/src/app/features/settings/pages/plans/plan.component.ts +++ b/frontend/src/app/features/settings/pages/plans/plan.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { PlanInfo, PlansState } from '@app/shared'; +import { ConfirmClickDirective, FileSizePipe, FormHintComponent, KNumberPipe, PlanInfo, PlansState, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-plan', styleUrls: ['./plan.component.scss'], templateUrl: './plan.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ConfirmClickDirective, + FileSizePipe, + FormHintComponent, + KNumberPipe, + NgIf, + TranslatePipe, + ], }) export class PlanComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/plans/plans-page.component.ts b/frontend/src/app/features/settings/pages/plans/plans-page.component.ts index 6f6521698..474bb4211 100644 --- a/frontend/src/app/features/settings/pages/plans/plans-page.component.ts +++ b/frontend/src/app/features/settings/pages/plans/plans-page.component.ts @@ -5,14 +5,40 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { ApiUrlConfig, PlanDto, PlansState } from '@app/shared'; +import { ActivatedRoute, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ApiUrlConfig, ExternalLinkDirective, FormHintComponent, LayoutComponent, ListViewComponent, MarkdownPipe, NotifoComponent, PlanDto, PlansState, SafeHtmlPipe, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, UserNamePipe } from '@app/shared'; +import { PlanComponent } from './plan.component'; @Component({ + standalone: true, selector: 'sqx-plans-page', styleUrls: ['./plans-page.component.scss'], templateUrl: './plans-page.component.html', + imports: [ + AsyncPipe, + ExternalLinkDirective, + FormHintComponent, + LayoutComponent, + ListViewComponent, + MarkdownPipe, + NgFor, + NgIf, + NotifoComponent, + PlanComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + SafeHtmlPipe, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + UserNamePipe, + ], }) export class PlansPageComponent implements OnInit { private overridePlanId?: string; diff --git a/frontend/src/app/features/settings/pages/roles/role-add-form.component.ts b/frontend/src/app/features/settings/pages/roles/role-add-form.component.ts index 4de8d531d..0a4c4331c 100644 --- a/frontend/src/app/features/settings/pages/roles/role-add-form.component.ts +++ b/frontend/src/app/features/settings/pages/roles/role-add-form.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { AddRoleForm, RolesState } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AddRoleForm, ControlErrorsComponent, FormHintComponent, RolesState, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-role-add-form', styleUrls: ['./role-add-form.component.scss'], templateUrl: './role-add-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class RoleAddFormComponent { public addRoleForm = new AddRoleForm(); diff --git a/frontend/src/app/features/settings/pages/roles/role.component.ts b/frontend/src/app/features/settings/pages/roles/role.component.ts index b17dbc8ad..6b9950e6c 100644 --- a/frontend/src/app/features/settings/pages/roles/role.component.ts +++ b/frontend/src/app/features/settings/pages/roles/role.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf, SlicePipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input, ViewChild } from '@angular/core'; -import { AutocompleteComponent, AutocompleteSource, EditRoleForm, RoleDto, RolesState, SchemaDto, Settings, TypedSimpleChanges } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AutocompleteComponent, AutocompleteSource, ConfirmClickDirective, ControlErrorsComponent, EditRoleForm, FormAlertComponent, FormHintComponent, RoleDto, RolesState, SchemaDto, Settings, TranslatePipe, TypedSimpleChanges } from '@app/shared'; const DESCRIPTIONS = { Developer: 'i18n:roles.defaults.developer', @@ -32,10 +34,24 @@ const SIMPLE_PROPERTIES: ReadonlyArray<Property> = [{ }]; @Component({ + standalone: true, selector: 'sqx-role', styleUrls: ['./role.component.scss'], templateUrl: './role.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AutocompleteComponent, + ConfirmClickDirective, + ControlErrorsComponent, + FormAlertComponent, + FormHintComponent, + FormsModule, + NgFor, + NgIf, + ReactiveFormsModule, + SlicePipe, + TranslatePipe, + ], }) export class RoleComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/roles/roles-page.component.ts b/frontend/src/app/features/settings/pages/roles/roles-page.component.ts index 92babaf8c..0166d650f 100644 --- a/frontend/src/app/features/settings/pages/roles/roles-page.component.ts +++ b/frontend/src/app/features/settings/pages/roles/roles-page.component.ts @@ -7,9 +7,13 @@ /* eslint-disable no-return-assign */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { Observable, of } from 'rxjs'; -import { AppsState, AutocompleteSource, RoleDto, RolesService, RolesState, SchemasState } from '@app/shared'; +import { AppsState, AutocompleteSource, FormHintComponent, LayoutComponent, ListViewComponent, RoleDto, RolesService, RolesState, SchemasState, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { RoleAddFormComponent } from './role-add-form.component'; +import { RoleComponent } from './role.component'; class PermissionsAutocomplete implements AutocompleteSource { private permissions: ReadonlyArray<string> = []; @@ -28,9 +32,29 @@ class PermissionsAutocomplete implements AutocompleteSource { } @Component({ + standalone: true, selector: 'sqx-roles-page', styleUrls: ['./roles-page.component.scss'], templateUrl: './roles-page.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RoleAddFormComponent, + RoleComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class RolesPageComponent implements OnInit { public allPermissions: AutocompleteSource = new PermissionsAutocomplete(this.appsState, this.rolesService); diff --git a/frontend/src/app/features/settings/pages/settings/settings-page.component.ts b/frontend/src/app/features/settings/pages/settings/settings-page.component.ts index 3470eeccb..c9442abcc 100644 --- a/frontend/src/app/features/settings/pages/settings/settings-page.component.ts +++ b/frontend/src/app/features/settings/pages/settings/settings-page.component.ts @@ -5,13 +5,37 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { AppSettingsDto, AppsState, EditAppSettingsForm, Subscriptions } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { AppSettingsDto, AppsState, ConfirmClickDirective, ControlErrorsComponent, EditAppSettingsForm, FormHintComponent, LayoutComponent, ListViewComponent, ShortcutDirective, SidebarMenuDirective, Subscriptions, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-settings-page', styleUrls: ['./settings-page.component.scss'], templateUrl: './settings-page.component.html', + imports: [ + ConfirmClickDirective, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class SettingsPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/settings/pages/templates/template.component.ts b/frontend/src/app/features/settings/pages/templates/template.component.ts index 45e4d59c8..9bf1adae5 100644 --- a/frontend/src/app/features/settings/pages/templates/template.component.ts +++ b/frontend/src/app/features/settings/pages/templates/template.component.ts @@ -5,15 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { map, Observable, shareReplay } from 'rxjs'; -import { AppsState, ClientsState, TemplateDetailsDto, TemplateDto, TemplatesService } from '@app/shared'; +import { AppsState, ClientsState, FormHintComponent, LoaderComponent, MarkdownPipe, SafeHtmlPipe, TemplateDetailsDto, TemplateDto, TemplatesService, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-template', styleUrls: ['./template.component.scss'], templateUrl: './template.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + FormHintComponent, + LoaderComponent, + MarkdownPipe, + NgIf, + SafeHtmlPipe, + TranslatePipe, + ], }) export class TemplateComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/settings/pages/templates/templates-page.component.ts b/frontend/src/app/features/settings/pages/templates/templates-page.component.ts index d1c7e7d6a..9f96323ad 100644 --- a/frontend/src/app/features/settings/pages/templates/templates-page.component.ts +++ b/frontend/src/app/features/settings/pages/templates/templates-page.component.ts @@ -5,13 +5,37 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ClientsState, TemplateDto, TemplatesState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ClientsState, FormAlertComponent, LayoutComponent, ListViewComponent, MarkdownInlinePipe, SafeHtmlPipe, ShortcutDirective, SidebarMenuDirective, TemplateDto, TemplatesState, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { TemplateComponent } from './template.component'; @Component({ + standalone: true, selector: 'sqx-templates-page', styleUrls: ['./templates-page.component.scss'], templateUrl: './templates-page.component.html', + imports: [ + AsyncPipe, + FormAlertComponent, + LayoutComponent, + ListViewComponent, + MarkdownInlinePipe, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + SafeHtmlPipe, + ShortcutDirective, + SidebarMenuDirective, + TemplateComponent, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class TemplatesPageComponent implements OnInit { constructor( diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.ts index d876e82da..5782a601e 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { AddWorkflowForm, WorkflowsState } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AddWorkflowForm, ControlErrorsComponent, FormHintComponent, TranslatePipe, WorkflowsState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-workflow-add-form', styleUrls: ['./workflow-add-form.component.scss'], templateUrl: './workflow-add-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormHintComponent, + FormsModule, + ReactiveFormsModule, + TranslatePipe, + ], }) export class WorkflowAddFormComponent { public addWorkflowForm = new AddWorkflowForm(); diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts index e343160a7..5f0a25825 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts @@ -6,14 +6,18 @@ */ import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; -import { ResourceLoaderService, WorkflowDto } from '@app/shared'; +import { ListViewComponent, ResourceLoaderService, WorkflowDto } from '@app/shared'; declare const vis: any; @Component({ + standalone: true, selector: 'sqx-workflow-diagram', styleUrls: ['./workflow-diagram.component.scss'], templateUrl: './workflow-diagram.component.html', + imports: [ + ListViewComponent, + ], }) export class WorkflowDiagramComponent implements AfterViewInit, OnDestroy { private network: any; diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts index 73f8dcf09..af5a95aa4 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts @@ -5,13 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; -import { TypedSimpleChanges, WorkflowDto, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { ColorPickerComponent, DropdownComponent, EditableTitleComponent, TagEditorComponent, TranslatePipe, TypedSimpleChanges, WorkflowDto, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared'; +import { WorkflowTransitionComponent } from './workflow-transition.component'; @Component({ + standalone: true, selector: 'sqx-workflow-step', styleUrls: ['./workflow-step.component.scss'], templateUrl: './workflow-step.component.html', + imports: [ + ColorPickerComponent, + DropdownComponent, + EditableTitleComponent, + FormsModule, + NgFor, + NgIf, + TagEditorComponent, + TranslatePipe, + WorkflowTransitionComponent, + ], }) export class WorkflowStepComponent { public readonly onBlur: { updateOn: 'blur' } = { updateOn: 'blur' }; diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.ts index beaaa02b5..32c2b579d 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.ts @@ -6,12 +6,19 @@ */ import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core'; -import { RoleDto, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { RoleDto, TagEditorComponent, TranslatePipe, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-workflow-transition', styleUrls: ['./workflow-transition.component.scss'], templateUrl: './workflow-transition.component.html', + imports: [ + FormsModule, + TagEditorComponent, + TranslatePipe, + ], }) export class WorkflowTransitionComponent { public readonly onBlur: { updateOn: 'blur' } = { updateOn: 'blur' }; diff --git a/frontend/src/app/features/settings/pages/workflows/workflow.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow.component.ts index 18e68f387..c95542c5c 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflow.component.ts @@ -5,13 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { ErrorDto, MathHelper, SchemaTagSource, WorkflowDto, WorkflowsState, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { ConfirmClickDirective, ErrorDto, FormErrorComponent, FormHintComponent, MathHelper, SchemaTagSource, TagEditorComponent, TranslatePipe, WorkflowDto, WorkflowsState, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues } from '@app/shared'; +import { WorkflowDiagramComponent } from './workflow-diagram.component'; +import { WorkflowStepComponent } from './workflow-step.component'; @Component({ + standalone: true, selector: 'sqx-workflow', styleUrls: ['./workflow.component.scss'], templateUrl: './workflow.component.html', + imports: [ + AsyncPipe, + ConfirmClickDirective, + FormErrorComponent, + FormHintComponent, + FormsModule, + NgFor, + NgIf, + TagEditorComponent, + TranslatePipe, + WorkflowDiagramComponent, + WorkflowStepComponent, + ], }) export class WorkflowComponent { public readonly onBlur: { updateOn: 'blur' } = { updateOn: 'blur' }; diff --git a/frontend/src/app/features/settings/pages/workflows/workflows-page.component.ts b/frontend/src/app/features/settings/pages/workflows/workflows-page.component.ts index f82f2dc44..08d3a3783 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflows-page.component.ts +++ b/frontend/src/app/features/settings/pages/workflows/workflows-page.component.ts @@ -5,13 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { RolesState, SchemaTagSource, Subscriptions, WorkflowDto, WorkflowsState } from '@app/shared'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { LayoutComponent, ListViewComponent, RolesState, SchemaTagSource, ShortcutDirective, SidebarMenuDirective, Subscriptions, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe, WorkflowDto, WorkflowsState } from '@app/shared'; +import { WorkflowAddFormComponent } from './workflow-add-form.component'; +import { WorkflowComponent } from './workflow.component'; @Component({ + standalone: true, selector: 'sqx-workflows-page', styleUrls: ['./workflows-page.component.scss'], templateUrl: './workflows-page.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + WorkflowAddFormComponent, + WorkflowComponent, + ], }) export class WorkflowsPageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/settings/module.ts b/frontend/src/app/features/settings/routes.ts similarity index 76% rename from frontend/src/app/features/settings/module.ts rename to frontend/src/app/features/settings/routes.ts index dd8c416f8..3140bf28f 100644 --- a/frontend/src/app/features/settings/module.ts +++ b/frontend/src/app/features/settings/routes.ts @@ -5,12 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { HelpComponent, HistoryComponent, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AssetScriptsPageComponent, BackupComponent, BackupsPageComponent, ClientAddFormComponent, ClientComponent, ClientConnectFormComponent, ClientsPageComponent, ContributorAddFormComponent, ContributorComponent, ContributorsPageComponent, ImportContributorsDialogComponent, LanguageAddFormComponent, LanguageComponent, LanguagesPageComponent, MorePageComponent, PlanComponent, PlansPageComponent, RoleAddFormComponent, RoleComponent, RolesPageComponent, SettingsAreaComponent, SettingsMenuComponent, SettingsPageComponent, TemplateComponent, TemplatesPageComponent, WorkflowAddFormComponent, WorkflowComponent, WorkflowDiagramComponent, WorkflowsPageComponent, WorkflowStepComponent, WorkflowTransitionComponent } from './declarations'; +import { Routes } from '@angular/router'; +import { HelpComponent, HistoryComponent } from '@app/shared'; +import { AssetScriptsPageComponent } from './pages/asset-scripts/asset-scripts-page.component'; +import { BackupsPageComponent } from './pages/backups/backups-page.component'; +import { ClientsPageComponent } from './pages/clients/clients-page.component'; +import { ContributorsPageComponent } from './pages/contributors/contributors-page.component'; +import { LanguagesPageComponent } from './pages/languages/languages-page.component'; +import { MorePageComponent } from './pages/more/more-page.component'; +import { PlansPageComponent } from './pages/plans/plans-page.component'; +import { RolesPageComponent } from './pages/roles/roles-page.component'; +import { SettingsPageComponent } from './pages/settings/settings-page.component'; +import { TemplatesPageComponent } from './pages/templates/templates-page.component'; +import { WorkflowsPageComponent } from './pages/workflows/workflows-page.component'; +import { SettingsAreaComponent } from './settings-area.component'; -const routes: Routes = [ +export const SETTINGS_ROUTES: Routes = [ { path: '', component: SettingsAreaComponent, @@ -223,46 +233,4 @@ const routes: Routes = [ }, ], }, -]; - -@NgModule({ - imports: [ - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AssetScriptsPageComponent, - BackupComponent, - BackupsPageComponent, - ClientAddFormComponent, - ClientComponent, - ClientConnectFormComponent, - ClientsPageComponent, - ContributorAddFormComponent, - ContributorComponent, - ContributorsPageComponent, - ImportContributorsDialogComponent, - LanguageAddFormComponent, - LanguageComponent, - LanguagesPageComponent, - MorePageComponent, - PlanComponent, - PlansPageComponent, - RoleAddFormComponent, - RoleComponent, - RolesPageComponent, - SettingsAreaComponent, - SettingsMenuComponent, - SettingsPageComponent, - TemplateComponent, - TemplatesPageComponent, - WorkflowAddFormComponent, - WorkflowComponent, - WorkflowDiagramComponent, - WorkflowsPageComponent, - WorkflowStepComponent, - WorkflowTransitionComponent, - ], -}) -export class SqxFeatureSettingsModule {} +]; \ No newline at end of file diff --git a/frontend/src/app/features/settings/settings-area.component.ts b/frontend/src/app/features/settings/settings-area.component.ts index 57f7b495d..3aa58fc30 100644 --- a/frontend/src/app/features/settings/settings-area.component.ts +++ b/frontend/src/app/features/settings/settings-area.component.ts @@ -5,13 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; -import { AppsState, defined } from '@app/shared'; +import { RouterOutlet } from '@angular/router'; +import { AppsState, defined, LayoutComponent, TitleComponent } from '@app/shared'; +import { SettingsMenuComponent } from './settings-menu.component'; @Component({ + standalone: true, selector: 'sqx-settings-area', styleUrls: ['./settings-area.component.scss'], templateUrl: './settings-area.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + NgIf, + RouterOutlet, + SettingsMenuComponent, + TitleComponent, + ], }) export class SettingsAreaComponent { public selectedApp = this.appsState.selectedApp.pipe(defined()); diff --git a/frontend/src/app/features/settings/settings-menu.component.ts b/frontend/src/app/features/settings/settings-menu.component.ts index 5356cabe8..0f65cd798 100644 --- a/frontend/src/app/features/settings/settings-menu.component.ts +++ b/frontend/src/app/features/settings/settings-menu.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { AppDto } from '@app/shared'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { AppDto, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-settings-menu', styleUrls: ['./settings-menu.component.scss'], templateUrl: './settings-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + RouterLink, + RouterLinkActive, + TourStepDirective, + TranslatePipe, + ], }) export class SettingsMenuComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/teams/declarations.ts b/frontend/src/app/features/teams/declarations.ts deleted file mode 100644 index 132bf4c76..000000000 --- a/frontend/src/app/features/teams/declarations.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './left-menu.component'; -export * from './pages/contributors/contributor-add-form.component'; -export * from './pages/contributors/contributor.component'; -export * from './pages/contributors/contributors-page.component'; -export * from './pages/contributors/import-contributors-dialog.component'; -export * from './pages/dashboard/cards/apps-card.component'; -export * from './pages/dashboard/dashboard-page.component'; -export * from './pages/more/more-page.component'; -export * from './pages/plans/plan.component'; -export * from './pages/plans/plans-page.component'; -export * from './shared/settings-area.component'; -export * from './shared/settings-menu.component'; -export * from './team-area.component'; \ No newline at end of file diff --git a/frontend/src/app/features/teams/left-menu.component.ts b/frontend/src/app/features/teams/left-menu.component.ts index 9ea4cf236..02dd02c91 100644 --- a/frontend/src/app/features/teams/left-menu.component.ts +++ b/frontend/src/app/features/teams/left-menu.component.ts @@ -6,13 +6,20 @@ */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { TeamDto } from '@app/shared'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { TeamDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-left-menu', styleUrls: ['./left-menu.component.scss'], templateUrl: './left-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + RouterLink, + RouterLinkActive, + TranslatePipe, + ], }) export class LeftMenuComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts index 4d0b601c0..e12dd270f 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts +++ b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.ts @@ -5,11 +5,16 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component, Injectable } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { withLatestFrom } from 'rxjs/operators'; -import { AssignTeamContributorForm, TeamContributorsState } from '@app/features/teams/internal'; -import { AutocompleteSource, DialogModel, DialogService, UsersService } from '@app/shared'; +import { AutocompleteComponent, AutocompleteSource, DialogModel, DialogService, FormHintComponent, ModalDirective, TranslatePipe, UsersService } from '@app/shared'; +import { UserDtoPicture } from '@app/shared'; +import { AssignTeamContributorForm } from '../../internal'; +import { TeamContributorsState } from '../../state/team-contributors.state'; +import { ImportContributorsDialogComponent } from './import-contributors-dialog.component'; @Injectable() export class UsersDataSource implements AutocompleteSource { @@ -39,12 +44,24 @@ export class UsersDataSource implements AutocompleteSource { } @Component({ + standalone: true, selector: 'sqx-contributor-add-form', styleUrls: ['./contributor-add-form.component.scss'], templateUrl: './contributor-add-form.component.html', providers: [ UsersDataSource, ], + imports: [ + AsyncPipe, + AutocompleteComponent, + FormHintComponent, + FormsModule, + ImportContributorsDialogComponent, + ModalDirective, + ReactiveFormsModule, + TranslatePipe, + UserDtoPicture, + ], }) export class ContributorAddFormComponent { public assignContributorForm = new AssignTeamContributorForm(); diff --git a/frontend/src/app/features/teams/pages/contributors/contributor.component.ts b/frontend/src/app/features/teams/pages/contributors/contributor.component.ts index fd30ada2b..8bc3d0640 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributor.component.ts +++ b/frontend/src/app/features/teams/pages/contributors/contributor.component.ts @@ -8,14 +8,21 @@ /* eslint-disable @angular-eslint/component-selector */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { TeamContributorsState } from '@app/features/teams/internal'; -import { ContributorDto } from '@app/shared'; +import { ConfirmClickDirective, ContributorDto, HighlightPipe, TooltipDirective, UserPicturePipe } from '@app/shared'; +import { TeamContributorsState } from '../../internal'; @Component({ + standalone: true, selector: '[sqxContributor]', styleUrls: ['./contributor.component.scss'], templateUrl: './contributor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + HighlightPipe, + TooltipDirective, + UserPicturePipe, + ], }) export class ContributorComponent { @Input() diff --git a/frontend/src/app/features/teams/pages/contributors/contributors-page.component.ts b/frontend/src/app/features/teams/pages/contributors/contributors-page.component.ts index d06451873..3a4f8ef5c 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributors-page.component.ts +++ b/frontend/src/app/features/teams/pages/contributors/contributors-page.component.ts @@ -5,17 +5,44 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { TeamContributorsState } from '@app/features/teams/internal'; -import { ContributorDto, Router2State } from '@app/shared'; +import { FormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ContributorDto, LayoutComponent, ListViewComponent, NotifoComponent, PagerComponent, Router2State, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { TeamContributorsState } from '../../internal'; +import { ContributorAddFormComponent } from './contributor-add-form.component'; +import { ContributorComponent } from './contributor.component'; @Component({ + standalone: true, selector: 'sqx-contributors-page', styleUrls: ['./contributors-page.component.scss'], templateUrl: './contributors-page.component.html', providers: [ Router2State, ], + imports: [ + AsyncPipe, + ContributorAddFormComponent, + ContributorComponent, + FormsModule, + LayoutComponent, + ListViewComponent, + NgFor, + NgIf, + NotifoComponent, + PagerComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class ContributorsPageComponent implements OnInit { constructor( diff --git a/frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.ts b/frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.ts index 88752757c..a704a390c 100644 --- a/frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.ts +++ b/frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.ts @@ -5,11 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { Component, EventEmitter, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EMPTY, of } from 'rxjs'; import { catchError, mergeMap, tap } from 'rxjs/operators'; -import { TeamContributorsState } from '@app/features/teams/internal'; -import { ErrorDto, ImportContributorsForm } from '@app/shared'; +import { ErrorDto, FormHintComponent, ImportContributorsForm, ModalDialogComponent, StatusIconComponent, TooltipDirective, TranslatePipe } from '@app/shared'; +import { TeamContributorsState } from '../../internal'; type ImportStatus = { email: string; @@ -19,9 +21,24 @@ type ImportStatus = { }; @Component({ + standalone: true, selector: 'sqx-import-contributors-dialog', styleUrls: ['./import-contributors-dialog.component.scss'], templateUrl: './import-contributors-dialog.component.html', + imports: [ + AsyncPipe, + FormHintComponent, + FormsModule, + ModalDialogComponent, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + ReactiveFormsModule, + StatusIconComponent, + TooltipDirective, + TranslatePipe, + ], }) export class ImportContributorsDialogComponent { @Output() diff --git a/frontend/src/app/features/teams/pages/dashboard/cards/apps-card.component.ts b/frontend/src/app/features/teams/pages/dashboard/cards/apps-card.component.ts index 4b6544f2d..e649246c0 100644 --- a/frontend/src/app/features/teams/pages/dashboard/cards/apps-card.component.ts +++ b/frontend/src/app/features/teams/pages/dashboard/cards/apps-card.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { AppDto, AppsService, StatefulComponent, TeamDto } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { AppDto, AppsService, StatefulComponent, StopClickDirective, TeamDto, TranslatePipe } from '@app/shared'; interface State { // The apps for this team. @@ -14,10 +16,18 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-apps-card', styleUrls: ['./apps-card.component.scss'], templateUrl: './apps-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + RouterLink, + StopClickDirective, + TranslatePipe, + ], }) export class AppsCardComponent extends StatefulComponent<State> implements OnInit { @Input({ required: true }) diff --git a/frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.ts b/frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.ts index 1463b3b2c..560b32d8f 100644 --- a/frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.ts +++ b/frontend/src/app/features/teams/pages/dashboard/dashboard-page.component.ts @@ -5,17 +5,46 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { AfterViewInit, Component, NgZone, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { GridsterComponent, GridsterConfig, GridsterItem, GridType } from 'angular-gridster2'; -import { AuthService, CallsUsageDto, CurrentStorageDto, DateTime, defined, fadeAnimation, LocalStoreService, Settings, StorageUsagePerDateDto, Subscriptions, switchSafe, TeamsState, UsagesService } from '@app/shared'; +import { GridsterComponent, GridsterConfig, GridsterItem, GridsterItemComponent, GridType } from 'angular-gridster2'; +import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, ApiTrafficSummaryCardComponent, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, defined, fadeAnimation, IFrameCardComponent, LocalStoreService, MarkdownPipe, RandomCatCardComponent, RandomDogCardComponent, SafeHtmlPipe, Settings, StorageUsagePerDateDto, Subscriptions, SupportCardComponent, switchSafe, TeamsState, TitleComponent, TranslatePipe, UsagesService } from '@app/shared'; +import { AppsCardComponent } from './cards/apps-card.component'; @Component({ + standalone: true, selector: 'sqx-dashboard-page', styleUrls: ['./dashboard-page.component.scss'], templateUrl: './dashboard-page.component.html', animations: [ fadeAnimation, ], + imports: [ + ApiCallsCardComponent, + ApiCallsSummaryCardComponent, + ApiPerformanceCardComponent, + ApiTrafficCardComponent, + ApiTrafficSummaryCardComponent, + AppsCardComponent, + AssetUploadsCountCardComponent, + AssetUploadsSizeCardComponent, + AssetUploadsSizeSummaryCardComponent, + AsyncPipe, + GridsterComponent, + GridsterItemComponent, + IFrameCardComponent, + MarkdownPipe, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + RandomCatCardComponent, + RandomDogCardComponent, + SafeHtmlPipe, + SupportCardComponent, + TitleComponent, + TranslatePipe, + ], }) export class DashboardPageComponent implements AfterViewInit, OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/teams/pages/more/more-page.component.ts b/frontend/src/app/features/teams/pages/more/more-page.component.ts index 1be8434c5..b8261e9c4 100644 --- a/frontend/src/app/features/teams/pages/more/more-page.component.ts +++ b/frontend/src/app/features/teams/pages/more/more-page.component.ts @@ -5,13 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { defined, Subscriptions, TeamDto, TeamsState, UpdateTeamForm } from '@app/shared'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ControlErrorsComponent, defined, FormErrorComponent, LayoutComponent, ListViewComponent, SidebarMenuDirective, Subscriptions, TeamDto, TeamsState, TooltipDirective, TourStepDirective, TranslatePipe, UpdateTeamForm } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-more-page', styleUrls: ['./more-page.component.scss'], templateUrl: './more-page.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FormErrorComponent, + FormsModule, + LayoutComponent, + ListViewComponent, + ReactiveFormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, + SidebarMenuDirective, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class MorePageComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/features/teams/pages/plans/plan.component.ts b/frontend/src/app/features/teams/pages/plans/plan.component.ts index d7459251d..3bf71aef9 100644 --- a/frontend/src/app/features/teams/pages/plans/plan.component.ts +++ b/frontend/src/app/features/teams/pages/plans/plan.component.ts @@ -5,15 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { TeamPlansState } from '@app/features/teams/internal'; -import { PlanInfo } from '@app/shared'; +import { ConfirmClickDirective, FileSizePipe, FormHintComponent, KNumberPipe, PlanInfo, TranslatePipe } from '@app/shared'; +import { TeamPlansState } from '../../internal'; @Component({ + standalone: true, selector: 'sqx-plan', styleUrls: ['./plan.component.scss'], templateUrl: './plan.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ConfirmClickDirective, + FileSizePipe, + FormHintComponent, + KNumberPipe, + NgIf, + TranslatePipe, + ], }) export class PlanComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/teams/pages/plans/plans-page.component.ts b/frontend/src/app/features/teams/pages/plans/plans-page.component.ts index ba1e038d0..42f9a7f4a 100644 --- a/frontend/src/app/features/teams/pages/plans/plans-page.component.ts +++ b/frontend/src/app/features/teams/pages/plans/plans-page.component.ts @@ -5,15 +5,40 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { TeamPlansState } from '@app/features/teams/internal'; -import { ApiUrlConfig, PlanDto } from '@app/shared'; +import { ActivatedRoute, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; +import { ApiUrlConfig, ExternalLinkDirective, FormHintComponent, LayoutComponent, ListViewComponent, MarkdownPipe, NotifoComponent, PlanDto, SafeHtmlPipe, ShortcutDirective, SidebarMenuDirective, TitleComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/shared'; +import { TeamPlansState } from '../../internal'; +import { PlanComponent } from './plan.component'; @Component({ + standalone: true, selector: 'sqx-plans-page', styleUrls: ['./plans-page.component.scss'], templateUrl: './plans-page.component.html', + imports: [ + AsyncPipe, + ExternalLinkDirective, + FormHintComponent, + LayoutComponent, + ListViewComponent, + MarkdownPipe, + NgFor, + NgIf, + NotifoComponent, + PlanComponent, + RouterLink, + RouterLinkActive, + RouterOutlet, + SafeHtmlPipe, + ShortcutDirective, + SidebarMenuDirective, + TitleComponent, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class PlansPageComponent implements OnInit { private overridePlanId?: string; diff --git a/frontend/src/app/features/teams/module.ts b/frontend/src/app/features/teams/routes.ts similarity index 69% rename from frontend/src/app/features/teams/module.ts rename to frontend/src/app/features/teams/routes.ts index 3d87ddb2e..dd3dc92cd 100644 --- a/frontend/src/app/features/teams/module.ts +++ b/frontend/src/app/features/teams/routes.ts @@ -5,14 +5,17 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { GridsterModule } from 'angular-gridster2'; -import { HelpComponent, HistoryComponent, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AppsCardComponent, ContributorAddFormComponent, ContributorComponent, ContributorsPageComponent, DashboardPageComponent, ImportContributorsDialogComponent, LeftMenuComponent, MorePageComponent, PlanComponent, PlansPageComponent, SettingsAreaComponent, SettingsMenuComponent, TeamAreaComponent } from './declarations'; +import { Routes } from '@angular/router'; +import { HelpComponent, HistoryComponent } from '@app/shared'; import { TeamContributorsService, TeamContributorsState, TeamPlansService, TeamPlansState } from './internal'; +import { ContributorsPageComponent } from './pages/contributors/contributors-page.component'; +import { DashboardPageComponent } from './pages/dashboard/dashboard-page.component'; +import { MorePageComponent } from './pages/more/more-page.component'; +import { PlansPageComponent } from './pages/plans/plans-page.component'; +import { SettingsAreaComponent } from './shared/settings-area.component'; +import { TeamAreaComponent } from './team-area.component'; -const routes: Routes = [ +export const TEAM_ROUTES: Routes = [ { path: '', component: TeamAreaComponent, @@ -28,6 +31,10 @@ const routes: Routes = [ { path: 'contributors', component: ContributorsPageComponent, + providers: [ + TeamContributorsService, + TeamContributorsState, + ], children: [ { path: 'history', @@ -48,6 +55,10 @@ const routes: Routes = [ { path: 'plans', component: PlansPageComponent, + providers: [ + TeamPlansService, + TeamPlansState, + ], children: [ { path: 'history', @@ -90,34 +101,3 @@ const routes: Routes = [ ], }, ]; - -@NgModule({ - imports: [ - GridsterModule, - RouterModule.forChild(routes), - SqxFrameworkModule, - SqxSharedModule, - ], - declarations: [ - AppsCardComponent, - DashboardPageComponent, - ContributorAddFormComponent, - ContributorComponent, - ContributorsPageComponent, - ImportContributorsDialogComponent, - LeftMenuComponent, - MorePageComponent, - PlanComponent, - PlansPageComponent, - SettingsAreaComponent, - SettingsMenuComponent, - TeamAreaComponent, - ], - providers: [ - TeamContributorsService, - TeamContributorsState, - TeamPlansService, - TeamPlansState, - ], -}) -export class SqxFeatureTeamsModule {} diff --git a/frontend/src/app/features/teams/services/team-contributors.service.spec.ts b/frontend/src/app/features/teams/services/team-contributors.service.spec.ts index 4dc3b6655..9edad6d63 100644 --- a/frontend/src/app/features/teams/services/team-contributors.service.spec.ts +++ b/frontend/src/app/features/teams/services/team-contributors.service.spec.ts @@ -7,8 +7,8 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; -import { TeamContributorsService } from '@app/features/teams/internal'; import { ApiUrlConfig, ContributorDto, ContributorsDto, ContributorsPayload, Resource, ResourceLinks, Version } from '@app/shared/internal'; +import { TeamContributorsService } from '../internal'; describe('TeamContributorsService', () => { const version = new Version('1'); diff --git a/frontend/src/app/features/teams/services/team-contributors.service.ts b/frontend/src/app/features/teams/services/team-contributors.service.ts index 4b93d351f..f3d2089fe 100644 --- a/frontend/src/app/features/teams/services/team-contributors.service.ts +++ b/frontend/src/app/features/teams/services/team-contributors.service.ts @@ -10,7 +10,9 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ApiUrlConfig, AssignContributorDto, ContributorsDto, HTTP, mapVersioned, parseContributors, pretifyError, Resource, Version } from '@app/shared'; -@Injectable() +@Injectable({ + providedIn: 'any', +}) export class TeamContributorsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/features/teams/services/team-plans.service.spec.ts b/frontend/src/app/features/teams/services/team-plans.service.spec.ts index b2dd2bf9b..cdb7c4496 100644 --- a/frontend/src/app/features/teams/services/team-plans.service.spec.ts +++ b/frontend/src/app/features/teams/services/team-plans.service.spec.ts @@ -7,8 +7,8 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; -import { TeamPlansService } from '@app/features/teams/internal'; import { ApiUrlConfig, PlanChangedDto, PlanDto, PlansDto, Version } from '@app/shared'; +import { TeamPlansService } from '../internal'; describe('TeamPlansService', () => { const version = new Version('1'); diff --git a/frontend/src/app/features/teams/services/team-plans.service.ts b/frontend/src/app/features/teams/services/team-plans.service.ts index 440207d52..0fdb39192 100644 --- a/frontend/src/app/features/teams/services/team-plans.service.ts +++ b/frontend/src/app/features/teams/services/team-plans.service.ts @@ -10,7 +10,9 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ApiUrlConfig, ChangePlanDto, HTTP, mapVersioned, parsePlans, PlanChangedDto, PlansDto, pretifyError, Version, Versioned } from '@app/shared'; -@Injectable() +@Injectable({ + providedIn: 'any', +}) export class TeamPlansService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/features/teams/shared/settings-area.component.ts b/frontend/src/app/features/teams/shared/settings-area.component.ts index 9e4fab90e..d369d0c98 100644 --- a/frontend/src/app/features/teams/shared/settings-area.component.ts +++ b/frontend/src/app/features/teams/shared/settings-area.component.ts @@ -5,13 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; -import { defined, TeamsState } from '@app/shared'; +import { RouterOutlet } from '@angular/router'; +import { defined, LayoutComponent, TeamsState, TitleComponent } from '@app/shared'; +import { SettingsMenuComponent } from './settings-menu.component'; @Component({ + standalone: true, selector: 'sqx-settings-area', styleUrls: ['./settings-area.component.scss'], templateUrl: './settings-area.component.html', + imports: [ + AsyncPipe, + LayoutComponent, + NgIf, + RouterOutlet, + SettingsMenuComponent, + TitleComponent, + ], }) export class SettingsAreaComponent { public selectedTeam = this.teamsState.selectedTeam.pipe(defined()); diff --git a/frontend/src/app/features/teams/shared/settings-menu.component.ts b/frontend/src/app/features/teams/shared/settings-menu.component.ts index e97a671a2..d660f9dc9 100644 --- a/frontend/src/app/features/teams/shared/settings-menu.component.ts +++ b/frontend/src/app/features/teams/shared/settings-menu.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { TeamDto } from '@app/shared'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { TeamDto, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-settings-menu', styleUrls: ['./settings-menu.component.scss'], templateUrl: './settings-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + RouterLink, + RouterLinkActive, + TranslatePipe, + ], }) export class SettingsMenuComponent { @Input({ required: true }) diff --git a/frontend/src/app/features/teams/state/team-contributors.state.spec.ts b/frontend/src/app/features/teams/state/team-contributors.state.spec.ts index 3b9d91c1a..6bcef4f26 100644 --- a/frontend/src/app/features/teams/state/team-contributors.state.spec.ts +++ b/frontend/src/app/features/teams/state/team-contributors.state.spec.ts @@ -7,11 +7,11 @@ import { EMPTY, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; +import { createContributors } from 'src/app/shared/services/contributors.service.spec'; +import { TestValues } from 'src/app/shared/state/_test-helpers'; import { IMock, It, Mock, Times } from 'typemoq'; -import { TeamContributorsService, TeamContributorsState } from '@app/features/teams/internal'; import { ContributorDto, ContributorsPayload, DialogService, ErrorDto, versioned } from '@app/shared'; -import { createContributors } from '@app/shared/services/contributors.service.spec'; -import { TestValues } from '@app/shared/state/_test-helpers'; +import { TeamContributorsService, TeamContributorsState } from '../internal'; describe('TeamContributorsState', () => { const { diff --git a/frontend/src/app/features/teams/state/team-contributors.state.ts b/frontend/src/app/features/teams/state/team-contributors.state.ts index 2e3eac961..8912334be 100644 --- a/frontend/src/app/features/teams/state/team-contributors.state.ts +++ b/frontend/src/app/features/teams/state/team-contributors.state.ts @@ -8,8 +8,8 @@ import { Injectable } from '@angular/core'; import { EMPTY, Observable, throwError } from 'rxjs'; import { catchError, finalize, tap } from 'rxjs/operators'; -import { TeamContributorsService } from '@app/features/teams/internal'; import { AssignContributorDto, ContributorDto, ContributorsPayload, debug, DialogService, ErrorDto, getPagingInfo, ListState, shareMapSubscribed, shareSubscribed, State, TeamsState, Types, Version } from '@app/shared'; +import { TeamContributorsService } from '../internal'; interface Snapshot extends ListState<string> { // The current contributors. @@ -25,7 +25,9 @@ interface Snapshot extends ListState<string> { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'any', +}) export class TeamContributorsState extends State<Snapshot> { public contributors = this.project(x => x.contributors); diff --git a/frontend/src/app/features/teams/state/team-plans.state.spec.ts b/frontend/src/app/features/teams/state/team-plans.state.spec.ts index f2574e29a..5d47c0444 100644 --- a/frontend/src/app/features/teams/state/team-plans.state.spec.ts +++ b/frontend/src/app/features/teams/state/team-plans.state.spec.ts @@ -6,10 +6,10 @@ */ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; +import { TestValues } from 'src/app/shared/state/_test-helpers'; import { IMock, It, Mock, Times } from 'typemoq'; -import { TeamPlansService, TeamPlansState } from '@app/features/teams/internal'; import { DialogService, PlanDto, PlanLockedReason, versioned } from '@app/shared'; -import { TestValues } from '@app/shared/state/_test-helpers'; +import { TeamPlansService, TeamPlansState } from '../internal'; describe('TeamPlansState', () => { const { diff --git a/frontend/src/app/features/teams/state/team-plans.state.ts b/frontend/src/app/features/teams/state/team-plans.state.ts index 9a2c14b58..d69a310de 100644 --- a/frontend/src/app/features/teams/state/team-plans.state.ts +++ b/frontend/src/app/features/teams/state/team-plans.state.ts @@ -8,8 +8,8 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; -import { TeamPlansService } from '@app/features/teams/internal'; import { debug, DialogService, LoadingState, PlanDto, PlanLockedReason, ReferralDto, shareSubscribed, State, TeamsState, Version } from '@app/shared'; +import { TeamPlansService } from '../internal'; export interface PlanInfo { // The plan. @@ -42,7 +42,9 @@ interface Snapshot extends LoadingState { version: Version; } -@Injectable() +@Injectable({ + providedIn: 'any', +}) export class TeamPlansState extends State<Snapshot> { public plans = this.project(x => x.plans); diff --git a/frontend/src/app/features/teams/team-area.component.ts b/frontend/src/app/features/teams/team-area.component.ts index 57cc5f428..2789f4f3b 100644 --- a/frontend/src/app/features/teams/team-area.component.ts +++ b/frontend/src/app/features/teams/team-area.component.ts @@ -5,13 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; -import { defined, TeamsState } from '@app/shared'; +import { RouterOutlet } from '@angular/router'; +import { defined, LayoutContainerDirective, TeamsState, TitleComponent } from '@app/shared'; +import { LeftMenuComponent } from './left-menu.component'; @Component({ + standalone: true, selector: 'sqx-team-area', styleUrls: ['./team-area.component.scss'], templateUrl: './team-area.component.html', + imports: [ + AsyncPipe, + LayoutContainerDirective, + LeftMenuComponent, + NgIf, + RouterOutlet, + TitleComponent, + ], }) export class TeamAreaComponent { public selectedTeam = this.teamsState.selectedTeam.pipe(defined()); diff --git a/frontend/src/app/framework/angular/avatar.component.ts b/frontend/src/app/framework/angular/avatar.component.ts index 41233bdf1..9fb937df0 100644 --- a/frontend/src/app/framework/angular/avatar.component.ts +++ b/frontend/src/app/framework/angular/avatar.component.ts @@ -5,14 +5,21 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core'; import { picasso, TypedSimpleChanges } from '@app/framework/internal'; +import { SafeUrlPipe } from './safe-html.pipe'; @Component({ + standalone: true, selector: 'sqx-avatar', styleUrls: ['./avatar.component.scss'], templateUrl: './avatar.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + SafeUrlPipe, + ], }) export class AvatarComponent { @Input() diff --git a/frontend/src/app/framework/angular/avatar.stories.ts b/frontend/src/app/framework/angular/avatar.stories.ts index 2e8d2137f..d834189cd 100644 --- a/frontend/src/app/framework/angular/avatar.stories.ts +++ b/frontend/src/app/framework/angular/avatar.stories.ts @@ -6,7 +6,7 @@ */ import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { AvatarComponent, SqxFrameworkModule } from '@app/framework'; +import { AvatarComponent } from '@app/framework'; export default { title: 'Framework/Avatar', @@ -25,8 +25,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/code.component.ts b/frontend/src/app/framework/angular/code.component.ts index 2cb1318a7..1f9498366 100644 --- a/frontend/src/app/framework/angular/code.component.ts +++ b/frontend/src/app/framework/angular/code.component.ts @@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { MathHelper } from '../internal'; @Component({ + standalone: true, selector: 'sqx-code', styleUrls: ['./code.component.scss'], templateUrl: './code.component.html', diff --git a/frontend/src/app/framework/angular/compensate-scrollbar.directive.ts b/frontend/src/app/framework/angular/compensate-scrollbar.directive.ts index 3f1b49fc8..232f97a48 100644 --- a/frontend/src/app/framework/angular/compensate-scrollbar.directive.ts +++ b/frontend/src/app/framework/angular/compensate-scrollbar.directive.ts @@ -10,6 +10,7 @@ import { ResizeListener, ResizeService, Subscriptions } from '@app/framework/int @Directive({ selector: '[sqxCompensateScrollbar]', + standalone: true, }) export class CompensateScrollbarDirective implements AfterViewInit, ResizeListener { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/drag-helper.ts b/frontend/src/app/framework/angular/drag-helper.ts index 5adba9ad6..f294ede49 100644 --- a/frontend/src/app/framework/angular/drag-helper.ts +++ b/frontend/src/app/framework/angular/drag-helper.ts @@ -6,7 +6,7 @@ */ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; export function sorted<T>(event: CdkDragDrop<ReadonlyArray<T>>): T[] { const items = <T[]>event.container.data; diff --git a/frontend/src/app/framework/angular/dropdown-menu.component.ts b/frontend/src/app/framework/angular/dropdown-menu.component.ts index c92aec9ec..fd60435a0 100644 --- a/frontend/src/app/framework/angular/dropdown-menu.component.ts +++ b/frontend/src/app/framework/angular/dropdown-menu.component.ts @@ -11,6 +11,7 @@ import { ChangeDetectionStrategy, Component, ElementRef, HostBinding } from '@an import { fadeAnimation } from './animations'; @Component({ + standalone: true, selector: 'sqx-dropdown-menu', styleUrls: ['./dropdown-menu.component.scss'], templateUrl: './dropdown-menu.component.html', diff --git a/frontend/src/app/framework/angular/external-link.directive.ts b/frontend/src/app/framework/angular/external-link.directive.ts index 1888e133b..3a8f4e102 100644 --- a/frontend/src/app/framework/angular/external-link.directive.ts +++ b/frontend/src/app/framework/angular/external-link.directive.ts @@ -9,6 +9,7 @@ import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular @Directive({ selector: '[sqxExternalLink]', + standalone: true, }) export class ExternalLinkDirective implements AfterViewInit { @Input('sqxExternalLink') diff --git a/frontend/src/app/framework/angular/forms/confirm-click.directive.ts b/frontend/src/app/framework/angular/forms/confirm-click.directive.ts index dc386c90f..0b3bf341b 100644 --- a/frontend/src/app/framework/angular/forms/confirm-click.directive.ts +++ b/frontend/src/app/framework/angular/forms/confirm-click.directive.ts @@ -11,6 +11,7 @@ import { DialogService, Types } from '@app/framework/internal'; @Directive({ selector: '[sqxConfirmClick]', + standalone: true, }) export class ConfirmClickDirective { @Input() diff --git a/frontend/src/app/framework/angular/forms/control-errors-messages.component.ts b/frontend/src/app/framework/angular/forms/control-errors-messages.component.ts index 400b88294..ca2365fb4 100644 --- a/frontend/src/app/framework/angular/forms/control-errors-messages.component.ts +++ b/frontend/src/app/framework/angular/forms/control-errors-messages.component.ts @@ -5,10 +5,12 @@ * Copyright (c) Sebastian Stehle. All rights r vbeserved */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { fadeAnimation } from '@app/framework/internal'; @Component({ + standalone: true, selector: 'sqx-errors-messages', styleUrls: ['./control-errors-messages.component.scss'], templateUrl: './control-errors-messages.component.html', @@ -16,6 +18,10 @@ import { fadeAnimation } from '@app/framework/internal'; fadeAnimation, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + ], }) export class ControlErrorsMessagesComponent { @Input() diff --git a/frontend/src/app/framework/angular/forms/control-errors.component.ts b/frontend/src/app/framework/angular/forms/control-errors.component.ts index f239f6301..7daa2e851 100644 --- a/frontend/src/app/framework/angular/forms/control-errors.component.ts +++ b/frontend/src/app/framework/angular/forms/control-errors.component.ts @@ -5,10 +5,12 @@ * Copyright (c) Sebastian Stehle. All rights r vbeserved */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Host, Input, OnDestroy, Optional } from '@angular/core'; import { AbstractControl, FormGroupDirective, UntypedFormArray } from '@angular/forms'; import { merge } from 'rxjs'; import { LocalizerService, StatefulComponent, Subscriptions, Types } from '@app/framework/internal'; +import { ControlErrorsMessagesComponent } from './control-errors-messages.component'; import { formatError } from './error-formatting'; import { touchedChange$ } from './forms-helper'; @@ -18,10 +20,15 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-control-errors', styleUrls: ['./control-errors.component.scss'], templateUrl: './control-errors.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ControlErrorsMessagesComponent, + NgIf, + ], }) export class ControlErrorsComponent extends StatefulComponent<State> implements OnDestroy { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/forms/copy-global.directive.ts b/frontend/src/app/framework/angular/forms/copy-global.directive.ts index f668e4388..59e87b41f 100644 --- a/frontend/src/app/framework/angular/forms/copy-global.directive.ts +++ b/frontend/src/app/framework/angular/forms/copy-global.directive.ts @@ -10,6 +10,7 @@ import { DialogService, Types } from '@app/framework/internal'; @Directive({ selector: '[sqxCopyGlobal]', + standalone: true, }) export class CopyGlobalDirective { constructor( diff --git a/frontend/src/app/framework/angular/forms/copy.directive.ts b/frontend/src/app/framework/angular/forms/copy.directive.ts index fc92b6d8f..593ccd520 100644 --- a/frontend/src/app/framework/angular/forms/copy.directive.ts +++ b/frontend/src/app/framework/angular/forms/copy.directive.ts @@ -10,6 +10,7 @@ import { DialogService, Types } from '@app/framework/internal'; @Directive({ selector: '[sqxCopy]', + standalone: true, }) export class CopyDirective { @Input('sqxCopy') diff --git a/frontend/src/app/framework/angular/forms/editable-title.component.ts b/frontend/src/app/framework/angular/forms/editable-title.component.ts index bf06e5557..da1595751 100644 --- a/frontend/src/app/framework/angular/forms/editable-title.component.ts +++ b/frontend/src/app/framework/angular/forms/editable-title.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; -import { FormControl, ValidatorFn, Validators } from '@angular/forms'; +import { FormControl, FormsModule, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms'; import { Keys } from '@app/framework/internal'; +import { ControlErrorsComponent } from './control-errors.component'; +import { FocusOnInitDirective } from './focus-on-init.directive'; @Component({ + standalone: true, selector: 'sqx-editable-title', styleUrls: ['./editable-title.component.scss'], templateUrl: './editable-title.component.html', + imports: [ + ControlErrorsComponent, + FocusOnInitDirective, + FormsModule, + NgIf, + ReactiveFormsModule, + ], }) export class EditableTitleComponent { @Output() diff --git a/frontend/src/app/framework/angular/forms/editable-title.stories.ts b/frontend/src/app/framework/angular/forms/editable-title.stories.ts index 14c1bb8b6..acf04a4cc 100644 --- a/frontend/src/app/framework/angular/forms/editable-title.stories.ts +++ b/frontend/src/app/framework/angular/forms/editable-title.stories.ts @@ -6,9 +6,8 @@ */ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { EditableTitleComponent, LocalizerService, SqxFrameworkModule } from '@app/framework'; +import { EditableTitleComponent, LocalizerService } from '@app/framework'; export default { title: 'Framework/EditableTitle', @@ -68,14 +67,14 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, FormsModule, ReactiveFormsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], providers: [ - { provide: LocalizerService, useValue: new LocalizerService({}) }, + { + provide: LocalizerService, + useValue: new LocalizerService({}), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts index f1096edb2..3318c8a45 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts @@ -5,11 +5,20 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, ContentChild, ElementRef, forwardRef, Input, numberAttribute, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { merge, Observable, of, Subject } from 'rxjs'; import { catchError, debounceTime, finalize, map, switchMap, tap } from 'rxjs/operators'; import { FloatingPlacement, Keys, ModalModel, StatefulControlComponent, Subscriptions, Types } from '@app/framework/internal'; +import { DropdownMenuComponent } from '../../dropdown-menu.component'; +import { LoaderComponent } from '../../loader.component'; +import { ModalPlacementDirective } from '../../modals/modal-placement.directive'; +import { ModalDirective } from '../../modals/modal.directive'; +import { ScrollActiveDirective } from '../../scroll-active.directive'; +import { StopClickDirective } from '../../stop-click.directive'; +import { TemplateWrapperDirective } from '../../template-wrapper.directive'; +import { FocusOnInitDirective } from '../focus-on-init.directive'; export interface AutocompleteSource { find(query: string): Observable<ReadonlyArray<any>>; @@ -36,6 +45,7 @@ interface State { const NO_EMIT = { emitEvent: false }; @Component({ + standalone: true, selector: 'sqx-autocomplete', styleUrls: ['./autocomplete.component.scss'], templateUrl: './autocomplete.component.html', @@ -43,6 +53,20 @@ const NO_EMIT = { emitEvent: false }; SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownMenuComponent, + FocusOnInitDirective, + FormsModule, + LoaderComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + ReactiveFormsModule, + ScrollActiveDirective, + StopClickDirective, + TemplateWrapperDirective, + ], }) export class AutocompleteComponent extends StatefulControlComponent<State, ReadonlyArray<any>> implements OnInit, OnDestroy { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts index d4235da6b..0c8cb9276 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts @@ -5,11 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; import { map, Observable, timer } from 'rxjs'; -import { AutocompleteComponent, LocalizerService, SqxFrameworkModule } from '@app/framework'; -import { AutocompleteSource } from './autocomplete.component'; +import { AutocompleteComponent, AutocompleteSource, LocalizerService, RootViewComponent } from '@app/framework'; const TRANSLATIONS = { 'common.search': 'Search', @@ -43,12 +41,13 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), + RootViewComponent, ], providers: [ - { provide: LocalizerService, useFactory: () => new LocalizerService(TRANSLATIONS) }, + { + provide: LocalizerService, + useFactory: () => new LocalizerService(TRANSLATIONS), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts b/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts index 3a920939f..781a76b33 100644 --- a/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts @@ -5,9 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { AfterViewChecked, AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { getTagValues, MathHelper, StatefulControlComponent, TagValue, TextMeasurer, Types } from '@app/framework/internal'; +import { ResizedDirective } from '../../resized.directive'; export const SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CheckboxGroupComponent), multi: true, @@ -22,6 +24,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-checkbox-group', styleUrls: ['./checkbox-group.component.scss'], templateUrl: './checkbox-group.component.html', @@ -29,6 +32,10 @@ interface State { SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + ResizedDirective, + ], }) export class CheckboxGroupComponent extends StatefulControlComponent<State, string[]> implements AfterViewInit, AfterViewChecked { private readonly textMeasurer: TextMeasurer; diff --git a/frontend/src/app/framework/angular/forms/editors/checkbox-group.stories.ts b/frontend/src/app/framework/angular/forms/editors/checkbox-group.stories.ts index 468e58f02..59ba95b2e 100644 --- a/frontend/src/app/framework/angular/forms/editors/checkbox-group.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/checkbox-group.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { CheckboxGroupComponent, SqxFrameworkModule } from '@app/framework'; +import { CheckboxGroupComponent } from '@app/framework'; export default { title: 'Framework/CheckboxGroup', @@ -38,9 +37,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts index 0b338d082..99d6f6d90 100644 --- a/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts @@ -9,7 +9,7 @@ import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, El import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { debounceTime, Subject } from 'rxjs'; import { ResourceLoaderService, ScriptCompletions, StatefulControlComponent, TypedSimpleChanges, Types } from '@app/framework/internal'; -import { FocusComponent } from './../forms-helper'; +import { FocusComponent } from '../forms-helper'; declare const ace: any; @@ -18,6 +18,7 @@ export const SQX_CODE_EDITOR_CONTROL_VALUE_ACCESSOR: any = { }; @Component({ + standalone: true, selector: 'sqx-code-editor', styleUrls: ['./code-editor.component.scss'], templateUrl: './code-editor.component.html', diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts index e459e9aca..7815986fa 100644 --- a/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { CodeEditorComponent, ScriptCompletions, SqxFrameworkModule } from '@app/framework'; +import { CodeEditorComponent, ScriptCompletions } from '@app/framework'; export default { title: 'Framework/CodeEditor', @@ -40,9 +39,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/color-picker.component.ts b/frontend/src/app/framework/angular/forms/editors/color-picker.component.ts index 7df870103..d8ebc0991 100644 --- a/frontend/src/app/framework/angular/forms/editors/color-picker.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/color-picker.component.ts @@ -5,10 +5,14 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ColorPickerModule } from 'ngx-color-picker'; import { MathHelper, ModalModel, StatefulControlComponent } from '@app/framework/internal'; -import { FocusComponent } from './../forms-helper'; +import { ModalPlacementDirective } from '../../modals/modal-placement.directive'; +import { ModalDirective } from '../../modals/modal.directive'; +import { FocusComponent } from '../forms-helper'; export const SQX_COLOR_PICKER_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ColorPickerComponent), multi: true, @@ -23,6 +27,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-color-picker', styleUrls: ['./color-picker.component.scss'], templateUrl: './color-picker.component.html', @@ -30,6 +35,13 @@ interface State { SQX_COLOR_PICKER_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ColorPickerModule, + FormsModule, + ModalDirective, + ModalPlacementDirective, + NgIf, + ], }) export class ColorPickerComponent extends StatefulControlComponent<State, string> implements FocusComponent { private wasOpen = false; diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts index 5da284468..ad9460b95 100644 --- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts @@ -5,11 +5,15 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, inject, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; -import * as Pikaday from 'pikaday/pikaday'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import Pikaday from 'pikaday/pikaday'; import { DateHelper, DateTime, StatefulControlComponent, Subscriptions, UIOptions } from '@app/framework/internal'; -import { FocusComponent } from './../forms-helper'; +import { TooltipDirective } from '../../modals/tooltip.directive'; +import { TranslatePipe } from '../../pipes/translate.pipe'; +import { ResizedDirective } from '../../resized.directive'; +import { FocusComponent } from '../forms-helper'; declare module 'pikaday/pikaday'; @@ -25,6 +29,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-date-time-editor', styleUrls: ['./date-time-editor.component.scss'], templateUrl: './date-time-editor.component.html', @@ -32,6 +37,14 @@ interface State { SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + NgIf, + ReactiveFormsModule, + ResizedDirective, + TooltipDirective, + TranslatePipe, + ], }) export class DateTimeEditorComponent extends StatefulControlComponent<State, string | null> implements OnInit, AfterViewInit, FocusComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts index 82093485b..9f229f1b3 100644 --- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { DateTimeEditorComponent, LocalizerService, SqxFrameworkModule, UIOptions } from '@app/framework'; +import { DateTimeEditorComponent, LocalizerService, UIOptions } from '@app/framework'; const translations = { 'common.date': 'Date', @@ -45,13 +44,16 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], providers: [ - { provide: LocalizerService, useFactory: () => new LocalizerService(translations) }, - { provide: UIOptions, useFactory: () => new UIOptions({}) }, + { + provide: LocalizerService, + useFactory: () => new LocalizerService(translations), + }, + { + provide: UIOptions, + useFactory: () => new UIOptions({}), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts index ab63a91ff..89ae17691 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts @@ -5,10 +5,19 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { AfterContentInit, booleanAttribute, ChangeDetectionStrategy, Component, ContentChildren, EventEmitter, forwardRef, Input, OnInit, Output, QueryList, TemplateRef } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { map } from 'rxjs/operators'; import { FloatingPlacement, Keys, ModalModel, StatefulControlComponent, Subscriptions, TypedSimpleChanges, Types } from '@app/framework/internal'; +import { DropdownMenuComponent } from '../../dropdown-menu.component'; +import { LoaderComponent } from '../../loader.component'; +import { ModalPlacementDirective } from '../../modals/modal-placement.directive'; +import { ModalDirective } from '../../modals/modal.directive'; +import { TranslatePipe } from '../../pipes/translate.pipe'; +import { ScrollActiveDirective } from '../../scroll-active.directive'; +import { TemplateWrapperDirective } from '../../template-wrapper.directive'; +import { FocusOnInitDirective } from '../focus-on-init.directive'; export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DropdownComponent), multi: true, @@ -29,6 +38,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-dropdown', styleUrls: ['./dropdown.component.scss'], templateUrl: './dropdown.component.html', @@ -36,6 +46,20 @@ interface State { SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownMenuComponent, + FocusOnInitDirective, + FormsModule, + LoaderComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + ReactiveFormsModule, + ScrollActiveDirective, + TemplateWrapperDirective, + TranslatePipe, + ], }) export class DropdownComponent extends StatefulControlComponent<State, ReadonlyArray<any>> implements AfterContentInit, OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts index 7f232f626..8f6cef3a2 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.stories.ts @@ -6,9 +6,8 @@ */ import { Component } from '@angular/core'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { DropdownComponent, LocalizerService, SqxFrameworkModule } from '@app/framework'; +import { DropdownComponent, LocalizerService, RootViewComponent } from '@app/framework'; const TRANSLATIONS = { 'common.search': 'Search', @@ -75,12 +74,13 @@ export default { TestComponent, ], imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), + RootViewComponent, ], providers: [ - { provide: LocalizerService, useFactory: () => new LocalizerService(TRANSLATIONS) }, + { + provide: LocalizerService, + useFactory: () => new LocalizerService(TRANSLATIONS), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/localized-input.component.ts b/frontend/src/app/framework/angular/forms/editors/localized-input.component.ts index 8d3817f5a..d9147ea08 100644 --- a/frontend/src/app/framework/angular/forms/editors/localized-input.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/localized-input.component.ts @@ -5,10 +5,14 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgSwitch, NgSwitchCase } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ModalModel, StatefulControlComponent, Types } from '@app/framework/internal'; -import { Language } from './../../language-selector.component'; +import { Language, LanguageSelectorComponent } from '../../language-selector.component'; +import { IndeterminateValueDirective } from '../indeterminate-value.directive'; +import { DateTimeEditorComponent } from './date-time-editor.component'; +import { TagEditorComponent } from './tag-editor.component'; export const SQX_LOCALIZED_INPUT_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => LocalizedInputComponent), multi: true, @@ -22,6 +26,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-localized-input', styleUrls: ['./localized-input.component.scss'], templateUrl: './localized-input.component.html', @@ -29,6 +34,15 @@ interface State { SQX_LOCALIZED_INPUT_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DateTimeEditorComponent, + FormsModule, + IndeterminateValueDirective, + LanguageSelectorComponent, + NgSwitch, + NgSwitchCase, + TagEditorComponent, + ], }) export class LocalizedInputComponent extends StatefulControlComponent<State, { [key: string]: any }> { private value: { [key: string]: any } | undefined; diff --git a/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts b/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts index 29045b430..d7af6181e 100644 --- a/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts @@ -5,9 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { AfterViewChecked, AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { getTagValues, MathHelper, StatefulControlComponent, TagValue, TextMeasurer } from '@app/framework/internal'; +import { ResizedDirective } from '../../resized.directive'; export const SQX_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true, @@ -19,6 +21,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-radio-group', styleUrls: ['./radio-group.component.scss'], templateUrl: './radio-group.component.html', @@ -26,6 +29,11 @@ interface State { SQX_RADIO_GROUP_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + NgFor, + ResizedDirective, + ], }) export class RadioGroupComponent extends StatefulControlComponent<State, string> implements AfterViewInit, AfterViewChecked { private readonly textMeasurer: TextMeasurer; diff --git a/frontend/src/app/framework/angular/forms/editors/radio-group.stories.ts b/frontend/src/app/framework/angular/forms/editors/radio-group.stories.ts index 26ab7ddd9..dfeaca55d 100644 --- a/frontend/src/app/framework/angular/forms/editors/radio-group.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/radio-group.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { RadioGroupComponent, SqxFrameworkModule } from '@app/framework'; +import { RadioGroupComponent } from '@app/framework'; export default { title: 'Framework/RadioGroup', @@ -38,9 +37,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/stars.component.ts b/frontend/src/app/framework/angular/forms/editors/stars.component.ts index 115a2667d..1d6352123 100644 --- a/frontend/src/app/framework/angular/forms/editors/stars.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/stars.component.ts @@ -5,9 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input, numberAttribute } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { StatefulControlComponent, Types } from '@app/framework/internal'; +import { TranslatePipe } from '../../pipes/translate.pipe'; export const SQX_STARS_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StarsComponent), multi: true, @@ -25,6 +27,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-stars', styleUrls: ['./stars.component.scss'], templateUrl: './stars.component.html', @@ -32,6 +35,11 @@ interface State { SQX_STARS_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + TranslatePipe, + ], }) export class StarsComponent extends StatefulControlComponent<State, number | null> { private maximumStarsValue = 5; diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts index 38eac4efe..e5560a67e 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts @@ -5,10 +5,19 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { getTagValues, Keys, ModalModel, StatefulControlComponent, StringConverter, Subscriptions, TagValue, TextMeasurer, TypedSimpleChanges, Types } from '@app/framework/internal'; +import { DropdownMenuComponent } from '../../dropdown-menu.component'; +import { LoaderComponent } from '../../loader.component'; +import { ModalPlacementDirective } from '../../modals/modal-placement.directive'; +import { ModalDirective } from '../../modals/modal.directive'; +import { TooltipDirective } from '../../modals/tooltip.directive'; +import { TranslatePipe } from '../../pipes/translate.pipe'; +import { ScrollActiveDirective } from '../../scroll-active.directive'; +import { StopClickDirective } from '../../stop-click.directive'; export const SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TagEditorComponent), multi: true, @@ -29,6 +38,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-tag-editor', styleUrls: ['./tag-editor.component.scss'], templateUrl: './tag-editor.component.html', @@ -36,6 +46,20 @@ interface State { SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownMenuComponent, + FormsModule, + LoaderComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + ReactiveFormsModule, + ScrollActiveDirective, + StopClickDirective, + TooltipDirective, + TranslatePipe, + ], }) export class TagEditorComponent extends StatefulControlComponent<State, ReadonlyArray<any>> implements AfterViewInit, OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts index 958112165..fea72a6c6 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts @@ -1,4 +1,3 @@ - /* * Squidex Headless CMS * @@ -7,9 +6,8 @@ */ import { Component } from '@angular/core'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { LocalizerService, SqxFrameworkModule, TagEditorComponent } from '@app/framework'; +import { LocalizerService, RootViewComponent, TagEditorComponent } from '@app/framework'; const TRANSLATIONS = { 'common.tagAdd': ', to add tag', @@ -78,12 +76,13 @@ export default { TestComponent, ], imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), + RootViewComponent, ], providers: [ - { provide: LocalizerService, useFactory: () => new LocalizerService(TRANSLATIONS) }, + { + provide: LocalizerService, + useFactory: () => new LocalizerService(TRANSLATIONS), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/forms/editors/toggle.component.ts b/frontend/src/app/framework/angular/forms/editors/toggle.component.ts index 6a634c698..71c7301a1 100644 --- a/frontend/src/app/framework/angular/forms/editors/toggle.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/toggle.component.ts @@ -19,6 +19,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-toggle', styleUrls: ['./toggle.component.scss'], templateUrl: './toggle.component.html', diff --git a/frontend/src/app/framework/angular/forms/error-formatting.spec.ts b/frontend/src/app/framework/angular/forms/error-formatting.spec.ts index fd4438ea8..e625bd05c 100644 --- a/frontend/src/app/framework/angular/forms/error-formatting.spec.ts +++ b/frontend/src/app/framework/angular/forms/error-formatting.spec.ts @@ -6,7 +6,7 @@ */ import { UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; -import { LocalizerService } from './../../services/localizer.service'; +import { LocalizerService } from '../../services/localizer.service'; import { formatError } from './error-formatting'; import { ValidatorsEx } from './validators'; diff --git a/frontend/src/app/framework/angular/forms/file-drop.directive.ts b/frontend/src/app/framework/angular/forms/file-drop.directive.ts index 7ac4298ca..aa05e43d7 100644 --- a/frontend/src/app/framework/angular/forms/file-drop.directive.ts +++ b/frontend/src/app/framework/angular/forms/file-drop.directive.ts @@ -12,6 +12,7 @@ import { Types } from '@app/framework/internal'; @Directive({ selector: '[sqxDropFile]', + standalone: true, }) export class FileDropDirective { private dragCounter = 0; diff --git a/frontend/src/app/framework/angular/forms/focus-on-init.directive.ts b/frontend/src/app/framework/angular/forms/focus-on-init.directive.ts index e22da24a0..8ffebd702 100644 --- a/frontend/src/app/framework/angular/forms/focus-on-init.directive.ts +++ b/frontend/src/app/framework/angular/forms/focus-on-init.directive.ts @@ -10,6 +10,7 @@ import { Types } from '@app/framework/internal'; @Directive({ selector: '[sqxFocusOnInit]', + standalone: true, }) export class FocusOnInitDirective implements AfterViewInit { @Input({ transform: booleanAttribute }) diff --git a/frontend/src/app/framework/angular/forms/form-alert.component.ts b/frontend/src/app/framework/angular/forms/form-alert.component.ts index 08c808ce3..61b04b1ea 100644 --- a/frontend/src/app/framework/angular/forms/form-alert.component.ts +++ b/frontend/src/app/framework/angular/forms/form-alert.component.ts @@ -8,6 +8,7 @@ import { booleanAttribute, ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-form-alert', styleUrls: ['./form-alert.component.scss'], templateUrl: './form-alert.component.html', diff --git a/frontend/src/app/framework/angular/forms/form-error.component.ts b/frontend/src/app/framework/angular/forms/form-error.component.ts index 3f53b6246..f111abd1a 100644 --- a/frontend/src/app/framework/angular/forms/form-error.component.ts +++ b/frontend/src/app/framework/angular/forms/form-error.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ErrorDto, TypedSimpleChanges } from '@app/framework/internal'; +import { MarkdownPipe } from '../pipes/markdown.pipe'; +import { TranslatePipe } from '../pipes/translate.pipe'; +import { SafeHtmlPipe } from '../safe-html.pipe'; @Component({ + standalone: true, selector: 'sqx-form-error', styleUrls: ['./form-error.component.scss'], templateUrl: './form-error.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + MarkdownPipe, + NgIf, + SafeHtmlPipe, + TranslatePipe, + ], }) export class FormErrorComponent { @Input({ required: true }) diff --git a/frontend/src/app/framework/angular/forms/form-hint.component.ts b/frontend/src/app/framework/angular/forms/form-hint.component.ts index a732eb1a8..907103186 100644 --- a/frontend/src/app/framework/angular/forms/form-hint.component.ts +++ b/frontend/src/app/framework/angular/forms/form-hint.component.ts @@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-form-hint', styleUrls: ['./form-hint.component.scss'], templateUrl: './form-hint.component.html', diff --git a/frontend/src/app/framework/angular/forms/forms-helper.ts b/frontend/src/app/framework/angular/forms/forms-helper.ts index 4a574b343..8202c9d75 100644 --- a/frontend/src/app/framework/angular/forms/forms-helper.ts +++ b/frontend/src/app/framework/angular/forms/forms-helper.ts @@ -8,7 +8,7 @@ import { AbstractControl, UntypedFormArray, UntypedFormGroup, ValidatorFn } from '@angular/forms'; import { combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, map, startWith } from 'rxjs/operators'; -import { Types } from './../../utils/types'; +import { Types } from '../../utils/types'; export interface FocusComponent { focus(): void; diff --git a/frontend/src/app/framework/angular/forms/indeterminate-value.directive.ts b/frontend/src/app/framework/angular/forms/indeterminate-value.directive.ts index 340be373d..08b14c76c 100644 --- a/frontend/src/app/framework/angular/forms/indeterminate-value.directive.ts +++ b/frontend/src/app/framework/angular/forms/indeterminate-value.directive.ts @@ -18,6 +18,7 @@ export const SQX_INDETERMINATE_VALUE_CONTROL_VALUE_ACCESSOR: any = { providers: [ SQX_INDETERMINATE_VALUE_CONTROL_VALUE_ACCESSOR, ], + standalone: true, }) export class IndeterminateValueDirective implements ControlValueAccessor { private callChange = (_: any) => { /* NOOP */ }; diff --git a/frontend/src/app/framework/angular/forms/model.ts b/frontend/src/app/framework/angular/forms/model.ts index a383e6b07..15c1313d2 100644 --- a/frontend/src/app/framework/angular/forms/model.ts +++ b/frontend/src/app/framework/angular/forms/model.ts @@ -7,7 +7,7 @@ import { AbstractControl, ValidatorFn } from '@angular/forms'; import { ErrorDto, Types } from '@app/framework/internal'; -import { State } from './../../state'; +import { State } from '../../state'; import { ErrorValidator } from './error-validator'; import { addValidator, hasNonCustomError, updateAll } from './forms-helper'; diff --git a/frontend/src/app/framework/angular/forms/progress-bar.component.ts b/frontend/src/app/framework/angular/forms/progress-bar.component.ts index 019ba1955..f58bb0bea 100644 --- a/frontend/src/app/framework/angular/forms/progress-bar.component.ts +++ b/frontend/src/app/framework/angular/forms/progress-bar.component.ts @@ -10,6 +10,7 @@ import * as ProgressBar from 'progressbar.js'; import { TypedSimpleChanges } from '@app/framework/internal'; @Component({ + standalone: true, selector: 'sqx-progress-bar', styles: [` :host ::ng-deep svg { diff --git a/frontend/src/app/framework/angular/forms/transform-input.directive.ts b/frontend/src/app/framework/angular/forms/transform-input.directive.ts index 5211de271..e513b912d 100644 --- a/frontend/src/app/framework/angular/forms/transform-input.directive.ts +++ b/frontend/src/app/framework/angular/forms/transform-input.directive.ts @@ -27,6 +27,7 @@ export const SQX_TRANSFORM_INPUT_VALUE_ACCESSOR: any = { providers: [ SQX_TRANSFORM_INPUT_VALUE_ACCESSOR, ], + standalone: true, }) export class TransformInputDirective implements ControlValueAccessor { private callChange = (_: any) => { /* NOOP */ }; diff --git a/frontend/src/app/framework/angular/global-error-handler.ts b/frontend/src/app/framework/angular/global-error-handler.ts index 4bcf1715e..9977c8746 100644 --- a/frontend/src/app/framework/angular/global-error-handler.ts +++ b/frontend/src/app/framework/angular/global-error-handler.ts @@ -6,7 +6,7 @@ */ import { ErrorHandler, Injectable, NgZone } from '@angular/core'; -import { DialogService } from './../internal'; +import { DialogService } from '../internal'; @Injectable() export class GlobalErrorHandler implements ErrorHandler { diff --git a/frontend/src/app/framework/angular/hover-background.directive.ts b/frontend/src/app/framework/angular/hover-background.directive.ts index f4678f942..93a95112a 100644 --- a/frontend/src/app/framework/angular/hover-background.directive.ts +++ b/frontend/src/app/framework/angular/hover-background.directive.ts @@ -9,6 +9,7 @@ import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/ @Directive({ selector: '[sqxHoverBackground]', + standalone: true, }) export class HoverBackgroundDirective { private previousBackground?: string | null; diff --git a/frontend/src/app/framework/angular/http/caching.interceptor.ts b/frontend/src/app/framework/angular/http/caching.interceptor.ts index a862f45ab..fe10e585d 100644 --- a/frontend/src/app/framework/angular/http/caching.interceptor.ts +++ b/frontend/src/app/framework/angular/http/caching.interceptor.ts @@ -5,40 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { catchError, Observable, of, tap, throwError } from 'rxjs'; +import { HttpErrorResponse, HttpHandlerFn, HttpRequest, HttpResponse } from '@angular/common/http'; +import { catchError, of, tap, throwError } from 'rxjs'; import { Types } from '@app/framework/internal'; -@Injectable() -export class CachingInterceptor implements HttpInterceptor { - private readonly cache: { [url: string]: HttpResponse<any> } = {}; +const CACHE: { [url: string]: HttpResponse<any> } = {}; - public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { - if (req.method === 'GET' && !req.reportProgress) { - const cacheEntry = this.cache[req.url]; +export const cachingInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn) => { + if (req.method === 'GET' && !req.reportProgress) { + const cacheEntry = CACHE[req.url]; - if (cacheEntry) { - req = req.clone({ headers: req.headers.set('If-None-Match', cacheEntry.headers.get('ETag')!) }); - } + if (cacheEntry) { + req = req.clone({ headers: req.headers.set('If-None-Match', cacheEntry.headers.get('ETag')!) }); + } - return next.handle(req).pipe( - tap(response => { - if (Types.is(response, HttpResponse)) { - if (response.headers.get('ETag')) { - this.cache[req.url] = response; - } - } - }), - catchError(error => { - if (Types.is(error, HttpErrorResponse) && error.status === 304 && cacheEntry) { - return of(cacheEntry); - } else { - return throwError(() => error); + return next(req).pipe( + tap(response => { + if (Types.is(response, HttpResponse)) { + if (response.headers.get('ETag')) { + CACHE[req.url] = response; } - })); - } else { - return next.handle(req); - } + } + }), + catchError(error => { + if (Types.is(error, HttpErrorResponse) && error.status === 304 && cacheEntry) { + return of(cacheEntry); + } else { + return throwError(() => error); + } + })); + } else { + return next(req); } -} +}; \ No newline at end of file diff --git a/frontend/src/app/framework/angular/http/http-extensions.spec.ts b/frontend/src/app/framework/angular/http/http-extensions.spec.ts index d9ce7f569..b3e9b2d16 100644 --- a/frontend/src/app/framework/angular/http/http-extensions.spec.ts +++ b/frontend/src/app/framework/angular/http/http-extensions.spec.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ErrorDto } from './../../utils/error'; +import { ErrorDto } from '../../utils/error'; import { parseError } from './http-extensions'; describe('ErrorParsing', () => { diff --git a/frontend/src/app/framework/angular/http/loading.interceptor.ts b/frontend/src/app/framework/angular/http/loading.interceptor.ts index 1be5033d2..5059f68ed 100644 --- a/frontend/src/app/framework/angular/http/loading.interceptor.ts +++ b/frontend/src/app/framework/angular/http/loading.interceptor.ts @@ -5,31 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { finalize, Observable } from 'rxjs'; +import { HttpHandlerFn, HttpRequest } from '@angular/common/http'; +import { inject } from '@angular/core'; +import { finalize } from 'rxjs'; import { LoadingService, MathHelper } from '@app/framework/internal'; -@Injectable() -export class LoadingInterceptor implements HttpInterceptor { - constructor( - private readonly loadingService: LoadingService, - ) { - } +export const loadingInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn) => { + const id = MathHelper.guid(); - public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { - const id = MathHelper.guid(); + const silent = req.headers.has('X-Silent'); - const silent = req.headers.has('X-Silent'); + if (silent) { + return next(req); + } - if (silent) { - return next.handle(req); - } + const loadingService = inject(LoadingService); - this.loadingService.startLoading(id); + loadingService.startLoading(id); - return next.handle(req).pipe(finalize(() => { - this.loadingService.completeLoading(id); - })); - } -} + return next(req).pipe(finalize(() => { + loadingService.completeLoading(id); + })); +}; diff --git a/frontend/src/app/framework/angular/if-once.directive.ts b/frontend/src/app/framework/angular/if-once.directive.ts index bde9850b6..f5926eae5 100644 --- a/frontend/src/app/framework/angular/if-once.directive.ts +++ b/frontend/src/app/framework/angular/if-once.directive.ts @@ -9,6 +9,7 @@ import { booleanAttribute, Directive, Input, TemplateRef, ViewContainerRef } fro @Directive({ selector: '[sqxIfOnce]', + standalone: true, }) export class IfOnceDirective { private hasView = false; diff --git a/frontend/src/app/framework/angular/image-source.directive.ts b/frontend/src/app/framework/angular/image-source.directive.ts index 4222b871c..262289124 100644 --- a/frontend/src/app/framework/angular/image-source.directive.ts +++ b/frontend/src/app/framework/angular/image-source.directive.ts @@ -12,6 +12,7 @@ const LAYOUT_CACHE: { [key: string]: { width: number; height: number } } = {}; @Directive({ selector: '[sqxImageSource]', + standalone: true, }) export class ImageSourceDirective implements OnDestroy, OnInit, AfterViewInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/image-url.directive.ts b/frontend/src/app/framework/angular/image-url.directive.ts index 5a88a20ad..86e370b3c 100644 --- a/frontend/src/app/framework/angular/image-url.directive.ts +++ b/frontend/src/app/framework/angular/image-url.directive.ts @@ -10,6 +10,7 @@ import { Subscriptions } from '@app/framework/internal'; @Directive({ selector: '[sqxImageUrl]', + standalone: true, }) export class ImageUrlDirective implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/language-selector.component.ts b/frontend/src/app/framework/angular/language-selector.component.ts index 8fc89db0c..2661e97bd 100644 --- a/frontend/src/app/framework/angular/language-selector.component.ts +++ b/frontend/src/app/framework/angular/language-selector.component.ts @@ -5,16 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, OnInit, Output } from '@angular/core'; import { FloatingPlacement, ModalModel } from '@app/framework/internal'; +import { DropdownMenuComponent } from './dropdown-menu.component'; +import { ModalPlacementDirective } from './modals/modal-placement.directive'; +import { ModalDirective } from './modals/modal.directive'; +import { TooltipDirective } from './modals/tooltip.directive'; export interface Language { iso2Code: string; englishName: string; isMasterLanguage?: boolean } @Component({ + standalone: true, selector: 'sqx-language-selector', styleUrls: ['./language-selector.component.scss'], templateUrl: './language-selector.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TooltipDirective, + ], }) export class LanguageSelectorComponent implements OnInit { @Output() diff --git a/frontend/src/app/framework/angular/language-selector.stories.ts b/frontend/src/app/framework/angular/language-selector.stories.ts index e96fa4449..14aae4ba4 100644 --- a/frontend/src/app/framework/angular/language-selector.stories.ts +++ b/frontend/src/app/framework/angular/language-selector.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { LanguageSelectorComponent, SqxFrameworkModule } from '@app/framework'; +import { LanguageSelectorComponent, RootViewComponent } from '@app/framework'; export default { title: 'Framework/Language-Selector', @@ -40,9 +39,7 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), + RootViewComponent, ], }), ], diff --git a/frontend/src/app/framework/angular/layout-container.directive.ts b/frontend/src/app/framework/angular/layout-container.directive.ts index 2639679c3..098fdf9f3 100644 --- a/frontend/src/app/framework/angular/layout-container.directive.ts +++ b/frontend/src/app/framework/angular/layout-container.directive.ts @@ -12,6 +12,7 @@ import { LayoutComponent } from './layout.component'; @Directive({ selector: '[sqxLayoutContainer]', + standalone: true, }) export class LayoutContainerDirective implements AfterViewInit { private readonly layouts: LayoutComponent[] = []; diff --git a/frontend/src/app/framework/angular/layout.component.ts b/frontend/src/app/framework/angular/layout.component.ts index 21cf9fa5c..d5ec49fd2 100644 --- a/frontend/src/app/framework/angular/layout.component.ts +++ b/frontend/src/app/framework/angular/layout.component.ts @@ -5,17 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf, NgTemplateOutlet } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, Input, numberAttribute, OnDestroy, OnInit, Optional, Renderer2, ViewChild } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, QueryParamsHandling, Router } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, QueryParamsHandling, Router, RouterLink } from '@angular/router'; import { concat, defer, filter, map, of } from 'rxjs'; import { LayoutContainerDirective } from './layout-container.directive'; +import { TranslatePipe } from './pipes/translate.pipe'; +import { StopClickDirective } from './stop-click.directive'; import { SidebarMenuDirective } from './template.directive'; @Component({ + standalone: true, selector: 'sqx-layout', styleUrls: ['./layout.component.scss'], templateUrl: './layout.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + NgIf, + NgTemplateOutlet, + RouterLink, + StopClickDirective, + TranslatePipe, + ], }) export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { private widthPrevious?: string; diff --git a/frontend/src/app/framework/angular/layout.stories.ts b/frontend/src/app/framework/angular/layout.stories.ts index b91edfeec..c4268270f 100644 --- a/frontend/src/app/framework/angular/layout.stories.ts +++ b/frontend/src/app/framework/angular/layout.stories.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { LayoutComponent, LocalizerService, SqxFrameworkModule } from '@app/framework'; +import { LayoutComponent, LayoutContainerDirective, ListViewComponent, LocalizerService, RootViewComponent } from '@app/framework'; export default { title: 'Framework/Layout', @@ -81,12 +80,15 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), + LayoutContainerDirective, + RootViewComponent, + ListViewComponent, ], providers: [ - { provide: LocalizerService, useValue: new LocalizerService({}) }, + { + provide: LocalizerService, + useValue: new LocalizerService({}), + }, ], }), ], diff --git a/frontend/src/app/framework/angular/list-view.component.ts b/frontend/src/app/framework/angular/list-view.component.ts index bd566e8cb..172b5c593 100644 --- a/frontend/src/app/framework/angular/list-view.component.ts +++ b/frontend/src/app/framework/angular/list-view.component.ts @@ -5,8 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf, NgTemplateOutlet } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, HostBinding, Input, Renderer2, ViewChild } from '@angular/core'; import { StatefulComponent } from '@app/framework/internal'; +import { CompensateScrollbarDirective } from './compensate-scrollbar.directive'; +import { LoaderComponent } from './loader.component'; +import { SyncScollingDirective } from './sync-scrolling.directive'; interface State { // True when loading. @@ -14,10 +18,18 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-list-view', styleUrls: ['./list-view.component.scss'], templateUrl: './list-view.component.html', changeDetection: ChangeDetectionStrategy.Default, + imports: [ + CompensateScrollbarDirective, + LoaderComponent, + NgIf, + NgTemplateOutlet, + SyncScollingDirective, + ], }) export class ListViewComponent extends StatefulComponent<State> implements AfterViewInit { private timer: any; diff --git a/frontend/src/app/framework/angular/loader.component.html b/frontend/src/app/framework/angular/loader.component.html index 1e8899e43..cdfa7b0f9 100644 --- a/frontend/src/app/framework/angular/loader.component.html +++ b/frontend/src/app/framework/angular/loader.component.html @@ -1,8 +1,8 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" - [attr.width]="size" - [attr.height]="size" - viewBox="0 0 100 100" - preserveAspectRatio="xMidYMid"> +<svg xmlns="http://www.w3.org/2000/svg" style="margin: auto; background: none; display: block; shape-rendering: auto;" + [attr.width]="size" + [attr.height]="size" + viewBox="0 0 100 100" + preserveAspectRatio="xMidYMid"> <circle cx="50" cy="50" fill="none" [class]="color" stroke-width="12" r="35" stroke-dasharray="164.93361431346415 56.97787143782138"> <animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform> </circle> diff --git a/frontend/src/app/framework/angular/loader.component.ts b/frontend/src/app/framework/angular/loader.component.ts index 946d2a2c6..0dc7ad700 100644 --- a/frontend/src/app/framework/angular/loader.component.ts +++ b/frontend/src/app/framework/angular/loader.component.ts @@ -10,6 +10,7 @@ import { ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-loader', styleUrls: ['./loader.component.scss'], templateUrl: './loader.component.html', diff --git a/frontend/src/app/framework/angular/loader.stories.ts b/frontend/src/app/framework/angular/loader.stories.ts index 16df4c0af..6492d4290 100644 --- a/frontend/src/app/framework/angular/loader.stories.ts +++ b/frontend/src/app/framework/angular/loader.stories.ts @@ -6,7 +6,7 @@ */ import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { LoaderComponent, SqxFrameworkModule } from '@app/framework'; +import { LoaderComponent } from '@app/framework'; export default { title: 'Framework/Loader', @@ -27,8 +27,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/long-hover.directive.stories.ts b/frontend/src/app/framework/angular/long-hover.directive.stories.ts index 9affa222a..457950254 100644 --- a/frontend/src/app/framework/angular/long-hover.directive.stories.ts +++ b/frontend/src/app/framework/angular/long-hover.directive.stories.ts @@ -5,14 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { action } from '@storybook/addon-actions'; import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; -import { CodeEditorComponent, LongHoverDirective, SqxFrameworkModule } from '@app/framework'; +import { LongHoverDirective } from '@app/framework'; export default { title: 'Framework/LongHover', - component: CodeEditorComponent, + component: LongHoverDirective, argTypes: { selector: { control: 'text', @@ -37,9 +36,6 @@ export default { decorators: [ moduleMetadata({ imports: [ - BrowserAnimationsModule, - SqxFrameworkModule, - SqxFrameworkModule.forRoot(), ], }), ], diff --git a/frontend/src/app/framework/angular/long-hover.directive.ts b/frontend/src/app/framework/angular/long-hover.directive.ts index da6fc3b46..e5fbe09cd 100644 --- a/frontend/src/app/framework/angular/long-hover.directive.ts +++ b/frontend/src/app/framework/angular/long-hover.directive.ts @@ -12,6 +12,7 @@ import { Directive, EventEmitter, HostListener, Input, numberAttribute, Output, @Directive({ selector: '[sqxLongHover]', + standalone: true, }) export class LongHoverDirective { private timerOut: Function | null = null; diff --git a/frontend/src/app/framework/angular/markdown.directive.ts b/frontend/src/app/framework/angular/markdown.directive.ts index 77e48b071..73f4ea113 100644 --- a/frontend/src/app/framework/angular/markdown.directive.ts +++ b/frontend/src/app/framework/angular/markdown.directive.ts @@ -10,6 +10,7 @@ import { renderMarkdown } from '@app/framework/internal'; @Directive({ selector: '[sqxMarkdown]', + standalone: true, }) export class MarkdownDirective { @Input('sqxMarkdown') diff --git a/frontend/src/app/framework/angular/modals/dialog-renderer.component.ts b/frontend/src/app/framework/angular/modals/dialog-renderer.component.ts index 9b20e5c9f..c09c47fde 100644 --- a/frontend/src/app/framework/angular/modals/dialog-renderer.component.ts +++ b/frontend/src/app/framework/angular/modals/dialog-renderer.component.ts @@ -5,9 +5,19 @@ * Copyright (c) Sebastian Stehle. All rights r vbeserved */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { timer } from 'rxjs'; import { DialogModel, DialogRequest, DialogService, fadeAnimation, Notification, StatefulComponent, Subscriptions, Tooltip } from '@app/framework/internal'; +import { FocusOnInitDirective } from '../forms/focus-on-init.directive'; +import { MarkdownPipe } from '../pipes/markdown.pipe'; +import { TranslatePipe } from '../pipes/translate.pipe'; +import { SafeHtmlPipe } from '../safe-html.pipe'; +import { ModalDialogComponent } from './modal-dialog.component'; +import { ModalPlacementDirective } from './modal-placement.directive'; +import { ModalDirective } from './modal.directive'; +import { TooltipDirective } from './tooltip.directive'; interface State { // The pending dialog request. @@ -21,6 +31,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-dialog-renderer', styleUrls: ['./dialog-renderer.component.scss'], templateUrl: './dialog-renderer.component.html', @@ -28,6 +39,19 @@ interface State { fadeAnimation, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FocusOnInitDirective, + FormsModule, + MarkdownPipe, + ModalDialogComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + SafeHtmlPipe, + TooltipDirective, + TranslatePipe, + ], }) export class DialogRendererComponent extends StatefulComponent<State> implements OnInit { private readonly subscriptions = new Subscriptions(); @@ -119,4 +143,4 @@ export class DialogRendererComponent extends StatefulComponent<State> implements notifications: s.notifications.filter(n => notification !== n), })); } -} \ No newline at end of file +} diff --git a/frontend/src/app/framework/angular/modals/modal-dialog.component.ts b/frontend/src/app/framework/angular/modals/modal-dialog.component.ts index 32976db4b..c039e4c51 100644 --- a/frontend/src/app/framework/angular/modals/modal-dialog.component.ts +++ b/frontend/src/app/framework/angular/modals/modal-dialog.component.ts @@ -5,10 +5,14 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, Renderer2, ViewChild } from '@angular/core'; import { fadeAnimation } from '@app/framework/internal'; +import { ShortcutComponent } from '../shortcut.component'; +import { TourStepDirective } from './tour-step.directive'; @Component({ + standalone: true, selector: 'sqx-modal-dialog', styleUrls: ['./modal-dialog.component.scss'], templateUrl: './modal-dialog.component.html', @@ -16,6 +20,11 @@ import { fadeAnimation } from '@app/framework/internal'; fadeAnimation, ], changeDetection: ChangeDetectionStrategy.Default, + imports: [ + NgIf, + ShortcutComponent, + TourStepDirective, + ], }) export class ModalDialogComponent implements AfterViewInit { @Output() diff --git a/frontend/src/app/framework/angular/modals/modal-placement.directive.ts b/frontend/src/app/framework/angular/modals/modal-placement.directive.ts index eef737e42..3e052afeb 100644 --- a/frontend/src/app/framework/angular/modals/modal-placement.directive.ts +++ b/frontend/src/app/framework/angular/modals/modal-placement.directive.ts @@ -11,6 +11,7 @@ import { FloatingPlacement, TypedSimpleChanges, Types } from '@app/framework/int @Directive({ selector: '[sqxAnchoredTo]', + standalone: true, }) export class ModalPlacementDirective implements AfterViewInit, OnDestroy { private currentListener?: any; diff --git a/frontend/src/app/framework/angular/modals/modal.directive.ts b/frontend/src/app/framework/angular/modals/modal.directive.ts index d76ba69b0..44f505d94 100644 --- a/frontend/src/app/framework/angular/modals/modal.directive.ts +++ b/frontend/src/app/framework/angular/modals/modal.directive.ts @@ -15,6 +15,7 @@ declare type Model = DialogModel | ModalModel | any; @Directive({ selector: '[sqxModal]', + standalone: true, }) export class ModalDirective<T = unknown> implements OnDestroy { private readonly eventsView = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/modals/root-view.component.ts b/frontend/src/app/framework/angular/modals/root-view.component.ts index f3e573628..465e5caf0 100644 --- a/frontend/src/app/framework/angular/modals/root-view.component.ts +++ b/frontend/src/app/framework/angular/modals/root-view.component.ts @@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, ViewChild, ViewContainerRef } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-root-view', styleUrls: ['./root-view.component.scss'], templateUrl: './root-view.component.html', diff --git a/frontend/src/app/framework/angular/modals/tooltip.directive.ts b/frontend/src/app/framework/angular/modals/tooltip.directive.ts index 0029d5758..ae9baa112 100644 --- a/frontend/src/app/framework/angular/modals/tooltip.directive.ts +++ b/frontend/src/app/framework/angular/modals/tooltip.directive.ts @@ -12,6 +12,7 @@ import { DialogService, FloatingPlacement, Tooltip } from '@app/framework/intern @Directive({ selector: '[title]:not(sqx-layout),[shortcut]', + standalone: true, }) export class TooltipDirective implements OnDestroy { private titleText: string | undefined | null; diff --git a/frontend/src/app/framework/angular/modals/tour-step.directive.ts b/frontend/src/app/framework/angular/modals/tour-step.directive.ts index 93ca153cf..17d18ec0d 100644 --- a/frontend/src/app/framework/angular/modals/tour-step.directive.ts +++ b/frontend/src/app/framework/angular/modals/tour-step.directive.ts @@ -11,6 +11,7 @@ import { StepDefinition, TourService } from './tour.service'; @Directive({ selector: '[sqxTourStep]', + standalone: true, }) export class TourStepDirective implements OnInit, OnDestroy, TourAnchorDirective { private isNextOnClick = false; diff --git a/frontend/src/app/framework/angular/modals/tour-template.component.ts b/frontend/src/app/framework/angular/modals/tour-template.component.ts index 61ef22c02..4b6b9f5ae 100644 --- a/frontend/src/app/framework/angular/modals/tour-template.component.ts +++ b/frontend/src/app/framework/angular/modals/tour-template.component.ts @@ -5,12 +5,18 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterContentInit, ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { fadeAnimation, StatefulComponent } from '@app/framework/internal'; -import { StepDefinition } from './tour.service'; -import { TourService } from './tour.service'; +import { MarkdownInlinePipe, MarkdownPipe } from '../pipes/markdown.pipe'; +import { TranslatePipe } from '../pipes/translate.pipe'; +import { SafeHtmlPipe } from '../safe-html.pipe'; +import { ModalPlacementDirective } from './modal-placement.directive'; +import { ModalDirective } from './modal.directive'; +import { StepDefinition, TourService } from './tour.service'; @Component({ + standalone: true, selector: 'sqx-tour-template', styleUrls: ['./tour-template.component.scss'], templateUrl: './tour-template.component.html', @@ -18,6 +24,15 @@ import { TourService } from './tour.service'; fadeAnimation, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + MarkdownInlinePipe, + MarkdownPipe, + ModalDirective, + ModalPlacementDirective, + NgIf, + SafeHtmlPipe, + TranslatePipe, + ], }) export class TourTemplateComponent extends StatefulComponent implements AfterContentInit { private delayedDestory: any; diff --git a/frontend/src/app/framework/angular/modals/tour.service.ts b/frontend/src/app/framework/angular/modals/tour.service.ts index c1b4960e2..157a58569 100644 --- a/frontend/src/app/framework/angular/modals/tour.service.ts +++ b/frontend/src/app/framework/angular/modals/tour.service.ts @@ -34,7 +34,9 @@ export function waitForAnchor(anchorId: string) { }; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TourService extends BaseTourService<StepDefinition> { private condition?: Subscription; diff --git a/frontend/src/app/framework/angular/pager.component.ts b/frontend/src/app/framework/angular/pager.component.ts index fdc9e3631..9e725c59a 100644 --- a/frontend/src/app/framework/angular/pager.component.ts +++ b/frontend/src/app/framework/angular/pager.component.ts @@ -5,16 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { PagingInfo } from './../state'; +import { FormsModule } from '@angular/forms'; +import { PagingInfo } from '../state'; +import { TooltipDirective } from './modals/tooltip.directive'; +import { TranslatePipe } from './pipes/translate.pipe'; export const PAGE_SIZES: ReadonlyArray<number> = [10, 20, 30, 50]; @Component({ + standalone: true, selector: 'sqx-pager', styleUrls: ['./pager.component.scss'], templateUrl: './pager.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class PagerComponent { @Output() diff --git a/frontend/src/app/framework/angular/pipes/colors.pipes.ts b/frontend/src/app/framework/angular/pipes/colors.pipes.ts index 0d604845a..62eaf391b 100644 --- a/frontend/src/app/framework/angular/pipes/colors.pipes.ts +++ b/frontend/src/app/framework/angular/pipes/colors.pipes.ts @@ -11,6 +11,7 @@ import { ColorHelper } from '@app/framework/utils/color-helper'; @Pipe({ name: 'sqxDarken', pure: true, + standalone: true, }) export class DarkenPipe implements PipeTransform { public transform(value: string, percentage: number): any { @@ -26,6 +27,7 @@ export class DarkenPipe implements PipeTransform { @Pipe({ name: 'sqxLighten', pure: true, + standalone: true, }) export class LightenPipe implements PipeTransform { public transform(value: string, percentage: number): any { @@ -41,6 +43,7 @@ export class LightenPipe implements PipeTransform { @Pipe({ name: 'sqxStringColor', pure: true, + standalone: true, }) export class StringColorPipe implements PipeTransform { public transform(value: string) { diff --git a/frontend/src/app/framework/angular/pipes/date-time.pipes.ts b/frontend/src/app/framework/angular/pipes/date-time.pipes.ts index 3cb9f51b8..2a15ed8b5 100644 --- a/frontend/src/app/framework/angular/pipes/date-time.pipes.ts +++ b/frontend/src/app/framework/angular/pipes/date-time.pipes.ts @@ -11,6 +11,7 @@ import { DateTime, Duration, Types } from '@app/framework/internal'; @Pipe({ name: 'sqxShortDate', pure: true, + standalone: true, }) export class ShortDatePipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -29,6 +30,7 @@ export class ShortDatePipe implements PipeTransform { @Pipe({ name: 'sqxISODate', pure: true, + standalone: true, }) export class ISODatePipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -47,6 +49,7 @@ export class ISODatePipe implements PipeTransform { @Pipe({ name: 'sqxDate', pure: true, + standalone: true, }) export class DatePipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -65,6 +68,7 @@ export class DatePipe implements PipeTransform { @Pipe({ name: 'sqxMonth', pure: true, + standalone: true, }) export class MonthPipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -83,6 +87,7 @@ export class MonthPipe implements PipeTransform { @Pipe({ name: 'sqxFromNow', pure: true, + standalone: true, }) export class FromNowPipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -101,6 +106,7 @@ export class FromNowPipe implements PipeTransform { @Pipe({ name: 'sqxDayOfWeek', pure: true, + standalone: true, }) export class DayOfWeekPipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -119,6 +125,7 @@ export class DayOfWeekPipe implements PipeTransform { @Pipe({ name: 'sqxDay', pure: true, + standalone: true, }) export class DayPipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -137,6 +144,7 @@ export class DayPipe implements PipeTransform { @Pipe({ name: 'sqxShortTime', pure: true, + standalone: true, }) export class ShortTimePipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -155,6 +163,7 @@ export class ShortTimePipe implements PipeTransform { @Pipe({ name: 'sqxFullDateTime', pure: true, + standalone: true, }) export class FullDateTimePipe implements PipeTransform { public transform(value: DateTime | string | undefined | null, fallback = ''): string { @@ -173,6 +182,7 @@ export class FullDateTimePipe implements PipeTransform { @Pipe({ name: 'sqxDuration', pure: true, + standalone: true, }) export class DurationPipe implements PipeTransform { public transform(value: Duration | undefined | null, fallback = ''): string { diff --git a/frontend/src/app/framework/angular/pipes/highlight.pipe.ts b/frontend/src/app/framework/angular/pipes/highlight.pipe.ts index 5f7a72511..5e386159c 100644 --- a/frontend/src/app/framework/angular/pipes/highlight.pipe.ts +++ b/frontend/src/app/framework/angular/pipes/highlight.pipe.ts @@ -11,6 +11,7 @@ import { escapeHTML, Types } from '@app/framework/internal'; @Pipe({ name: 'sqxHighlight', pure: false, + standalone: true, }) export class HighlightPipe implements PipeTransform { public transform(text: string, highlight: string | RegExp | undefined | null): string { diff --git a/frontend/src/app/framework/angular/pipes/keys.pipe.ts b/frontend/src/app/framework/angular/pipes/keys.pipe.ts index 0062cbf36..8af522755 100644 --- a/frontend/src/app/framework/angular/pipes/keys.pipe.ts +++ b/frontend/src/app/framework/angular/pipes/keys.pipe.ts @@ -10,6 +10,7 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'sqxKeys', pure: true, + standalone: true, }) export class KeysPipe implements PipeTransform { public transform(value: any): any { diff --git a/frontend/src/app/framework/angular/pipes/markdown.pipe.ts b/frontend/src/app/framework/angular/pipes/markdown.pipe.ts index d47942017..4ba142856 100644 --- a/frontend/src/app/framework/angular/pipes/markdown.pipe.ts +++ b/frontend/src/app/framework/angular/pipes/markdown.pipe.ts @@ -11,6 +11,7 @@ import { renderMarkdown } from '@app/framework/internal'; @Pipe({ name: 'sqxMarkdown', pure: true, + standalone: true, }) export class MarkdownPipe implements PipeTransform { public transform(text: string | undefined | null): string { @@ -21,6 +22,7 @@ export class MarkdownPipe implements PipeTransform { @Pipe({ name: 'sqxMarkdownInline', pure: true, + standalone: true, }) export class MarkdownInlinePipe implements PipeTransform { public transform(text: string | undefined | null): string { diff --git a/frontend/src/app/framework/angular/pipes/name.pipe.ts b/frontend/src/app/framework/angular/pipes/name.pipe.ts index b3d35fd17..ed0e53850 100644 --- a/frontend/src/app/framework/angular/pipes/name.pipe.ts +++ b/frontend/src/app/framework/angular/pipes/name.pipe.ts @@ -11,6 +11,7 @@ import { StringHelper } from '@app/framework/internal'; @Pipe({ name: 'sqxDisplayName', pure: true, + standalone: true, }) export class DisplayNamePipe implements PipeTransform { public transform(value: any, field1 = 'label', field2 = 'name'): any { diff --git a/frontend/src/app/framework/angular/pipes/numbers.pipes.ts b/frontend/src/app/framework/angular/pipes/numbers.pipes.ts index 5ea94366d..655c141dc 100644 --- a/frontend/src/app/framework/angular/pipes/numbers.pipes.ts +++ b/frontend/src/app/framework/angular/pipes/numbers.pipes.ts @@ -10,6 +10,7 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'sqxKNumber', pure: true, + standalone: true, }) export class KNumberPipe implements PipeTransform { public transform(value: number) { @@ -34,6 +35,7 @@ export class KNumberPipe implements PipeTransform { @Pipe({ name: 'sqxFileSize', pure: true, + standalone: true, }) export class FileSizePipe implements PipeTransform { public transform(value: number) { diff --git a/frontend/src/app/framework/angular/pipes/strings.pipes.ts b/frontend/src/app/framework/angular/pipes/strings.pipes.ts index aed890512..1c29d22f8 100644 --- a/frontend/src/app/framework/angular/pipes/strings.pipes.ts +++ b/frontend/src/app/framework/angular/pipes/strings.pipes.ts @@ -10,6 +10,7 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'sqxJoin', pure: true, + standalone: true, }) export class JoinPipe implements PipeTransform { public transform(value: ReadonlyArray<string>) { diff --git a/frontend/src/app/framework/angular/pipes/translate.pipe.spec.ts b/frontend/src/app/framework/angular/pipes/translate.pipe.spec.ts index b38e96acc..5c497f615 100644 --- a/frontend/src/app/framework/angular/pipes/translate.pipe.spec.ts +++ b/frontend/src/app/framework/angular/pipes/translate.pipe.spec.ts @@ -6,7 +6,7 @@ */ import { IMock, It, Mock } from 'typemoq'; -import { LocalizerService } from './../../services/localizer.service'; +import { LocalizerService } from '../../services/localizer.service'; import { TranslatePipe } from './translate.pipe'; describe('TranslatePipe', () => { diff --git a/frontend/src/app/framework/angular/pipes/translate.pipe.ts b/frontend/src/app/framework/angular/pipes/translate.pipe.ts index dbead7bd3..cbdfe86fb 100644 --- a/frontend/src/app/framework/angular/pipes/translate.pipe.ts +++ b/frontend/src/app/framework/angular/pipes/translate.pipe.ts @@ -11,6 +11,7 @@ import { LocalizerService, Types } from '@app/framework/internal'; @Pipe({ name: 'sqxTranslate', pure: true, + standalone: true, }) export class TranslatePipe implements PipeTransform { constructor( diff --git a/frontend/src/app/framework/angular/resized.directive.ts b/frontend/src/app/framework/angular/resized.directive.ts index 65e684466..4759408ac 100644 --- a/frontend/src/app/framework/angular/resized.directive.ts +++ b/frontend/src/app/framework/angular/resized.directive.ts @@ -12,6 +12,7 @@ import { ResizeListener, ResizeService, Subscriptions } from '@app/framework/int @Directive({ selector: '[sqxResized], [sqxResizeCondition]', + standalone: true, }) export class ResizedDirective implements ResizeListener { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/routers/can-deactivate.guard.spec.ts b/frontend/src/app/framework/angular/routers/can-deactivate.guard.spec.ts index e1510a2b0..0efd7c6d5 100644 --- a/frontend/src/app/framework/angular/routers/can-deactivate.guard.spec.ts +++ b/frontend/src/app/framework/angular/routers/can-deactivate.guard.spec.ts @@ -6,7 +6,7 @@ */ import { of } from 'rxjs'; -import { CanDeactivateGuard } from './can-deactivate.guard'; +import { canDeactivateGuard } from './can-deactivate.guard'; describe('CanDeactivateGuard', () => { it('should call component', () => { @@ -20,7 +20,7 @@ describe('CanDeactivateGuard', () => { }, }; - const result = new CanDeactivateGuard().canDeactivate(component); + const result = canDeactivateGuard(component); expect(result).toBeDefined(); expect(called).toBeTruthy(); diff --git a/frontend/src/app/framework/angular/routers/can-deactivate.guard.ts b/frontend/src/app/framework/angular/routers/can-deactivate.guard.ts index 99f014896..5fc500c46 100644 --- a/frontend/src/app/framework/angular/routers/can-deactivate.guard.ts +++ b/frontend/src/app/framework/angular/routers/can-deactivate.guard.ts @@ -5,7 +5,6 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; import { UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; @@ -13,9 +12,6 @@ export interface CanComponentDeactivate { canDeactivate(): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree; } -@Injectable() -export class CanDeactivateGuard { - public canDeactivate(component: CanComponentDeactivate) { - return component?.canDeactivate ? component.canDeactivate() : true; - } -} +export const canDeactivateGuard = (component: CanComponentDeactivate) => { + return component?.canDeactivate ? component.canDeactivate() : true; +}; diff --git a/frontend/src/app/framework/angular/routers/parent-link.directive.ts b/frontend/src/app/framework/angular/routers/parent-link.directive.ts index 13f1c5374..843784153 100644 --- a/frontend/src/app/framework/angular/routers/parent-link.directive.ts +++ b/frontend/src/app/framework/angular/routers/parent-link.directive.ts @@ -11,6 +11,7 @@ import { Subscriptions } from '@app/framework/internal'; @Directive({ selector: '[sqxParentLink]', + standalone: true, }) export class ParentLinkDirective implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/routers/router-2-state.spec.ts b/frontend/src/app/framework/angular/routers/router-2-state.spec.ts index 2620016b7..7200a7b26 100644 --- a/frontend/src/app/framework/angular/routers/router-2-state.spec.ts +++ b/frontend/src/app/framework/angular/routers/router-2-state.spec.ts @@ -8,7 +8,7 @@ import { NavigationExtras, Params, Router } from '@angular/router'; import { IMock, It, Mock, Times } from 'typemoq'; import { LocalStoreService } from '@app/framework/internal'; -import { State } from './../../state'; +import { State } from '../../state'; import { PagingSynchronizer, QueryParams, Router2State, StringKeysSynchronizer, StringSynchronizer } from './router-2-state'; describe('Router2State', () => { diff --git a/frontend/src/app/framework/angular/safe-html.pipe.ts b/frontend/src/app/framework/angular/safe-html.pipe.ts index efb29ce3a..2970d79be 100644 --- a/frontend/src/app/framework/angular/safe-html.pipe.ts +++ b/frontend/src/app/framework/angular/safe-html.pipe.ts @@ -11,6 +11,7 @@ import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeUrl } from '@angular/platf @Pipe({ name: 'sqxSafeHtml', pure: true, + standalone: true, }) export class SafeHtmlPipe implements PipeTransform { constructor( @@ -26,6 +27,7 @@ export class SafeHtmlPipe implements PipeTransform { @Pipe({ name: 'sqxSafeUrl', pure: true, + standalone: true, }) export class SafeUrlPipe implements PipeTransform { constructor( @@ -41,6 +43,7 @@ export class SafeUrlPipe implements PipeTransform { @Pipe({ name: 'sqxSafeResourceUrl', pure: true, + standalone: true, }) export class SafeResourceUrlPipe implements PipeTransform { constructor( diff --git a/frontend/src/app/framework/angular/scroll-active.directive.ts b/frontend/src/app/framework/angular/scroll-active.directive.ts index 95fa5c75e..b5ed39678 100644 --- a/frontend/src/app/framework/angular/scroll-active.directive.ts +++ b/frontend/src/app/framework/angular/scroll-active.directive.ts @@ -11,6 +11,7 @@ import { AfterViewInit, booleanAttribute, Directive, ElementRef, Input, Renderer @Directive({ selector: '[sqxScrollActive]', + standalone: true, }) export class ScrollActiveDirective implements AfterViewInit { @Input({ alias: 'sqxScrollActive', transform: booleanAttribute }) diff --git a/frontend/src/app/framework/angular/shortcut.component.ts b/frontend/src/app/framework/angular/shortcut.component.ts index afcc8203e..3db81f190 100644 --- a/frontend/src/app/framework/angular/shortcut.component.ts +++ b/frontend/src/app/framework/angular/shortcut.component.ts @@ -9,6 +9,7 @@ import { booleanAttribute, ChangeDetectorRef, Component, EventEmitter, Input, On import { ShortcutService } from '@app/framework/internal'; @Component({ + standalone: true, selector: 'sqx-shortcut', template: '', }) diff --git a/frontend/src/app/framework/angular/shortcut.directive.ts b/frontend/src/app/framework/angular/shortcut.directive.ts index 6fd93583c..1abb75678 100644 --- a/frontend/src/app/framework/angular/shortcut.directive.ts +++ b/frontend/src/app/framework/angular/shortcut.directive.ts @@ -12,6 +12,7 @@ import { ShortcutService } from '@app/framework/internal'; @Directive({ selector: '[shortcut]', + standalone: true, }) export class ShortcutDirective implements OnDestroy, OnInit { private subscription?: Function; diff --git a/frontend/src/app/framework/angular/stateful.component.ts b/frontend/src/app/framework/angular/stateful.component.ts index 7bc20c851..12aca7621 100644 --- a/frontend/src/app/framework/angular/stateful.component.ts +++ b/frontend/src/app/framework/angular/stateful.component.ts @@ -8,7 +8,7 @@ import { ChangeDetectorRef, Directive, inject, OnDestroy } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; import { skip, Subscription } from 'rxjs'; -import { State } from './../state'; +import { State } from '../state'; @Directive() export abstract class StatefulComponent<T extends {} = object> extends State<T> implements OnDestroy { diff --git a/frontend/src/app/framework/angular/status-icon.component.ts b/frontend/src/app/framework/angular/status-icon.component.ts index 1c9a5bab3..b89b18882 100644 --- a/frontend/src/app/framework/angular/status-icon.component.ts +++ b/frontend/src/app/framework/angular/status-icon.component.ts @@ -5,13 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { TooltipDirective } from './modals/tooltip.directive'; @Component({ + standalone: true, selector: 'sqx-status-icon', styleUrls: ['./status-icon.component.scss'], templateUrl: './status-icon.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + TooltipDirective, + ], }) export class StatusIconComponent { @Input({ required: true }) diff --git a/frontend/src/app/framework/angular/stop-click.directive.ts b/frontend/src/app/framework/angular/stop-click.directive.ts index 3fb88e673..87229b6cf 100644 --- a/frontend/src/app/framework/angular/stop-click.directive.ts +++ b/frontend/src/app/framework/angular/stop-click.directive.ts @@ -9,6 +9,7 @@ import { booleanAttribute, Directive, HostListener, Input } from '@angular/core' @Directive({ selector: '[sqxStopClick]', + standalone: true, }) export class StopClickDirective { @Input({ alias: 'sqxStopClick', transform: booleanAttribute }) diff --git a/frontend/src/app/framework/angular/stop-drag.directive.ts b/frontend/src/app/framework/angular/stop-drag.directive.ts index 9fe4a6971..64a39e326 100644 --- a/frontend/src/app/framework/angular/stop-drag.directive.ts +++ b/frontend/src/app/framework/angular/stop-drag.directive.ts @@ -9,6 +9,7 @@ import { booleanAttribute, Directive, HostListener, Input } from '@angular/core' @Directive({ selector: '[sqxStopDrag]', + standalone: true, }) export class StopDragDirective { @Input({ alias: 'sqxStopDrag', transform: booleanAttribute }) diff --git a/frontend/src/app/framework/angular/subscriptions.ts b/frontend/src/app/framework/angular/subscriptions.ts index 9b29bb5c4..266799d4c 100644 --- a/frontend/src/app/framework/angular/subscriptions.ts +++ b/frontend/src/app/framework/angular/subscriptions.ts @@ -7,7 +7,7 @@ import { ChangeDetectorRef, inject, ViewRef } from '@angular/core'; import { catchError, EMPTY, Observable, Subscription } from 'rxjs'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; export type UnsubscribeFunction = () => void; diff --git a/frontend/src/app/framework/angular/sync-scrolling.directive.ts b/frontend/src/app/framework/angular/sync-scrolling.directive.ts index c39c72ee2..33f6273dd 100644 --- a/frontend/src/app/framework/angular/sync-scrolling.directive.ts +++ b/frontend/src/app/framework/angular/sync-scrolling.directive.ts @@ -9,6 +9,7 @@ import { Directive, HostListener, Input, Renderer2 } from '@angular/core'; @Directive({ selector: '[sqxSyncScrolling]', + standalone: true, }) export class SyncScollingDirective { @Input('sqxSyncScrolling') diff --git a/frontend/src/app/framework/angular/sync-width.directive.ts b/frontend/src/app/framework/angular/sync-width.directive.ts index 4fed351a0..f884fe643 100644 --- a/frontend/src/app/framework/angular/sync-width.directive.ts +++ b/frontend/src/app/framework/angular/sync-width.directive.ts @@ -10,6 +10,7 @@ import { ResizeListener, ResizeService, Subscriptions } from '@app/framework/int @Directive({ selector: '[sqxSyncWidth]', + standalone: true, }) export class SyncWidthDirective implements AfterViewInit, ResizeListener { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/framework/angular/tab-router-link.directive.ts b/frontend/src/app/framework/angular/tab-router-link.directive.ts index cbd830ea6..3ab2cd319 100644 --- a/frontend/src/app/framework/angular/tab-router-link.directive.ts +++ b/frontend/src/app/framework/angular/tab-router-link.directive.ts @@ -10,6 +10,7 @@ import { ActivatedRoute, Router } from '@angular/router'; @Directive({ selector: '[sqxTabRouterLink]', + standalone: true, }) export class TabRouterlinkDirective { @Input('sqxTabRouterLink') diff --git a/frontend/src/app/framework/angular/template-wrapper.directive.ts b/frontend/src/app/framework/angular/template-wrapper.directive.ts index 0a4ee4c43..5eb5b5119 100644 --- a/frontend/src/app/framework/angular/template-wrapper.directive.ts +++ b/frontend/src/app/framework/angular/template-wrapper.directive.ts @@ -10,6 +10,7 @@ import { TypedSimpleChanges } from '@app/framework/internal'; @Directive({ selector: '[sqxTemplateWrapper]', + standalone: true, }) export class TemplateWrapperDirective implements OnDestroy, OnInit { @Input() diff --git a/frontend/src/app/framework/angular/template.directive.ts b/frontend/src/app/framework/angular/template.directive.ts index bb66f7e18..de31bec85 100644 --- a/frontend/src/app/framework/angular/template.directive.ts +++ b/frontend/src/app/framework/angular/template.directive.ts @@ -11,6 +11,7 @@ import { Directive, TemplateRef } from '@angular/core'; @Directive({ selector: '[sidebarMenu]', + standalone: true, }) export class SidebarMenuDirective { constructor( diff --git a/frontend/src/app/framework/angular/title.component.spec.ts b/frontend/src/app/framework/angular/title.component.spec.ts index 18f5deaf3..596086824 100644 --- a/frontend/src/app/framework/angular/title.component.spec.ts +++ b/frontend/src/app/framework/angular/title.component.spec.ts @@ -7,7 +7,7 @@ import { Router } from '@angular/router'; import { IMock, It, Mock, Times } from 'typemoq'; -import { TitleService } from './../internal'; +import { TitleService } from '../internal'; import { TitleComponent } from './title.component'; describe('TitleComponent', () => { diff --git a/frontend/src/app/framework/angular/title.component.ts b/frontend/src/app/framework/angular/title.component.ts index 354709ff2..7a6f7994c 100644 --- a/frontend/src/app/framework/angular/title.component.ts +++ b/frontend/src/app/framework/angular/title.component.ts @@ -10,6 +10,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { TitleService, Types } from '@app/framework/internal'; @Component({ + standalone: true, selector: 'sqx-title', template: '', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/frontend/src/app/framework/angular/toolbar.component.ts b/frontend/src/app/framework/angular/toolbar.component.ts index fc251cf8a..bd10cb279 100644 --- a/frontend/src/app/framework/angular/toolbar.component.ts +++ b/frontend/src/app/framework/angular/toolbar.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ToolbarService } from '@app/framework/internal'; +import { TranslatePipe } from './pipes/translate.pipe'; @Component({ + standalone: true, selector: 'sqx-toolbar', styleUrls: ['./toolbar.component.scss'], templateUrl: './toolbar.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + NgFor, + NgIf, + TranslatePipe, + ], }) export class ToolbarComponent { constructor( diff --git a/frontend/src/app/framework/angular/video-player.component.ts b/frontend/src/app/framework/angular/video-player.component.ts index 365b162ae..eb8ab67cb 100644 --- a/frontend/src/app/framework/angular/video-player.component.ts +++ b/frontend/src/app/framework/angular/video-player.component.ts @@ -11,6 +11,7 @@ import { ResourceLoaderService } from '@app/framework/internal'; declare const videojs: any; @Component({ + standalone: true, selector: 'sqx-video-player', styleUrls: ['./video-player.component.scss'], templateUrl: './video-player.component.html', diff --git a/frontend/src/app/framework/declarations.ts b/frontend/src/app/framework/declarations.ts deleted file mode 100644 index 6f7d8935f..000000000 --- a/frontend/src/app/framework/declarations.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './angular/avatar.component'; -export * from './angular/code.component'; -export * from './angular/compensate-scrollbar.directive'; -export * from './angular/dropdown-menu.component'; -export * from './angular/external-link.directive'; -export * from './angular/forms/confirm-click.directive'; -export * from './angular/forms/control-errors-messages.component'; -export * from './angular/forms/control-errors.component'; -export * from './angular/forms/copy-global.directive'; -export * from './angular/forms/copy.directive'; -export * from './angular/forms/editable-title.component'; -export * from './angular/forms/editors/autocomplete.component'; -export * from './angular/forms/editors/checkbox-group.component'; -export * from './angular/forms/editors/code-editor.component'; -export * from './angular/forms/editors/color-picker.component'; -export * from './angular/forms/editors/date-time-editor.component'; -export * from './angular/forms/editors/dropdown.component'; -export * from './angular/forms/editors/localized-input.component'; -export * from './angular/forms/editors/radio-group.component'; -export * from './angular/forms/editors/stars.component'; -export * from './angular/forms/editors/tag-editor.component'; -export * from './angular/forms/editors/toggle.component'; -export * from './angular/forms/extended-form-array'; -export * from './angular/forms/extended-form-group'; -export * from './angular/forms/file-drop.directive'; -export * from './angular/forms/focus-on-init.directive'; -export * from './angular/forms/form-alert.component'; -export * from './angular/forms/form-error.component'; -export * from './angular/forms/form-hint.component'; -export * from './angular/forms/forms-helper'; -export * from './angular/forms/indeterminate-value.directive'; -export * from './angular/forms/model'; -export * from './angular/forms/progress-bar.component'; -export * from './angular/forms/templated-form-array'; -export * from './angular/forms/transform-input.directive'; -export * from './angular/forms/validators'; -export * from './angular/global-error-handler'; -export * from './angular/hover-background.directive'; -export * from './angular/http/caching.interceptor'; -export * from './angular/http/http-extensions'; -export * from './angular/http/loading.interceptor'; -export * from './angular/if-once.directive'; -export * from './angular/image-source.directive'; -export * from './angular/image-url.directive'; -export * from './angular/language-selector.component'; -export * from './angular/layout-container.directive'; -export * from './angular/layout.component'; -export * from './angular/list-view.component'; -export * from './angular/loader.component'; -export * from './angular/long-hover.directive'; -export * from './angular/markdown.directive'; -export * from './angular/modals/dialog-renderer.component'; -export * from './angular/modals/modal-dialog.component'; -export * from './angular/modals/modal-placement.directive'; -export * from './angular/modals/modal.directive'; -export * from './angular/modals/root-view.component'; -export * from './angular/modals/tooltip.directive'; -export * from './angular/modals/tour-step.directive'; -export * from './angular/modals/tour-template.component'; -export * from './angular/modals/tour.service'; -export * from './angular/pager.component'; -export * from './angular/pipes/colors.pipes'; -export * from './angular/pipes/date-time.pipes'; -export * from './angular/pipes/highlight.pipe'; -export * from './angular/pipes/keys.pipe'; -export * from './angular/pipes/markdown.pipe'; -export * from './angular/pipes/name.pipe'; -export * from './angular/pipes/numbers.pipes'; -export * from './angular/pipes/strings.pipes'; -export * from './angular/pipes/translate.pipe'; -export * from './angular/resized.directive'; -export * from './angular/routers/can-deactivate.guard'; -export * from './angular/routers/parent-link.directive'; -export * from './angular/routers/router-2-state'; -export * from './angular/safe-html.pipe'; -export * from './angular/scroll-active.directive'; -export * from './angular/shortcut.component'; -export * from './angular/shortcut.directive'; -export * from './angular/status-icon.component'; -export * from './angular/stop-click.directive'; -export * from './angular/stop-drag.directive'; -export * from './angular/sync-scrolling.directive'; -export * from './angular/sync-width.directive'; -export * from './angular/tab-router-link.directive'; -export * from './angular/template-wrapper.directive'; -export * from './angular/template.directive'; -export * from './angular/title.component'; -export * from './angular/toolbar.component'; -export * from './angular/video-player.component'; -export * from './internal'; -export * from './state'; diff --git a/frontend/src/app/framework/index.ts b/frontend/src/app/framework/index.ts index 52813190b..30c3d256f 100644 --- a/frontend/src/app/framework/index.ts +++ b/frontend/src/app/framework/index.ts @@ -5,7 +5,97 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ + import './utils/array-extensions'; -export * from './declarations'; -export * from './module'; +export * from './angular/avatar.component'; +export * from './angular/code.component'; +export * from './angular/compensate-scrollbar.directive'; +export * from './angular/dropdown-menu.component'; +export * from './angular/external-link.directive'; +export * from './angular/forms/confirm-click.directive'; +export * from './angular/forms/control-errors-messages.component'; +export * from './angular/forms/control-errors.component'; +export * from './angular/forms/copy-global.directive'; +export * from './angular/forms/copy.directive'; +export * from './angular/forms/editable-title.component'; +export * from './angular/forms/editors/autocomplete.component'; +export * from './angular/forms/editors/checkbox-group.component'; +export * from './angular/forms/editors/code-editor.component'; +export * from './angular/forms/editors/color-picker.component'; +export * from './angular/forms/editors/date-time-editor.component'; +export * from './angular/forms/editors/dropdown.component'; +export * from './angular/forms/editors/localized-input.component'; +export * from './angular/forms/editors/radio-group.component'; +export * from './angular/forms/editors/stars.component'; +export * from './angular/forms/editors/tag-editor.component'; +export * from './angular/forms/editors/toggle.component'; +export * from './angular/forms/extended-form-array'; +export * from './angular/forms/extended-form-group'; +export * from './angular/forms/file-drop.directive'; +export * from './angular/forms/focus-on-init.directive'; +export * from './angular/forms/form-alert.component'; +export * from './angular/forms/form-error.component'; +export * from './angular/forms/form-hint.component'; +export * from './angular/forms/forms-helper'; +export * from './angular/forms/indeterminate-value.directive'; +export * from './angular/forms/model'; +export * from './angular/forms/progress-bar.component'; +export * from './angular/forms/templated-form-array'; +export * from './angular/forms/transform-input.directive'; +export * from './angular/forms/validators'; +export * from './angular/global-error-handler'; +export * from './angular/hover-background.directive'; +export * from './angular/http/caching.interceptor'; +export * from './angular/http/http-extensions'; +export * from './angular/http/loading.interceptor'; +export * from './angular/if-once.directive'; +export * from './angular/image-source.directive'; +export * from './angular/image-url.directive'; +export * from './angular/language-selector.component'; +export * from './angular/layout-container.directive'; +export * from './angular/layout.component'; +export * from './angular/list-view.component'; +export * from './angular/loader.component'; +export * from './angular/long-hover.directive'; +export * from './angular/markdown.directive'; +export * from './angular/modals/dialog-renderer.component'; +export * from './angular/modals/modal-dialog.component'; +export * from './angular/modals/modal-placement.directive'; +export * from './angular/modals/modal.directive'; +export * from './angular/modals/root-view.component'; +export * from './angular/modals/tooltip.directive'; +export * from './angular/modals/tour-step.directive'; +export * from './angular/modals/tour-template.component'; +export * from './angular/modals/tour.service'; +export * from './angular/pager.component'; +export * from './angular/pipes/colors.pipes'; +export * from './angular/pipes/date-time.pipes'; +export * from './angular/pipes/highlight.pipe'; +export * from './angular/pipes/keys.pipe'; +export * from './angular/pipes/markdown.pipe'; +export * from './angular/pipes/name.pipe'; +export * from './angular/pipes/numbers.pipes'; +export * from './angular/pipes/strings.pipes'; +export * from './angular/pipes/translate.pipe'; +export * from './angular/resized.directive'; +export * from './angular/routers/can-deactivate.guard'; +export * from './angular/routers/parent-link.directive'; +export * from './angular/routers/router-2-state'; +export * from './angular/safe-html.pipe'; +export * from './angular/scroll-active.directive'; +export * from './angular/shortcut.component'; +export * from './angular/shortcut.directive'; +export * from './angular/status-icon.component'; +export * from './angular/stop-click.directive'; +export * from './angular/stop-drag.directive'; +export * from './angular/sync-scrolling.directive'; +export * from './angular/sync-width.directive'; +export * from './angular/tab-router-link.directive'; +export * from './angular/template-wrapper.directive'; +export * from './angular/template.directive'; +export * from './angular/title.component'; +export * from './angular/toolbar.component'; +export * from './angular/video-player.component'; +export * from './internal'; +export * from './state'; diff --git a/frontend/src/app/framework/module.ts b/frontend/src/app/framework/module.ts deleted file mode 100644 index ffdfbbefb..000000000 --- a/frontend/src/app/framework/module.ts +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { CommonModule } from '@angular/common'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ErrorHandler, Injector, ModuleWithProviders, NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { RouterModule } from '@angular/router'; -import { ColorPickerModule } from 'ngx-color-picker'; -import { TourService as BaseTourService } from 'ngx-ui-tour-core'; -import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, CopyGlobalDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, SidebarMenuDirective, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, StringColorPipe, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TourService, TourStepDirective, TourTemplateComponent, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; - -@NgModule({ - imports: [ - ColorPickerModule, - CommonModule, - FormsModule, - ReactiveFormsModule, - RouterModule, - ], - declarations: [ - AutocompleteComponent, - AvatarComponent, - CheckboxGroupComponent, - CodeComponent, - CodeEditorComponent, - ColorPickerComponent, - CompensateScrollbarDirective, - ConfirmClickDirective, - ControlErrorsComponent, - ControlErrorsMessagesComponent, - CopyDirective, - CopyGlobalDirective, - DarkenPipe, - DatePipe, - DateTimeEditorComponent, - DayOfWeekPipe, - DayPipe, - DialogRendererComponent, - DisplayNamePipe, - DropdownComponent, - DropdownMenuComponent, - DurationPipe, - EditableTitleComponent, - ExternalLinkDirective, - FileDropDirective, - FileSizePipe, - FocusOnInitDirective, - FormAlertComponent, - FormErrorComponent, - FormHintComponent, - FromNowPipe, - FullDateTimePipe, - HighlightPipe, - HoverBackgroundDirective, - IfOnceDirective, - ImageSourceDirective, - ImageUrlDirective, - IndeterminateValueDirective, - ISODatePipe, - JoinPipe, - KeysPipe, - KNumberPipe, - LanguageSelectorComponent, - LayoutComponent, - LayoutContainerDirective, - LightenPipe, - ListViewComponent, - LoaderComponent, - LocalizedInputComponent, - LongHoverDirective, - MarkdownDirective, - MarkdownInlinePipe, - MarkdownPipe, - ModalDialogComponent, - ModalDirective, - ModalPlacementDirective, - MonthPipe, - PagerComponent, - ParentLinkDirective, - ProgressBarComponent, - RadioGroupComponent, - ResizedDirective, - RootViewComponent, - SafeHtmlPipe, - SafeResourceUrlPipe, - SafeUrlPipe, - ScrollActiveDirective, - ShortcutComponent, - ShortcutDirective, - ShortDatePipe, - ShortTimePipe, - SidebarMenuDirective, - StarsComponent, - StatusIconComponent, - StopClickDirective, - StopDragDirective, - StringColorPipe, - SyncScollingDirective, - SyncWidthDirective, - TabRouterlinkDirective, - TagEditorComponent, - TemplateWrapperDirective, - TitleComponent, - ToggleComponent, - ToolbarComponent, - TooltipDirective, - TourStepDirective, - TourTemplateComponent, - TransformInputDirective, - TranslatePipe, - VideoPlayerComponent, - ], - exports: [ - AutocompleteComponent, - AvatarComponent, - CheckboxGroupComponent, - CodeComponent, - CodeEditorComponent, - ColorPickerComponent, - CommonModule, - CompensateScrollbarDirective, - ConfirmClickDirective, - ControlErrorsComponent, - CopyDirective, - CopyGlobalDirective, - DarkenPipe, - DatePipe, - DateTimeEditorComponent, - DayOfWeekPipe, - DayPipe, - DialogRendererComponent, - DisplayNamePipe, - DropdownComponent, - DropdownMenuComponent, - DurationPipe, - EditableTitleComponent, - ExternalLinkDirective, - FileDropDirective, - FileSizePipe, - FocusOnInitDirective, - FormAlertComponent, - FormErrorComponent, - FormHintComponent, - FormsModule, - FromNowPipe, - FullDateTimePipe, - HighlightPipe, - HoverBackgroundDirective, - IfOnceDirective, - ImageSourceDirective, - ImageUrlDirective, - IndeterminateValueDirective, - ISODatePipe, - JoinPipe, - KeysPipe, - KNumberPipe, - LanguageSelectorComponent, - LayoutComponent, - LayoutContainerDirective, - LightenPipe, - ListViewComponent, - LoaderComponent, - LocalizedInputComponent, - LongHoverDirective, - MarkdownDirective, - MarkdownInlinePipe, - MarkdownPipe, - ModalDialogComponent, - ModalDirective, - ModalPlacementDirective, - MonthPipe, - PagerComponent, - ParentLinkDirective, - ProgressBarComponent, - RadioGroupComponent, - ReactiveFormsModule, - ResizedDirective, - RootViewComponent, - SafeHtmlPipe, - SafeResourceUrlPipe, - SafeUrlPipe, - ScrollActiveDirective, - ShortcutComponent, - ShortcutDirective, - ShortDatePipe, - ShortTimePipe, - SidebarMenuDirective, - StarsComponent, - StatusIconComponent, - StopClickDirective, - StopDragDirective, - StringColorPipe, - SyncScollingDirective, - SyncWidthDirective, - TabRouterlinkDirective, - TagEditorComponent, - TemplateWrapperDirective, - TitleComponent, - ToggleComponent, - ToolbarComponent, - TooltipDirective, - TourStepDirective, - TourTemplateComponent, - TransformInputDirective, - TranslatePipe, - VideoPlayerComponent, - ], -}) -export class SqxFrameworkModule { - constructor(injector: Injector) { - injector.get(AnalyticsService); - } - - public static forRoot(): ModuleWithProviders<SqxFrameworkModule> { - return { - ngModule: SqxFrameworkModule, - providers: [ - AnalyticsService, - CanDeactivateGuard, - ClipboardService, - DialogService, - LoadingService, - LocalStoreService, - MessageBus, - ResizeService, - ResourceLoaderService, - ShortcutService, - TempService, - TourService, - TitleService, - { - provide: BaseTourService, - useClass: TourService, - }, - { - provide: ErrorHandler, - useClass: GlobalErrorHandler, - multi: false, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: LoadingInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: CachingInterceptor, - multi: true, - }, - ], - }; - } -} diff --git a/frontend/src/app/framework/services/analytics.service.ts b/frontend/src/app/framework/services/analytics.service.ts index b334443f1..a520418a9 100644 --- a/frontend/src/app/framework/services/analytics.service.ts +++ b/frontend/src/app/framework/services/analytics.service.ts @@ -7,12 +7,14 @@ import { Injectable } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; -import { Types } from './../internal'; +import { Types } from '../internal'; type TrackEvent = (name: string, properties: any) => void; type TrackPage = (url: string) => void; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AnalyticsService { private readonly globalTrackEvent?: TrackEvent; private readonly globalTrackPage?: TrackPage; diff --git a/frontend/src/app/framework/services/clipboard.service.ts b/frontend/src/app/framework/services/clipboard.service.ts index 1d9e5739c..617424d2c 100644 --- a/frontend/src/app/framework/services/clipboard.service.ts +++ b/frontend/src/app/framework/services/clipboard.service.ts @@ -8,7 +8,9 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ClipboardService { private readonly text$ = new BehaviorSubject<string>(''); diff --git a/frontend/src/app/framework/services/dialog.service.ts b/frontend/src/app/framework/services/dialog.service.ts index 198f66a00..cb330a751 100644 --- a/frontend/src/app/framework/services/dialog.service.ts +++ b/frontend/src/app/framework/services/dialog.service.ts @@ -9,9 +9,9 @@ import { Injectable } from '@angular/core'; import { Observable, ReplaySubject, Subject, throwError } from 'rxjs'; -import { ErrorDto } from './../utils/error'; -import { FloatingPlacement } from './../utils/modal-view'; -import { Types } from './../utils/types'; +import { ErrorDto } from '../utils/error'; +import { FloatingPlacement } from '../utils/modal-view'; +import { Types } from '../utils/types'; import { LocalStoreService } from './local-store.service'; export class DialogRequest { @@ -87,7 +87,9 @@ export class Notification { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class DialogService { private readonly requestStream$ = new Subject<DialogRequest>(); private readonly notificationsStream$ = new Subject<Notification>(); diff --git a/frontend/src/app/framework/services/loading.service.ts b/frontend/src/app/framework/services/loading.service.ts index 9cfd9b4ae..528fe8212 100644 --- a/frontend/src/app/framework/services/loading.service.ts +++ b/frontend/src/app/framework/services/loading.service.ts @@ -8,9 +8,11 @@ import { Injectable, OnDestroy } from '@angular/core'; import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; import { BehaviorSubject, map, Observable, Subscription } from 'rxjs'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class LoadingService implements OnDestroy { private readonly routerSubscription: Subscription; private readonly loading$ = new BehaviorSubject(0); diff --git a/frontend/src/app/framework/services/local-store.service.ts b/frontend/src/app/framework/services/local-store.service.ts index 4345eae1b..776a90296 100644 --- a/frontend/src/app/framework/services/local-store.service.ts +++ b/frontend/src/app/framework/services/local-store.service.ts @@ -6,9 +6,11 @@ */ import { Injectable } from '@angular/core'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class LocalStoreService { private readonly fallback: { [key: string]: string } = {}; private store = localStorage; diff --git a/frontend/src/app/framework/services/localizer.service.ts b/frontend/src/app/framework/services/localizer.service.ts index de6c3ba78..8030f79d1 100644 --- a/frontend/src/app/framework/services/localizer.service.ts +++ b/frontend/src/app/framework/services/localizer.service.ts @@ -6,7 +6,7 @@ */ import { Injectable } from '@angular/core'; -import { compareStrings } from './../utils/array-helper'; +import { compareStrings } from '../utils/array-helper'; @Injectable() export class LocalizerService { diff --git a/frontend/src/app/framework/services/message-bus.service.ts b/frontend/src/app/framework/services/message-bus.service.ts index a6d8d3c93..dffe0172f 100644 --- a/frontend/src/app/framework/services/message-bus.service.ts +++ b/frontend/src/app/framework/services/message-bus.service.ts @@ -16,7 +16,9 @@ interface Message { data: any; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class MessageBus { private message$ = new Subject<Message>(); diff --git a/frontend/src/app/framework/services/resize.service.ts b/frontend/src/app/framework/services/resize.service.ts index 069355c3c..5e4edc35c 100644 --- a/frontend/src/app/framework/services/resize.service.ts +++ b/frontend/src/app/framework/services/resize.service.ts @@ -11,7 +11,9 @@ export interface ResizeListener { onResize(rect: DOMRect, element: Element): void; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ResizeService implements OnDestroy { private readonly listeners = new WeakMap<Element, ResizeListener>(); private observer?: ResizeObserver; diff --git a/frontend/src/app/framework/services/resource-loader.service.ts b/frontend/src/app/framework/services/resource-loader.service.ts index 6388c069f..d3de1b1ac 100644 --- a/frontend/src/app/framework/services/resource-loader.service.ts +++ b/frontend/src/app/framework/services/resource-loader.service.ts @@ -7,7 +7,9 @@ import { Injectable, Renderer2, RendererFactory2 } from '@angular/core'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ResourceLoaderService { private readonly cache: { [path: string]: Promise<any> } = {}; private readonly renderer: Renderer2; diff --git a/frontend/src/app/framework/services/shortcut.service.ts b/frontend/src/app/framework/services/shortcut.service.ts index 2d2944f8f..21f2dddb1 100644 --- a/frontend/src/app/framework/services/shortcut.service.ts +++ b/frontend/src/app/framework/services/shortcut.service.ts @@ -8,7 +8,9 @@ import { Injectable } from '@angular/core'; import * as Mousetrap from 'mousetrap'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ShortcutService { public listen(keys: string, callback: (e: KeyboardEvent, combo: string) => void): () => void { const trimmed = keys.toLowerCase().replace(/\s/g, '').split(','); diff --git a/frontend/src/app/framework/services/temp.service.ts b/frontend/src/app/framework/services/temp.service.ts index 8987cd5bc..b77938127 100644 --- a/frontend/src/app/framework/services/temp.service.ts +++ b/frontend/src/app/framework/services/temp.service.ts @@ -7,7 +7,9 @@ import { Injectable } from '@angular/core'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TempService { private value: any = null; diff --git a/frontend/src/app/framework/services/title.service.ts b/frontend/src/app/framework/services/title.service.ts index bf704f094..526b213b6 100644 --- a/frontend/src/app/framework/services/title.service.ts +++ b/frontend/src/app/framework/services/title.service.ts @@ -7,7 +7,7 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; import { LocalizerService } from './localizer.service'; export class TitlesConfig { @@ -21,7 +21,9 @@ export class TitlesConfig { export type Title = { route?: any; localized: string; value: string }; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TitleService { private readonly path$ = new BehaviorSubject<ReadonlyArray<Title>>([]); diff --git a/frontend/src/app/framework/services/toolbar.service.ts b/frontend/src/app/framework/services/toolbar.service.ts index 79f30ac2c..5a274f46f 100644 --- a/frontend/src/app/framework/services/toolbar.service.ts +++ b/frontend/src/app/framework/services/toolbar.service.ts @@ -7,7 +7,7 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; -import { Types } from './../utils/types'; +import { Types } from '../utils/types'; export type ButtonItem = { owner: any; name: string; method: () => void } & ButtonOptions; export type ButtonOptions = { disabled?: boolean; color?: string }; diff --git a/frontend/src/app/framework/utils/date-time.spec.ts b/frontend/src/app/framework/utils/date-time.spec.ts index e69e57bda..dfabb261b 100644 --- a/frontend/src/app/framework/utils/date-time.spec.ts +++ b/frontend/src/app/framework/utils/date-time.spec.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { DateHelper } from './..'; +import { DateHelper } from '..'; import { DateTime } from './date-time'; describe('DateTime', () => { diff --git a/frontend/src/app/framework/utils/error.spec.ts b/frontend/src/app/framework/utils/error.spec.ts index 4fa493f2e..ee68c7b77 100644 --- a/frontend/src/app/framework/utils/error.spec.ts +++ b/frontend/src/app/framework/utils/error.spec.ts @@ -6,7 +6,7 @@ */ import { IMock, It, Mock } from 'typemoq'; -import { LocalizerService } from './../services/localizer.service'; +import { LocalizerService } from '../services/localizer.service'; import { ErrorDto } from './error'; describe('ErrorDto', () => { diff --git a/frontend/src/app/framework/utils/error.ts b/frontend/src/app/framework/utils/error.ts index 770c71e6b..a79bb4796 100644 --- a/frontend/src/app/framework/utils/error.ts +++ b/frontend/src/app/framework/utils/error.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { LocalizerService } from './../services/localizer.service'; +import { LocalizerService } from '../services/localizer.service'; import { StringHelper } from './string-helper'; import { Types } from './types'; diff --git a/frontend/src/app/framework/utils/rxjs-extensions.ts b/frontend/src/app/framework/utils/rxjs-extensions.ts index 0c2ddb7fb..fcb1fa840 100644 --- a/frontend/src/app/framework/utils/rxjs-extensions.ts +++ b/frontend/src/app/framework/utils/rxjs-extensions.ts @@ -7,7 +7,7 @@ import { EMPTY, Observable, of, onErrorResumeNextWith, ReplaySubject, throwError } from 'rxjs'; import { catchError, debounceTime, distinctUntilChanged, filter, map, share, switchMap, tap } from 'rxjs/operators'; -import { DialogService } from './../services/dialog.service'; +import { DialogService } from '../services/dialog.service'; import { Version, versioned, Versioned } from './version'; export function mapVersioned<T = any, R = any>(project: (value: T, version: Version) => R) { diff --git a/frontend/src/app/framework/utils/tag-values.spec.ts b/frontend/src/app/framework/utils/tag-values.spec.ts index d860c9814..337152372 100644 --- a/frontend/src/app/framework/utils/tag-values.spec.ts +++ b/frontend/src/app/framework/utils/tag-values.spec.ts @@ -6,8 +6,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import './array-extensions'; import { FloatConverter, getTagValues, IntConverter, StringConverter, TagValue } from './tag-values'; +import './array-extensions'; describe('TagValue', () => { it('should create with value and search string', () => { diff --git a/frontend/src/app/framework/utils/text-measurer.ts b/frontend/src/app/framework/utils/text-measurer.ts index ffc493bd9..5209b26f1 100644 --- a/frontend/src/app/framework/utils/text-measurer.ts +++ b/frontend/src/app/framework/utils/text-measurer.ts @@ -6,7 +6,7 @@ */ import { ElementRef } from '@angular/core'; -import { Types } from './../internal'; +import { Types } from './types'; let CANVAS: HTMLCanvasElement | null = null; diff --git a/frontend/src/app/shared/components/app-form.component.ts b/frontend/src/app/shared/components/app-form.component.ts index 08cae0442..2c7528c14 100644 --- a/frontend/src/app/shared/components/app-form.component.ts +++ b/frontend/src/app/shared/components/app-form.component.ts @@ -5,14 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { ApiUrlConfig, AppsState, CreateAppForm, TemplateDto } from '@app/shared/internal'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ApiUrlConfig, ControlErrorsComponent, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, ModalDialogComponent, TooltipDirective, TransformInputDirective, TranslatePipe } from '@app/framework'; +import { AppsState, CreateAppForm, TemplateDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-app-form', styleUrls: ['./app-form.component.scss'], templateUrl: './app-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FocusOnInitDirective, + FormAlertComponent, + FormErrorComponent, + FormHintComponent, + FormsModule, + ModalDialogComponent, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TransformInputDirective, + TranslatePipe, + ], }) export class AppFormComponent { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-dialog.component.ts b/frontend/src/app/shared/components/assets/asset-dialog.component.ts index 3d75f190a..f3cfa5965 100644 --- a/frontend/src/app/shared/components/assets/asset-dialog.component.ts +++ b/frontend/src/app/shared/components/assets/asset-dialog.component.ts @@ -5,20 +5,60 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { NgxDocViewerModule } from 'ngx-doc-viewer'; import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AnnotateAssetDto, AnnotateAssetForm, AppsState, AssetDto, AssetsState, AssetUploaderState, AuthService, DialogService, MoveAssetForm, switchMapCached, Types, UploadCanceled } from '@app/shared/internal'; -import { AssetsService, MoveAssetItemDto } from '@app/shared/services/assets.service'; -import { AssetPathItem, ROOT_ITEM } from '@app/shared/state/assets.state'; +import { ConfirmClickDirective, ControlErrorsComponent, CopyDirective, DialogService, FormErrorComponent, FormHintComponent, ModalDialogComponent, ProgressBarComponent, switchMapCached, TagEditorComponent, TooltipDirective, TransformInputDirective, TranslatePipe, Types, VideoPlayerComponent } from '@app/framework'; +import { AnnotateAssetDto, AnnotateAssetForm, AppsState, AssetDto, AssetPathItem, AssetsService, AssetsState, AssetUploaderState, AuthService, MoveAssetForm, MoveAssetItemDto, ROOT_ITEM, UploadCanceled } from '@app/shared/internal'; +import { AssetFolderDropdownComponent } from './asset-folder-dropdown.component'; +import { AssetHistoryComponent } from './asset-history.component'; +import { AssetPathComponent } from './asset-path.component'; import { AssetTextEditorComponent } from './asset-text-editor.component'; import { ImageCropperComponent } from './image-cropper.component'; import { ImageFocusPointComponent } from './image-focus-point.component'; +import { AssetPreviewUrlPipe, AssetUrlPipe, PreviewableType } from './pipes'; @Component({ + standalone: true, selector: 'sqx-asset-dialog', styleUrls: ['./asset-dialog.component.scss'], templateUrl: './asset-dialog.component.html', + imports: [ + AssetFolderDropdownComponent, + AssetHistoryComponent, + AssetPathComponent, + AssetPreviewUrlPipe, + AssetTextEditorComponent, + AssetUrlPipe, + AsyncPipe, + ConfirmClickDirective, + ControlErrorsComponent, + CopyDirective, + FormErrorComponent, + FormHintComponent, + FormsModule, + ImageCropperComponent, + ImageFocusPointComponent, + ModalDialogComponent, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + NgxDocViewerModule, + PreviewableType, + ProgressBarComponent, + ReactiveFormsModule, + RouterLink, + TagEditorComponent, + TooltipDirective, + TransformInputDirective, + TranslatePipe, + VideoPlayerComponent, + ], }) export class AssetDialogComponent implements OnInit { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-folder-dialog.component.ts b/frontend/src/app/shared/components/assets/asset-folder-dialog.component.ts index 2e30086db..8c50113ad 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dialog.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder-dialog.component.ts @@ -5,13 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ControlErrorsComponent, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, ModalDialogComponent, TooltipDirective, TranslatePipe } from '@app/framework'; import { AssetFolderDto, AssetsState, RenameAssetFolderForm } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-folder-dialog', styleUrls: ['./asset-folder-dialog.component.scss'], templateUrl: './asset-folder-dialog.component.html', + imports: [ + AsyncPipe, + ControlErrorsComponent, + FocusOnInitDirective, + FormAlertComponent, + FormErrorComponent, + FormsModule, + ModalDialogComponent, + NgIf, + ReactiveFormsModule, + TooltipDirective, + TranslatePipe, + ], }) export class AssetFolderDialogComponent implements OnInit { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts index c3fbd4304..b92e1c63c 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectorRef, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; +import { LoaderComponent, StopClickDirective, TranslatePipe } from '@app/framework'; import { AssetsService } from '@app/shared/internal'; import { AssetFolderDropdowNode } from './asset-folder-dropdown.state'; @Component({ + standalone: true, selector: 'sqx-asset-folder-dropdown-item', styleUrls: ['./asset-folder-dropdown-item.component.scss'], templateUrl: './asset-folder-dropdown-item.component.html', + imports: [ + LoaderComponent, + NgFor, + NgIf, + StopClickDirective, + TranslatePipe, + ], }) export class AssetFolderDropdownItemComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts index 798206f4e..c7da5deed 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts @@ -5,10 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, Component, forwardRef, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { ModalModel, StatefulControlComponent, Types } from '@app/framework'; +import { ModalDirective, ModalModel, ModalPlacementDirective, StatefulControlComponent, TranslatePipe, Types } from '@app/framework'; import { AppsState, AssetsService, ROOT_ITEM } from '@app/shared/internal'; +import { AssetFolderDropdownItemComponent } from './asset-folder-dropdown-item.component'; import { AssetFolderDropdowNode } from './asset-folder-dropdown.state'; export const SQX_ASSETS_FOLDER_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { @@ -16,12 +18,20 @@ export const SQX_ASSETS_FOLDER_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { }; @Component({ + standalone: true, selector: 'sqx-asset-folder-dropdown', styleUrls: ['./asset-folder-dropdown.component.scss'], templateUrl: './asset-folder-dropdown.component.html', providers: [ SQX_ASSETS_FOLDER_DROPDOWN_CONTROL_VALUE_ACCESSOR, ], + imports: [ + AssetFolderDropdownItemComponent, + ModalDirective, + ModalPlacementDirective, + NgIf, + TranslatePipe, + ], }) export class AssetFolderDropdownComponent extends StatefulControlComponent<any, string> { @Input({ transform: booleanAttribute }) diff --git a/frontend/src/app/shared/components/assets/asset-folder.component.ts b/frontend/src/app/shared/components/assets/asset-folder.component.ts index 1199ee39d..7a1d0269a 100644 --- a/frontend/src/app/shared/components/assets/asset-folder.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder.component.ts @@ -5,14 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { ConfirmClickDirective, DropdownMenuComponent, ModalDirective, ModalPlacementDirective, TooltipDirective, TranslatePipe } from '@app/framework'; import { AssetFolderDto, AssetPathItem, DialogModel, ModalModel, Types } from '@app/shared/internal'; +import { AssetFolderDialogComponent } from './asset-folder-dialog.component'; @Component({ + standalone: true, selector: 'sqx-asset-folder', styleUrls: ['./asset-folder.component.scss'], templateUrl: './asset-folder.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetFolderDialogComponent, + ConfirmClickDirective, + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class AssetFolderComponent { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-history.component.ts b/frontend/src/app/shared/components/assets/asset-history.component.ts index e6347be90..a1acce257 100644 --- a/frontend/src/app/shared/components/assets/asset-history.component.ts +++ b/frontend/src/app/shared/components/assets/asset-history.component.ts @@ -5,17 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { ExternalLinkDirective, FromNowPipe, TooltipDirective, TranslatePipe } from '@app/framework'; import { AppsState, AssetDto, HistoryEventDto, HistoryService } from '@app/shared/internal'; +import { HistoryMessagePipe } from '../history/pipes'; +import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; +import { AssetUrlPipe } from './pipes'; interface AssetEvent { event: HistoryEventDto; version: number; canDownload: boolean } @Component({ + standalone: true, selector: 'sqx-asset-history', styleUrls: ['./asset-history.component.scss'], templateUrl: './asset-history.component.html', + imports: [ + AssetUrlPipe, + AsyncPipe, + ExternalLinkDirective, + FromNowPipe, + HistoryMessagePipe, + NgFor, + NgIf, + TooltipDirective, + TranslatePipe, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class AssetHistoryComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/assets/asset-path.component.ts b/frontend/src/app/shared/components/assets/asset-path.component.ts index 1a064d363..475614e0f 100644 --- a/frontend/src/app/shared/components/assets/asset-path.component.ts +++ b/frontend/src/app/shared/components/assets/asset-path.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { TranslatePipe } from '@app/framework'; import { AssetPathItem } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-path', styleUrls: ['./asset-path.component.scss'], templateUrl: './asset-path.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + TranslatePipe, + ], }) export class AssetPathComponent { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-selector.component.ts b/frontend/src/app/shared/components/assets/asset-selector.component.ts index 6649acbbe..dd27dcf9c 100644 --- a/frontend/src/app/shared/components/assets/asset-selector.component.ts +++ b/frontend/src/app/shared/components/assets/asset-selector.component.ts @@ -5,8 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ListViewComponent, ModalDialogComponent, PagerComponent, TagEditorComponent, TooltipDirective, TranslatePipe } from '@app/framework'; import { AssetDto, ComponentAssetsState, LocalStoreService, Query, Settings, StatefulComponent } from '@app/shared/internal'; +import { SearchFormComponent } from '../search/search-form.component'; +import { AssetsListComponent } from './assets-list.component'; interface State { // The selected assets. @@ -20,6 +25,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-asset-selector', styleUrls: ['./asset-selector.component.scss'], templateUrl: './asset-selector.component.html', @@ -27,6 +33,18 @@ interface State { ComponentAssetsState, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetsListComponent, + AsyncPipe, + FormsModule, + ListViewComponent, + ModalDialogComponent, + PagerComponent, + SearchFormComponent, + TagEditorComponent, + TooltipDirective, + TranslatePipe, + ], }) export class AssetSelectorComponent extends StatefulComponent<State> implements OnInit { @Output() diff --git a/frontend/src/app/shared/components/assets/asset-text-editor.component.ts b/frontend/src/app/shared/components/assets/asset-text-editor.component.ts index 84d16b8d4..89a766c50 100644 --- a/frontend/src/app/shared/components/assets/asset-text-editor.component.ts +++ b/frontend/src/app/shared/components/assets/asset-text-editor.component.ts @@ -7,12 +7,19 @@ import { HttpClient } from '@angular/common/http'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { CodeEditorComponent } from '@app/framework'; @Component({ + standalone: true, selector: 'sqx-asset-text-editor', styleUrls: ['./asset-text-editor.component.scss'], templateUrl: './asset-text-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CodeEditorComponent, + FormsModule, + ], }) export class AssetTextEditorComponent implements OnInit { @Input() diff --git a/frontend/src/app/shared/components/assets/asset-uploader.component.ts b/frontend/src/app/shared/components/assets/asset-uploader.component.ts index 187fba08c..93ba9ccb4 100644 --- a/frontend/src/app/shared/components/assets/asset-uploader.component.ts +++ b/frontend/src/app/shared/components/assets/asset-uploader.component.ts @@ -5,14 +5,30 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { DropdownMenuComponent, FileDropDirective, ModalDirective, ProgressBarComponent, TranslatePipe } from '@app/framework'; import { AppsState, AssetsState, AssetUploaderState, ModalModel, Types, Upload } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-uploader', styleUrls: ['./asset-uploader.component.scss'], templateUrl: './asset-uploader.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + DropdownMenuComponent, + FileDropDirective, + ModalDirective, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + ProgressBarComponent, + TranslatePipe, + ], }) export class AssetUploaderComponent { public modalMenu = new ModalModel(); diff --git a/frontend/src/app/shared/components/assets/asset.component.ts b/frontend/src/app/shared/components/assets/asset.component.ts index b62cc5c6e..c350d58aa 100644 --- a/frontend/src/app/shared/components/assets/asset.component.ts +++ b/frontend/src/app/shared/components/assets/asset.component.ts @@ -5,8 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnInit, Output } from '@angular/core'; +import { ConfirmClickDirective, ExternalLinkDirective, FileDropDirective, FromNowPipe, ImageSourceDirective, ProgressBarComponent, StopClickDirective, TooltipDirective, TranslatePipe } from '@app/framework'; import { AssetDto, AssetUploaderState, DialogService, StatefulComponent, Types, UploadCanceled } from '@app/shared/internal'; +import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; +import { AssetPreviewUrlPipe, AssetUrlPipe, FileIconPipe } from './pipes'; interface State { // The download progress. @@ -14,10 +18,29 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-asset', styleUrls: ['./asset.component.scss'], templateUrl: './asset.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetPreviewUrlPipe, + AssetUrlPipe, + ConfirmClickDirective, + ExternalLinkDirective, + FileDropDirective, + FileIconPipe, + FromNowPipe, + ImageSourceDirective, + NgFor, + NgIf, + ProgressBarComponent, + StopClickDirective, + TooltipDirective, + TranslatePipe, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class AssetComponent extends StatefulComponent<State> implements OnInit { @Output() diff --git a/frontend/src/app/shared/components/assets/assets-list.component.ts b/frontend/src/app/shared/components/assets/assets-list.component.ts index 5bd09240d..6e0f12e78 100644 --- a/frontend/src/app/shared/components/assets/assets-list.component.ts +++ b/frontend/src/app/shared/components/assets/assets-list.component.ts @@ -5,9 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDropList, CdkDropListGroup } from '@angular/cdk/drag-drop'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FileDropDirective, TourStepDirective, TranslatePipe } from '@app/framework'; import { AssetDto, AssetFolderDto, AssetsState, getFiles, StatefulComponent, Types } from '@app/shared/internal'; +import { AssetFolderComponent } from './asset-folder.component'; +import { AssetComponent } from './asset.component'; interface State { // The new files. @@ -15,10 +19,24 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-assets-list', styleUrls: ['./assets-list.component.scss'], templateUrl: './assets-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetComponent, + AssetFolderComponent, + AsyncPipe, + CdkDrag, + CdkDropList, + CdkDropListGroup, + FileDropDirective, + NgFor, + NgIf, + TourStepDirective, + TranslatePipe, + ], }) export class AssetsListComponent extends StatefulComponent<State> { @Output() diff --git a/frontend/src/app/shared/components/assets/image-cropper.component.ts b/frontend/src/app/shared/components/assets/image-cropper.component.ts index 6c89d3fd4..900bb543d 100644 --- a/frontend/src/app/shared/components/assets/image-cropper.component.ts +++ b/frontend/src/app/shared/components/assets/image-cropper.component.ts @@ -7,13 +7,17 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; import Cropper from 'cropperjs'; -import { Types } from '@app/framework'; +import { TooltipDirective, Types } from '@app/framework'; @Component({ + standalone: true, selector: 'sqx-image-editor', styleUrls: ['./image-cropper.component.scss'], templateUrl: './image-cropper.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TooltipDirective, + ], }) export class ImageCropperComponent implements AfterViewInit, OnDestroy { private cropper: Cropper | null = null; diff --git a/frontend/src/app/shared/components/assets/image-focus-point.component.ts b/frontend/src/app/shared/components/assets/image-focus-point.component.ts index d7ebadff2..d95ac9c46 100644 --- a/frontend/src/app/shared/components/assets/image-focus-point.component.ts +++ b/frontend/src/app/shared/components/assets/image-focus-point.component.ts @@ -7,14 +7,18 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core'; import { FocusedImage, FocusPicker } from 'image-focus'; -import { Types } from '@app/framework'; -import { AnnotateAssetDto, AssetDto } from '@app/shared/services/assets.service'; +import { TranslatePipe, Types } from '@app/framework'; +import { AnnotateAssetDto, AssetDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-image-focus-point', styleUrls: ['./image-focus-point.component.scss'], templateUrl: './image-focus-point.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TranslatePipe, + ], }) export class ImageFocusPointComponent implements AfterViewInit, OnDestroy { private readonly previewImages: FocusedImage[] = []; diff --git a/frontend/src/app/shared/components/assets/pipes.ts b/frontend/src/app/shared/components/assets/pipes.ts index 4f6fbe667..dc1b1fc11 100644 --- a/frontend/src/app/shared/components/assets/pipes.ts +++ b/frontend/src/app/shared/components/assets/pipes.ts @@ -12,6 +12,7 @@ import { ApiUrlConfig, AssetDto, AuthService, MathHelper, StringHelper, Types } @Pipe({ name: 'sqxAssetUrl', pure: true, + standalone: true, }) export class AssetUrlPipe implements PipeTransform { constructor( @@ -39,6 +40,7 @@ export class AssetUrlPipe implements PipeTransform { @Pipe({ name: 'sqxAssetPreviewUrl', pure: true, + standalone: true, }) export class AssetPreviewUrlPipe implements PipeTransform { constructor( @@ -59,6 +61,7 @@ export class AssetPreviewUrlPipe implements PipeTransform { @Pipe({ name: 'sqxFileIcon', pure: true, + standalone: true, }) export class FileIconPipe implements PipeTransform { public transform(asset: { mimeType: string; fileType: string }): string { @@ -79,6 +82,7 @@ export class FileIconPipe implements PipeTransform { @Pipe({ name: 'sqxPreviewable', pure: true, + standalone: true, }) export class PreviewableType implements PipeTransform { public transform(asset: { fileSize: number; fileType: string }): boolean { diff --git a/frontend/src/app/shared/components/cards/api-calls-card.component.ts b/frontend/src/app/shared/components/cards/api-calls-card.component.ts index 4aee8e89c..57c0e608e 100644 --- a/frontend/src/app/shared/components/cards/api-calls-card.component.ts +++ b/frontend/src/app/shared/components/cards/api-calls-card.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { NgChartsModule } from 'ng2-charts'; +import { TranslatePipe } from '@app/framework'; import { AppDto, CallsUsageDto, ChartHelpers, ChartOptions, UsagesService } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-api-calls-card', styleUrls: ['./api-calls-card.component.scss'], templateUrl: './api-calls-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgChartsModule, + NgIf, + TranslatePipe, + ], }) export class ApiCallsCardComponent { @Input() diff --git a/frontend/src/app/shared/components/cards/api-calls-summary-card.component.ts b/frontend/src/app/shared/components/cards/api-calls-summary-card.component.ts index a9e3c84a6..590f9ad5a 100644 --- a/frontend/src/app/shared/components/cards/api-calls-summary-card.component.ts +++ b/frontend/src/app/shared/components/cards/api-calls-summary-card.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { CallsUsageDto } from '@app/shared/internal'; +import { KNumberPipe, TranslatePipe } from '@app/framework'; +import { CallsUsageDto } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-api-calls-summary-card', styleUrls: ['./api-calls-summary-card.component.scss'], templateUrl: './api-calls-summary-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + KNumberPipe, + NgIf, + TranslatePipe, + ], }) export class ApiCallsSummaryCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/api-performance-card.component.ts b/frontend/src/app/shared/components/cards/api-performance-card.component.ts index df1e99036..3e24139e3 100644 --- a/frontend/src/app/shared/components/cards/api-performance-card.component.ts +++ b/frontend/src/app/shared/components/cards/api-performance-card.component.ts @@ -6,13 +6,22 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { CallsUsageDto, ChartHelpers, ChartOptions } from '@app/shared/internal'; +import { FormsModule } from '@angular/forms'; +import { NgChartsModule } from 'ng2-charts'; +import { TranslatePipe } from '@app/framework'; +import { CallsUsageDto, ChartHelpers, ChartOptions } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-api-performance-card', styleUrls: ['./api-performance-card.component.scss'], templateUrl: './api-performance-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + NgChartsModule, + TranslatePipe, + ], }) export class ApiPerformanceCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/api-traffic-card.component.ts b/frontend/src/app/shared/components/cards/api-traffic-card.component.ts index f09a108b9..58fbcabb7 100644 --- a/frontend/src/app/shared/components/cards/api-traffic-card.component.ts +++ b/frontend/src/app/shared/components/cards/api-traffic-card.component.ts @@ -6,13 +6,23 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { NgChartsModule } from 'ng2-charts'; +import { FileSizePipe, TranslatePipe } from '@app/framework'; import { CallsUsageDto, ChartHelpers, ChartOptions, TypedSimpleChanges } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-api-traffic-card', styleUrls: ['./api-traffic-card.component.scss'], templateUrl: './api-traffic-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FileSizePipe, + FormsModule, + NgChartsModule, + TranslatePipe, + ], }) export class ApiTrafficCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/api-traffic-summary-card.component.ts b/frontend/src/app/shared/components/cards/api-traffic-summary-card.component.ts index ac5e2d586..e22968400 100644 --- a/frontend/src/app/shared/components/cards/api-traffic-summary-card.component.ts +++ b/frontend/src/app/shared/components/cards/api-traffic-summary-card.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { CallsUsageDto } from '@app/shared/internal'; +import { TranslatePipe } from '@app/framework'; +import { CallsUsageDto, FileSizePipe } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-api-traffic-summary-card', styleUrls: ['./api-traffic-summary-card.component.scss'], templateUrl: './api-traffic-summary-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FileSizePipe, + NgIf, + TranslatePipe, + ], }) export class ApiTrafficSummaryCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/asset-uploads-count-card.component.ts b/frontend/src/app/shared/components/cards/asset-uploads-count-card.component.ts index 11cb9086f..4b544d4dc 100644 --- a/frontend/src/app/shared/components/cards/asset-uploads-count-card.component.ts +++ b/frontend/src/app/shared/components/cards/asset-uploads-count-card.component.ts @@ -6,13 +6,20 @@ */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { NgChartsModule } from 'ng2-charts'; +import { TranslatePipe } from '@app/framework'; import { ChartHelpers, ChartOptions, StorageUsagePerDateDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-uploads-count-card', styleUrls: ['./asset-uploads-count-card.component.scss'], templateUrl: './asset-uploads-count-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgChartsModule, + TranslatePipe, + ], }) export class AssetUploadsCountCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/asset-uploads-size-card.component.ts b/frontend/src/app/shared/components/cards/asset-uploads-size-card.component.ts index c22389d06..297ab7b43 100644 --- a/frontend/src/app/shared/components/cards/asset-uploads-size-card.component.ts +++ b/frontend/src/app/shared/components/cards/asset-uploads-size-card.component.ts @@ -6,13 +6,20 @@ */ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { NgChartsModule } from 'ng2-charts'; +import { TranslatePipe } from '@app/framework'; import { ChartHelpers, ChartOptions, StorageUsagePerDateDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-uploads-size-card', styleUrls: ['./asset-uploads-size-card.component.scss'], templateUrl: './asset-uploads-size-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgChartsModule, + TranslatePipe, + ], }) export class AssetUploadsSizeCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/asset-uploads-size-summary-card.component.ts b/frontend/src/app/shared/components/cards/asset-uploads-size-summary-card.component.ts index 20d2a8745..91fd222ff 100644 --- a/frontend/src/app/shared/components/cards/asset-uploads-size-summary-card.component.ts +++ b/frontend/src/app/shared/components/cards/asset-uploads-size-summary-card.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FileSizePipe, TranslatePipe } from '@app/framework'; import { CurrentStorageDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-asset-uploads-size-summary-card', styleUrls: ['./asset-uploads-size-summary-card.component.scss'], templateUrl: './asset-uploads-size-summary-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FileSizePipe, + NgIf, + TranslatePipe, + ], }) export class AssetUploadsSizeSummaryCardComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/cards/iframe-card.component.ts b/frontend/src/app/shared/components/cards/iframe-card.component.ts index b1870e221..3ac19aa47 100644 --- a/frontend/src/app/shared/components/cards/iframe-card.component.ts +++ b/frontend/src/app/shared/components/cards/iframe-card.component.ts @@ -10,6 +10,7 @@ import { ApiUrlConfig, Types } from '@app/framework'; import { AppDto, AuthService, TeamDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-iframe-card', styleUrls: ['./iframe-card.component.scss'], templateUrl: './iframe-card.component.html', diff --git a/frontend/src/app/shared/components/cards/random-cat-card.component.ts b/frontend/src/app/shared/components/cards/random-cat-card.component.ts index c7f75df66..29ffb9806 100644 --- a/frontend/src/app/shared/components/cards/random-cat-card.component.ts +++ b/frontend/src/app/shared/components/cards/random-cat-card.component.ts @@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-random-cat-card', styleUrls: ['./random-cat-card.component.scss'], templateUrl: './random-cat-card.component.html', diff --git a/frontend/src/app/shared/components/cards/random-dog-card.component.ts b/frontend/src/app/shared/components/cards/random-dog-card.component.ts index 7e52675e3..0434d354e 100644 --- a/frontend/src/app/shared/components/cards/random-dog-card.component.ts +++ b/frontend/src/app/shared/components/cards/random-dog-card.component.ts @@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ + standalone: true, selector: 'sqx-random-dog-card', styleUrls: ['./random-dog-card.component.scss'], templateUrl: './random-dog-card.component.html', diff --git a/frontend/src/app/shared/components/cards/shared.ts b/frontend/src/app/shared/components/cards/shared.ts index 9feb4dc1b..404db6781 100644 --- a/frontend/src/app/shared/components/cards/shared.ts +++ b/frontend/src/app/shared/components/cards/shared.ts @@ -6,7 +6,7 @@ */ import { ChartOptions as IChartOptions } from 'chart.js'; -import { DateTime } from '@app/shared'; +import { DateTime } from '@app/framework'; const ColorSchema: ReadonlyArray<string> = [ ' 51, 137, 213', diff --git a/frontend/src/app/shared/components/cards/support-card.component.ts b/frontend/src/app/shared/components/cards/support-card.component.ts index bec7a352a..044fbd700 100644 --- a/frontend/src/app/shared/components/cards/support-card.component.ts +++ b/frontend/src/app/shared/components/cards/support-card.component.ts @@ -6,12 +6,18 @@ */ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ExternalLinkDirective, TranslatePipe } from '@app/framework'; @Component({ + standalone: true, selector: 'sqx-support-card', styleUrls: ['./support-card.component.scss'], templateUrl: './support-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ExternalLinkDirective, + TranslatePipe, + ], }) export class SupportCardComponent { } diff --git a/frontend/src/app/shared/components/chat-dialog.component.ts b/frontend/src/app/shared/components/chat-dialog.component.ts index eb368cd6b..91d00b34f 100644 --- a/frontend/src/app/shared/components/chat-dialog.component.ts +++ b/frontend/src/app/shared/components/chat-dialog.component.ts @@ -5,9 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { delay } from 'rxjs/operators'; +import { FocusOnInitDirective, ModalDialogComponent, ResizedDirective, ScrollActiveDirective, TooltipDirective, TranslatePipe } from '@app/framework'; import { AppsState, AuthService, StatefulComponent, TranslationsService } from '@app/shared/internal'; +import { UserIdPicturePipe } from './pipes'; interface State { // True, when running @@ -21,9 +25,22 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-chat-dialog', styleUrls: ['./chat-dialog.component.scss'], templateUrl: './chat-dialog.component.html', + imports: [ + FocusOnInitDirective, + FormsModule, + ModalDialogComponent, + NgFor, + NgIf, + ResizedDirective, + ScrollActiveDirective, + TooltipDirective, + TranslatePipe, + UserIdPicturePipe, + ], }) export class ChatDialogComponent extends StatefulComponent<State> { @Output() @@ -117,4 +134,4 @@ export class ChatDialogComponent extends StatefulComponent<State> { }, }); } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/comments/comment.component.ts b/frontend/src/app/shared/components/comments/comment.component.ts index 77e71ea88..58f64c81d 100644 --- a/frontend/src/app/shared/components/comments/comment.component.ts +++ b/frontend/src/app/shared/components/comments/comment.component.ts @@ -5,19 +5,40 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { MentionConfig } from 'angular-mentions'; +import { FormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { MentionConfig, MentionModule } from 'angular-mentions'; +import { ConfirmClickDirective, FocusOnInitDirective, FromNowPipe, MarkdownPipe, SafeHtmlPipe, TooltipDirective, TranslatePipe } from '@app/framework'; import { Comment, ContributorDto, DialogService, Keys, SharedArray, StatefulComponent } from '@app/shared/internal'; +import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; interface State { isEditing: boolean; } @Component({ + standalone: true, selector: 'sqx-comment', styleUrls: ['./comment.component.scss'], templateUrl: './comment.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ConfirmClickDirective, + FocusOnInitDirective, + FormsModule, + FromNowPipe, + MarkdownPipe, + MentionModule, + NgIf, + RouterLink, + SafeHtmlPipe, + TooltipDirective, + TranslatePipe, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class CommentComponent extends StatefulComponent<State> { @Input({ transform: booleanAttribute }) diff --git a/frontend/src/app/shared/components/comments/comments.component.ts b/frontend/src/app/shared/components/comments/comments.component.ts index b0cd8204c..6e5c081bf 100644 --- a/frontend/src/app/shared/components/comments/comments.component.ts +++ b/frontend/src/app/shared/components/comments/comments.component.ts @@ -5,17 +5,32 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, ElementRef, Input, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; -import { MentionConfig } from 'angular-mentions'; +import { MentionConfig, MentionModule } from 'angular-mentions'; import { Observable } from 'rxjs'; +import { ResizedDirective, TranslatePipe } from '@app/framework'; import { AuthService, CollaborationService, Comment, ContributorsState, SharedArray, UpsertCommentForm } from '@app/shared/internal'; import { CommentComponent } from './comment.component'; @Component({ + standalone: true, selector: 'sqx-comments', styleUrls: ['./comments.component.scss'], templateUrl: './comments.component.html', + imports: [ + AsyncPipe, + CommentComponent, + FormsModule, + MentionModule, + NgFor, + NgIf, + ReactiveFormsModule, + ResizedDirective, + TranslatePipe, + ], }) export class CommentsComponent { @ViewChild('scrollContainer', { static: false }) diff --git a/frontend/src/app/shared/components/contents/content-list-cell.directive.ts b/frontend/src/app/shared/components/contents/content-list-cell.directive.ts index 225ffa493..19e5e0f1e 100644 --- a/frontend/src/app/shared/components/contents/content-list-cell.directive.ts +++ b/frontend/src/app/shared/components/contents/content-list-cell.directive.ts @@ -49,6 +49,7 @@ export function getCellWidth(field: TableField, sizes: FieldSizes | undefined | @Pipe({ name: 'sqxContentsColumns', pure: true, + standalone: true, }) export class ContentsColumnsPipe implements PipeTransform { public transform(value: ReadonlyArray<ContentDto>) { @@ -64,6 +65,7 @@ export class ContentsColumnsPipe implements PipeTransform { @Directive({ selector: '[sqxContentListWidth]', + standalone: true, }) export class ContentListWidthDirective { private readonly subscriptions = new Subscriptions(); @@ -117,6 +119,7 @@ export class ContentListWidthDirective { @Directive({ selector: '[sqxContentListCell]', + standalone: true, }) export class ContentListCellDirective { private readonly subscriptions = new Subscriptions(); @@ -170,6 +173,7 @@ export class ContentListCellDirective { @Directive({ selector: '[sqxContentListCellResize][field][fields]', + standalone: true, }) export class ContentListCellResizeDirective implements OnInit, OnDestroy { private mouseMove?: Function; diff --git a/frontend/src/app/shared/components/contents/content-list-field.component.ts b/frontend/src/app/shared/components/contents/content-list-field.component.ts index 44bda3091..09ee24141 100644 --- a/frontend/src/app/shared/components/contents/content-list-field.component.ts +++ b/frontend/src/app/shared/components/contents/content-list-field.component.ts @@ -5,9 +5,16 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; +import { FromNowPipe, ShortDatePipe, TooltipDirective, TranslatePipe } from '@app/framework'; import { ContentDto, FieldValue, getContentValue, LanguageDto, META_FIELDS, SchemaDto, StatefulComponent, TableField, TableSettings } from '@app/shared/internal'; +import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; +import { ContentStatusComponent } from './content-status.component'; +import { ContentValueEditorComponent } from './content-value-editor.component'; +import { ContentValueComponent } from './content-value.component'; +import { TranslationStatusComponent } from './translation-status.component'; interface State { // The formatted value. @@ -15,10 +22,27 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-content-list-field', styleUrls: ['./content-list-field.component.scss'], templateUrl: './content-list-field.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ContentStatusComponent, + ContentValueComponent, + ContentValueEditorComponent, + FromNowPipe, + NgIf, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + ShortDatePipe, + TooltipDirective, + TranslatePipe, + TranslationStatusComponent, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class ContentListFieldComponent extends StatefulComponent<State> { public readonly metaFields = META_FIELDS; diff --git a/frontend/src/app/shared/components/contents/content-list-header.component.ts b/frontend/src/app/shared/components/contents/content-list-header.component.ts index 289018194..8abbe2f04 100644 --- a/frontend/src/app/shared/components/contents/content-list-header.component.ts +++ b/frontend/src/app/shared/components/contents/content-list-header.component.ts @@ -7,12 +7,17 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { LanguageDto, META_FIELDS, Query, SortMode, TableField } from '@app/shared/internal'; +import { TableHeaderComponent } from '../table-header.component'; @Component({ + standalone: true, selector: 'sqx-content-list-header', styleUrls: ['./content-list-header.component.scss'], templateUrl: './content-list-header.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TableHeaderComponent, + ], }) export class ContentListHeaderComponent { public readonly metaFields = META_FIELDS; diff --git a/frontend/src/app/shared/components/contents/content-status.component.ts b/frontend/src/app/shared/components/contents/content-status.component.ts index 8b36b3f5f..83cdfe7ee 100644 --- a/frontend/src/app/shared/components/contents/content-status.component.ts +++ b/frontend/src/app/shared/components/contents/content-status.component.ts @@ -5,15 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { LocalizerService, TypedSimpleChanges } from '@app/framework'; +import { FullDateTimePipe, LocalizerService, TooltipDirective, TranslatePipe, TypedSimpleChanges } from '@app/framework'; import { ScheduleDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-content-status', styleUrls: ['./content-status.component.scss'], templateUrl: './content-status.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FullDateTimePipe, + NgIf, + TooltipDirective, + TranslatePipe, + ], }) export class ContentStatusComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/contents/content-value-editor.component.ts b/frontend/src/app/shared/components/contents/content-value-editor.component.ts index f87bd2c63..9e5adcfac 100644 --- a/frontend/src/app/shared/components/contents/content-value-editor.component.ts +++ b/frontend/src/app/shared/components/contents/content-value-editor.component.ts @@ -5,15 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgSwitch, NgSwitchCase } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; +import { IndeterminateValueDirective, StarsComponent, ToggleComponent, TransformInputDirective } from '@app/framework'; import { FieldDto, MathHelper } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-content-value-editor', styleUrls: ['./content-value-editor.component.scss'], templateUrl: './content-value-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + IndeterminateValueDirective, + NgFor, + NgSwitch, + NgSwitchCase, + ReactiveFormsModule, + StarsComponent, + ToggleComponent, + TransformInputDirective, + ], }) export class ContentValueEditorComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/contents/content-value.component.ts b/frontend/src/app/shared/components/contents/content-value.component.ts index 4e9d6253a..8574aca02 100644 --- a/frontend/src/app/shared/components/contents/content-value.component.ts +++ b/frontend/src/app/shared/components/contents/content-value.component.ts @@ -5,8 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { ModalModel, StatefulComponent, Subscriptions, TypedSimpleChanges } from '@app/framework'; +import { NgIf } from '@angular/common'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { LongHoverDirective, ModalDirective, ModalModel, ModalPlacementDirective, StatefulComponent, StopClickDirective, Subscriptions, TooltipDirective, TypedSimpleChanges } from '@app/framework'; import { HtmlValue, TableField, TableSettings, Types } from '@app/shared/internal'; interface State { @@ -14,10 +15,19 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-content-value', styleUrls: ['./content-value.component.scss'], templateUrl: './content-value.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + LongHoverDirective, + ModalDirective, + ModalPlacementDirective, + NgIf, + StopClickDirective, + TooltipDirective, + ], }) export class ContentValueComponent extends StatefulComponent<State> { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/contents/translation-status.component.ts b/frontend/src/app/shared/components/contents/translation-status.component.ts index 3608a8521..dbb480334 100644 --- a/frontend/src/app/shared/components/contents/translation-status.component.ts +++ b/frontend/src/app/shared/components/contents/translation-status.component.ts @@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { contentTranslationStatus, LanguageDto, SchemaDto } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-translation-status', styleUrls: ['./translation-status.component.scss'], templateUrl: './translation-status.component.html', diff --git a/frontend/src/app/shared/components/cursors.component.ts b/frontend/src/app/shared/components/cursors.component.ts index 891397052..c46b464c3 100644 --- a/frontend/src/app/shared/components/cursors.component.ts +++ b/frontend/src/app/shared/components/cursors.component.ts @@ -1,4 +1,3 @@ - /* * Squidex Headless CMS * @@ -6,17 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { map } from 'rxjs'; +import { StringColorPipe } from '@app/framework'; import { CollaborationService, Profile } from '@app/shared/internal'; type CursorState = { user: Profile; cursor: { x: number; y: number } }; @Component({ + standalone: true, selector: 'sqx-cursors', styleUrls: ['./cursors.component.scss'], templateUrl: './cursors.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + NgFor, + StringColorPipe, + ], }) export class CursorsComponent { public otherCursor = diff --git a/frontend/src/app/shared/components/cursors.directive.ts b/frontend/src/app/shared/components/cursors.directive.ts index 48cf4198e..38b4d33c1 100644 --- a/frontend/src/app/shared/components/cursors.directive.ts +++ b/frontend/src/app/shared/components/cursors.directive.ts @@ -1,4 +1,3 @@ - /* * Squidex Headless CMS * @@ -11,6 +10,7 @@ import { CollaborationService, Subscriptions } from '@app/shared/internal'; @Directive({ selector: '[sqxCursors]', + standalone: true, }) export class CursorsDirective { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/focus-marker.component.ts b/frontend/src/app/shared/components/focus-marker.component.ts index 7e3be4085..296c9b78a 100644 --- a/frontend/src/app/shared/components/focus-marker.component.ts +++ b/frontend/src/app/shared/components/focus-marker.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component, Input, Optional, Renderer2 } from '@angular/core'; import { map } from 'rxjs'; +import { StringColorPipe } from '@app/framework'; import { CollaborationService, Subscriptions } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-focus-marker', styleUrls: ['./focus-marker.component.scss'], templateUrl: './focus-marker.component.html', + imports: [ + AsyncPipe, + NgIf, + StringColorPipe, + ], }) export class FocusMarkerComponent { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/forms/geolocation-editor.component.ts b/frontend/src/app/shared/components/forms/geolocation-editor.component.ts index a8a96c58e..ef15828df 100644 --- a/frontend/src/app/shared/components/forms/geolocation-editor.component.ts +++ b/frontend/src/app/shared/components/forms/geolocation-editor.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, forwardRef, inject, Input, ViewChild } from '@angular/core'; -import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { ControlErrorsComponent, ResizedDirective, TooltipDirective, TranslatePipe } from '@app/framework'; import { ExtendedFormGroup, LocalStoreService, ResourceLoaderService, Settings, StatefulControlComponent, Types, UIOptions, ValidatorsEx } from '@app/shared/internal'; declare const L: any; @@ -29,6 +31,7 @@ interface State { type UpdateOptions = { reset?: boolean; pan?: true; fire?: boolean }; @Component({ + standalone: true, selector: 'sqx-geolocation-editor', styleUrls: ['./geolocation-editor.component.scss'], templateUrl: './geolocation-editor.component.html', @@ -36,6 +39,15 @@ type UpdateOptions = { reset?: boolean; pan?: true; fire?: boolean }; SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ControlErrorsComponent, + FormsModule, + NgIf, + ReactiveFormsModule, + ResizedDirective, + TooltipDirective, + TranslatePipe, + ], }) export class GeolocationEditorComponent extends StatefulControlComponent<State, Geolocation> implements AfterViewInit { private readonly googleMapsKey = inject(UIOptions).value.map.googleMaps.key; diff --git a/frontend/src/app/shared/components/forms/rich-editor.component.ts b/frontend/src/app/shared/components/forms/rich-editor.component.ts index 4aee803c7..93cfdf0fe 100644 --- a/frontend/src/app/shared/components/forms/rich-editor.component.ts +++ b/frontend/src/app/shared/components/forms/rich-editor.component.ts @@ -5,17 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject, catchError, of, switchMap } from 'rxjs'; -import { ContentDto } from '@app/shared'; -import { ApiUrlConfig, AppsState, AssetDto, AssetsService, AssetUploaderState, DialogModel, getContentValue, LanguageDto, ResourceLoaderService, StatefulControlComponent, Types } from '@app/shared/internal'; +import { ModalDirective } from '@app/framework'; +import { ApiUrlConfig, AppsState, AssetDto, AssetsService, AssetUploaderState, ContentDto, DialogModel, getContentValue, LanguageDto, ResourceLoaderService, StatefulControlComponent, Types } from '@app/shared/internal'; +import { AssetDialogComponent } from '../assets/asset-dialog.component'; +import { AssetSelectorComponent } from '../assets/asset-selector.component'; +import { ChatDialogComponent } from '../chat-dialog.component'; +import { ContentSelectorComponent } from '../references/content-selector.component'; export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true, }; @Component({ + standalone: true, selector: 'sqx-rich-editor', styleUrls: ['./rich-editor.component.scss'], templateUrl: './rich-editor.component.html', @@ -23,6 +29,14 @@ export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AssetDialogComponent, + AssetSelectorComponent, + AsyncPipe, + ChatDialogComponent, + ContentSelectorComponent, + ModalDirective, + ], }) export class RichEditorComponent extends StatefulControlComponent<{}, string> implements AfterViewInit, OnDestroy { private readonly assetId = new BehaviorSubject<string | null>(null); @@ -269,4 +283,4 @@ class ResolvablePromise<T> { public resolve(value: T) { this.resolver?.(value); } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/help/help-markdown.pipe.ts b/frontend/src/app/shared/components/help/help-markdown.pipe.ts index a1a3a9694..a240e5c4a 100644 --- a/frontend/src/app/shared/components/help/help-markdown.pipe.ts +++ b/frontend/src/app/shared/components/help/help-markdown.pipe.ts @@ -21,6 +21,7 @@ renderer.link = (href, _, text) => { @Pipe({ name: 'sqxHelpMarkdown', pure: true, + standalone: true, }) export class HelpMarkdownPipe implements PipeTransform { public transform(text: string | undefined | null): string { diff --git a/frontend/src/app/shared/components/help/help.component.ts b/frontend/src/app/shared/components/help/help.component.ts index b6ac72442..25bc5da49 100644 --- a/frontend/src/app/shared/components/help/help.component.ts +++ b/frontend/src/app/shared/components/help/help.component.ts @@ -5,15 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { LayoutComponent } from '@app/framework'; import { HelpService } from '@app/shared/internal'; +import { HelpMarkdownPipe } from './help-markdown.pipe'; @Component({ + standalone: true, selector: 'sqx-help', styleUrls: ['./help.component.scss'], templateUrl: './help.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + HelpMarkdownPipe, + LayoutComponent, + ], }) export class HelpComponent { public helpMarkdown = this.helpService.getHelp(this.route.snapshot.data.helpPage); diff --git a/frontend/src/app/shared/components/history/history-list.component.ts b/frontend/src/app/shared/components/history/history-list.component.ts index 43c4fdff8..4e5f72433 100644 --- a/frontend/src/app/shared/components/history/history-list.component.ts +++ b/frontend/src/app/shared/components/history/history-list.component.ts @@ -5,14 +5,28 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FromNowPipe, TooltipDirective } from '@app/framework'; import { HistoryEventDto } from '@app/shared/internal'; +import { UserNameRefPipe, UserPictureRefPipe } from '../pipes'; +import { HistoryMessagePipe } from './pipes'; @Component({ + standalone: true, selector: 'sqx-history-list', styleUrls: ['./history-list.component.scss'], templateUrl: './history-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FromNowPipe, + HistoryMessagePipe, + NgFor, + NgIf, + TooltipDirective, + UserNameRefPipe, + UserPictureRefPipe, + ], }) export class HistoryListComponent { @Input({ required: true }) diff --git a/frontend/src/app/shared/components/history/history.component.ts b/frontend/src/app/shared/components/history/history.component.ts index d8757bbc7..8fa2e3d28 100644 --- a/frontend/src/app/shared/components/history/history.component.ts +++ b/frontend/src/app/shared/components/history/history.component.ts @@ -5,17 +5,26 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { merge, Observable, timer } from 'rxjs'; import { delay } from 'rxjs/operators'; +import { LayoutComponent } from '@app/framework'; import { allParams, AppsState, HistoryChannelUpdated, HistoryEventDto, HistoryService, MessageBus, SchemasState, switchSafe, TeamsState } from '@app/shared/internal'; +import { HistoryListComponent } from './history-list.component'; @Component({ + standalone: true, selector: 'sqx-history', styleUrls: ['./history.component.scss'], templateUrl: './history.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + HistoryListComponent, + LayoutComponent, + ], }) export class HistoryComponent { private readonly channel = this.calculateChannel(); diff --git a/frontend/src/app/shared/components/history/pipes.ts b/frontend/src/app/shared/components/history/pipes.ts index ddabb7550..aaaa51ecd 100644 --- a/frontend/src/app/shared/components/history/pipes.ts +++ b/frontend/src/app/shared/components/history/pipes.ts @@ -12,6 +12,7 @@ import { formatHistoryMessage, HistoryEventDto, UsersProviderService } from '@ap @Pipe({ name: 'sqxHistoryMessage', pure: false, + standalone: true, }) export class HistoryMessagePipe implements OnDestroy, PipeTransform { private subscription?: Subscription; diff --git a/frontend/src/app/shared/components/notifo.component.ts b/frontend/src/app/shared/components/notifo.component.ts index 1273cfe23..3454b8949 100644 --- a/frontend/src/app/shared/components/notifo.component.ts +++ b/frontend/src/app/shared/components/notifo.component.ts @@ -8,12 +8,17 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, inject, Input, OnDestroy, Renderer2, ViewChild } from '@angular/core'; import { ResourceLoaderService, TypedSimpleChanges, UIOptions } from '@app/framework'; import { AuthService } from '@app/shared/internal'; +import { TourHintDirective } from './tour-hint.directive'; @Component({ + standalone: true, selector: 'sqx-notifo', styleUrls: ['./notifo.component.scss'], templateUrl: './notifo.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TourHintDirective, + ], }) export class NotifoComponent implements AfterViewInit, OnDestroy { private readonly notifoApiUrl: string = inject(UIOptions).value.notifoApi; diff --git a/frontend/src/app/shared/components/pipes.ts b/frontend/src/app/shared/components/pipes.ts index 5c7d73de6..112acaf71 100644 --- a/frontend/src/app/shared/components/pipes.ts +++ b/frontend/src/app/shared/components/pipes.ts @@ -13,6 +13,7 @@ import { ApiUrlConfig, UserDto, UsersProviderService } from '@app/shared/interna @Pipe({ name: 'sqxScriptName', pure: true, + standalone: true, }) export class ScriptNamePipe implements PipeTransform { public transform(value: string) { @@ -75,6 +76,7 @@ class UserAsyncPipe { @Pipe({ name: 'sqxUserName', pure: false, + standalone: true, }) export class UserNamePipe extends UserAsyncPipe implements OnDestroy, PipeTransform { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) { @@ -94,6 +96,7 @@ export class UserNamePipe extends UserAsyncPipe implements OnDestroy, PipeTransf @Pipe({ name: 'sqxUserNameRef', pure: false, + standalone: true, }) export class UserNameRefPipe extends UserAsyncPipe implements OnDestroy, PipeTransform { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) { @@ -122,6 +125,7 @@ export class UserNameRefPipe extends UserAsyncPipe implements OnDestroy, PipeTra @Pipe({ name: 'sqxUserDtoPicture', pure: false, + standalone: true, }) export class UserDtoPicture implements PipeTransform { constructor( @@ -137,6 +141,7 @@ export class UserDtoPicture implements PipeTransform { @Pipe({ name: 'sqxUserIdPicture', pure: false, + standalone: true, }) export class UserIdPicturePipe implements PipeTransform { constructor( @@ -152,6 +157,7 @@ export class UserIdPicturePipe implements PipeTransform { @Pipe({ name: 'sqxUserPicture', pure: false, + standalone: true, }) export class UserPicturePipe extends UserAsyncPipe implements OnDestroy, PipeTransform { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef, @@ -173,6 +179,7 @@ export class UserPicturePipe extends UserAsyncPipe implements OnDestroy, PipeTra @Pipe({ name: 'sqxUserPictureRef', pure: false, + standalone: true, }) export class UserPictureRefPipe extends UserAsyncPipe implements OnDestroy, PipeTransform { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef, diff --git a/frontend/src/app/shared/components/references/content-selector-item.component.ts b/frontend/src/app/shared/components/references/content-selector-item.component.ts index f8e1ab108..4be7f3e7c 100644 --- a/frontend/src/app/shared/components/references/content-selector-item.component.ts +++ b/frontend/src/app/shared/components/references/content-selector-item.component.ts @@ -7,14 +7,27 @@ /* eslint-disable @angular-eslint/component-selector */ +import { NgFor } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { StopClickDirective } from '@app/framework'; import { ContentDto, LanguageDto, META_FIELDS, SchemaDto } from '@app/shared/internal'; +import { ContentListCellDirective } from '../contents/content-list-cell.directive'; +import { ContentListFieldComponent } from '../contents/content-list-field.component'; @Component({ + standalone: true, selector: '[sqxContentSelectorItem][language][languages][schema]', styleUrls: ['./content-selector-item.component.scss'], templateUrl: './content-selector-item.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ContentListCellDirective, + ContentListFieldComponent, + FormsModule, + NgFor, + StopClickDirective, + ], }) export class ContentSelectorItemComponent { public readonly metaFields = META_FIELDS; diff --git a/frontend/src/app/shared/components/references/content-selector.component.ts b/frontend/src/app/shared/components/references/content-selector.component.ts index a421ca1c5..e1715538a 100644 --- a/frontend/src/app/shared/components/references/content-selector.component.ts +++ b/frontend/src/app/shared/components/references/content-selector.component.ts @@ -5,18 +5,44 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, Component, EventEmitter, Input, numberAttribute, OnInit, Output } from '@angular/core'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; +import { booleanAttribute, Component, EventEmitter, forwardRef, Input, numberAttribute, OnInit, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { BehaviorSubject, of } from 'rxjs'; import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; +import { LanguageSelectorComponent, ListViewComponent, ModalDialogComponent, PagerComponent, SyncWidthDirective, TooltipDirective, TranslatePipe } from '@app/framework'; import { ApiUrlConfig, AppsState, ComponentContentsState, ContentDto, LanguageDto, META_FIELDS, Query, SchemaDto, SchemasService, SchemasState, Subscriptions } from '@app/shared/internal'; +import { ContentListCellDirective, ContentListWidthDirective } from '../contents/content-list-cell.directive'; +import { ContentListHeaderComponent } from '../contents/content-list-header.component'; +import { SearchFormComponent } from '../search/search-form.component'; +import { ContentSelectorItemComponent } from './content-selector-item.component'; @Component({ + standalone: true, selector: 'sqx-content-selector', styleUrls: ['./content-selector.component.scss'], templateUrl: './content-selector.component.html', providers: [ ComponentContentsState, ], + imports: [ + AsyncPipe, + ContentListCellDirective, + ContentListHeaderComponent, + ContentListWidthDirective, + ContentSelectorItemComponent, + FormsModule, + LanguageSelectorComponent, + ListViewComponent, + ModalDialogComponent, + NgFor, + NgIf, + PagerComponent, + SyncWidthDirective, + TooltipDirective, + TranslatePipe, + forwardRef(() => SearchFormComponent), + ], }) export class ContentSelectorComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/references/reference-input.component.ts b/frontend/src/app/shared/components/references/reference-input.component.ts index 7a7a0b1fc..7570d13a9 100644 --- a/frontend/src/app/shared/components/references/reference-input.component.ts +++ b/frontend/src/app/shared/components/references/reference-input.component.ts @@ -6,8 +6,10 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ModalDirective, StopClickDirective } from '@app/framework'; import { AppsState, ContentDto, ContentsService, DialogModel, getContentValue, LanguageDto, LocalizerService, StatefulControlComponent, TypedSimpleChanges, Types } from '@app/shared/internal'; +import { ContentSelectorComponent } from './content-selector.component'; export const SQX_REFERENCE_INPUT_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferenceInputComponent), multi: true, @@ -22,6 +24,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-reference-input', styleUrls: ['./reference-input.component.scss'], templateUrl: './reference-input.component.html', @@ -29,6 +32,12 @@ interface State { SQX_REFERENCE_INPUT_CONTROL_VALUE_ACCESSOR, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ContentSelectorComponent, + FormsModule, + ModalDirective, + StopClickDirective, + ], }) export class ReferenceInputComponent extends StatefulControlComponent<State, ReadonlyArray<string> | string> { @Input() diff --git a/frontend/src/app/shared/components/schema-category.component.ts b/frontend/src/app/shared/components/schema-category.component.ts index 4bc8bb595..5349a939b 100644 --- a/frontend/src/app/shared/components/schema-category.component.ts +++ b/frontend/src/app/shared/components/schema-category.component.ts @@ -5,17 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop, CdkDragStart } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDragStart, CdkDropList } from '@angular/cdk/drag-drop'; +import { NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { EditableTitleComponent, StopDragDirective, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/framework'; import { LocalStoreService, SchemaCategory, SchemaDto, SchemasState } from '@app/shared/internal'; const ITEM_HEIGHT = 2.5; @Component({ + standalone: true, selector: 'sqx-schema-category', styleUrls: ['./schema-category.component.scss'], templateUrl: './schema-category.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CdkDrag, + CdkDragHandle, + CdkDropList, + EditableTitleComponent, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + StopDragDirective, + TooltipDirective, + TourStepDirective, + TranslatePipe, + ], }) export class SchemaCategoryComponent { @Output() diff --git a/frontend/src/app/shared/components/search/queries/filter-comparison.component.ts b/frontend/src/app/shared/components/search/queries/filter-comparison.component.ts index 6aeaf273c..76c3a7d50 100644 --- a/frontend/src/app/shared/components/search/queries/filter-comparison.component.ts +++ b/frontend/src/app/shared/components/search/queries/filter-comparison.component.ts @@ -5,15 +5,38 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { FilterableField, FilterComparison, FilterFieldUI, getFilterUI, LanguageDto, QueryModel, StatusInfo } from '@app/shared/internal'; -import { ContributorsState } from '@app/shared/state/contributors.state'; +import { FormsModule } from '@angular/forms'; +import { DateTimeEditorComponent, DropdownComponent, HighlightPipe, TranslatePipe } from '@app/framework'; +import { ContributorsState, FilterableField, FilterComparison, FilterFieldUI, getFilterUI, LanguageDto, QueryModel, StatusInfo } from '@app/shared/internal'; +import { UserDtoPicture } from '../../pipes'; +import { ReferenceInputComponent } from '../../references/reference-input.component'; +import { QueryPathComponent } from './query-path.component'; +import { FilterOperatorPipe } from './query.pipes'; @Component({ + standalone: true, selector: 'sqx-filter-comparison', styleUrls: ['./filter-comparison.component.scss'], templateUrl: './filter-comparison.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + DateTimeEditorComponent, + DropdownComponent, + FilterOperatorPipe, + FormsModule, + HighlightPipe, + NgFor, + NgIf, + NgSwitch, + NgSwitchCase, + QueryPathComponent, + ReferenceInputComponent, + TranslatePipe, + UserDtoPicture, + ], }) export class FilterComparisonComponent { @Output() diff --git a/frontend/src/app/shared/components/search/queries/filter-logical.component.ts b/frontend/src/app/shared/components/search/queries/filter-logical.component.ts index 19229bdbd..4f40e8866 100644 --- a/frontend/src/app/shared/components/search/queries/filter-logical.component.ts +++ b/frontend/src/app/shared/components/search/queries/filter-logical.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; +import { TranslatePipe } from '@app/framework'; import { FilterLogical, FilterNode, LanguageDto, QueryModel, StatusInfo } from '@app/shared/internal'; +import { FilterNodeComponent } from './filter-node.component'; @Component({ + standalone: true, selector: 'sqx-filter-logical', styleUrls: ['./filter-logical.component.scss'], templateUrl: './filter-logical.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FilterNodeComponent, + NgFor, + NgIf, + TranslatePipe, + ], }) export class FilterLogicalComponent { private filterValue!: FilterLogical; diff --git a/frontend/src/app/shared/components/search/queries/filter-node.component.ts b/frontend/src/app/shared/components/search/queries/filter-node.component.ts index 8f26feb78..c03ba172e 100644 --- a/frontend/src/app/shared/components/search/queries/filter-node.component.ts +++ b/frontend/src/app/shared/components/search/queries/filter-node.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; +import { NgIf } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, numberAttribute, Output } from '@angular/core'; import { FilterComparison, FilterLogical, FilterNode, LanguageDto, QueryModel, StatusInfo } from '@app/shared/internal'; +import { FilterComparisonComponent } from './filter-comparison.component'; +import { FilterLogicalComponent } from './filter-logical.component'; @Component({ + standalone: true, selector: 'sqx-filter-node', styleUrls: ['./filter-node.component.scss'], templateUrl: './filter-node.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + forwardRef(() => FilterComparisonComponent), + forwardRef(() => FilterLogicalComponent), + ], }) export class FilterNodeComponent { public comparison?: FilterComparison; diff --git a/frontend/src/app/shared/components/search/queries/query-path.component.ts b/frontend/src/app/shared/components/search/queries/query-path.component.ts index c67b4dd72..4cf1e919d 100644 --- a/frontend/src/app/shared/components/search/queries/query-path.component.ts +++ b/frontend/src/app/shared/components/search/queries/query-path.component.ts @@ -6,13 +6,21 @@ */ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { DropdownComponent, FormHintComponent } from '@app/framework'; import { FilterableField, QueryModel } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-query-path', styleUrls: ['./query-path.component.scss'], templateUrl: './query-path.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DropdownComponent, + FormHintComponent, + FormsModule, + ], }) export class QueryPathComponent { @Output() diff --git a/frontend/src/app/shared/components/search/queries/query.component.ts b/frontend/src/app/shared/components/search/queries/query.component.ts index 847e3dc36..8692078ec 100644 --- a/frontend/src/app/shared/components/search/queries/query.component.ts +++ b/frontend/src/app/shared/components/search/queries/query.component.ts @@ -5,14 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { TranslatePipe } from '@app/framework'; import { LanguageDto, Query, QueryModel, StatusInfo } from '@app/shared/internal'; +import { FilterLogicalComponent } from './filter-logical.component'; +import { SortingComponent } from './sorting.component'; @Component({ + standalone: true, selector: 'sqx-query', styleUrls: ['./query.component.scss'], templateUrl: './query.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FilterLogicalComponent, + NgFor, + SortingComponent, + TranslatePipe, + ], }) export class QueryComponent { @Output() diff --git a/frontend/src/app/shared/components/search/queries/query.pipes.ts b/frontend/src/app/shared/components/search/queries/query.pipes.ts index b670309f7..db0421a89 100644 --- a/frontend/src/app/shared/components/search/queries/query.pipes.ts +++ b/frontend/src/app/shared/components/search/queries/query.pipes.ts @@ -10,6 +10,7 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'sqxFilterOperator', pure: true, + standalone: true, }) export class FilterOperatorPipe implements PipeTransform { public transform(value: any) { diff --git a/frontend/src/app/shared/components/search/queries/sorting.component.ts b/frontend/src/app/shared/components/search/queries/sorting.component.ts index 3106df356..1f0f23682 100644 --- a/frontend/src/app/shared/components/search/queries/sorting.component.ts +++ b/frontend/src/app/shared/components/search/queries/sorting.component.ts @@ -5,15 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { QueryModel, QuerySorting, SORT_MODES } from '@app/shared/internal'; +import { QueryPathComponent } from './query-path.component'; - @Component({ +@Component({ + standalone: true, selector: 'sqx-sorting', - styleUrls: ['./sorting.component.scss'], - templateUrl: './sorting.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - }) + styleUrls: ['./sorting.component.scss'], + templateUrl: './sorting.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + NgFor, + QueryPathComponent, + ], +}) export class SortingComponent { @Output() public sortingChange = new EventEmitter(); diff --git a/frontend/src/app/shared/components/search/query-list.component.ts b/frontend/src/app/shared/components/search/query-list.component.ts index a0e9d0742..865ac4b6e 100644 --- a/frontend/src/app/shared/components/search/query-list.component.ts +++ b/frontend/src/app/shared/components/search/query-list.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgFor, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { StopClickDirective, TranslatePipe } from '@app/framework'; import { equalsQuery, Query, SavedQuery } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-query-list', styleUrls: ['./query-list.component.scss'], templateUrl: './query-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgFor, + NgIf, + StopClickDirective, + TranslatePipe, + ], }) export class QueryListComponent { @Output() diff --git a/frontend/src/app/shared/components/search/search-form.component.ts b/frontend/src/app/shared/components/search/search-form.component.ts index ec9e1a6a2..c90fa05f2 100644 --- a/frontend/src/app/shared/components/search/search-form.component.ts +++ b/frontend/src/app/shared/components/search/search-form.component.ts @@ -5,15 +5,42 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable } from 'rxjs'; +import { ControlErrorsComponent, FocusOnInitDirective, MarkdownPipe, ModalDialogComponent, ModalDirective, SafeHtmlPipe, ShortcutComponent, ShortcutDirective, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/framework'; import { DialogModel, equalsQuery, hasFilter, LanguageDto, Queries, Query, QueryModel, SaveQueryForm, StatusInfo, TypedSimpleChanges, Types } from '@app/shared/internal'; +import { TourHintDirective } from '../tour-hint.directive'; +import { QueryComponent } from './queries/query.component'; +import { SavedQueriesComponent } from './shared-queries.component'; @Component({ + standalone: true, selector: 'sqx-search-form', styleUrls: ['./search-form.component.scss'], templateUrl: './search-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FocusOnInitDirective, + FormsModule, + MarkdownPipe, + ModalDialogComponent, + ModalDirective, + NgIf, + QueryComponent, + ReactiveFormsModule, + SafeHtmlPipe, + SavedQueriesComponent, + ShortcutComponent, + ShortcutDirective, + TooltipDirective, + TourHintDirective, + TourStepDirective, + TranslatePipe, + ], }) export class SearchFormComponent { private previousQuery?: Query | null; diff --git a/frontend/src/app/shared/components/search/shared-queries.component.ts b/frontend/src/app/shared/components/search/shared-queries.component.ts index 3e2963e48..c201a3814 100644 --- a/frontend/src/app/shared/components/search/shared-queries.component.ts +++ b/frontend/src/app/shared/components/search/shared-queries.component.ts @@ -5,14 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { TranslatePipe } from '@app/framework'; import { Queries, Query } from '@app/shared/internal'; +import { QueryListComponent } from './query-list.component'; @Component({ + standalone: true, selector: 'sqx-shared-queries', styleUrls: ['./shared-queries.component.scss'], templateUrl: './shared-queries.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + QueryListComponent, + TranslatePipe, + ], }) export class SavedQueriesComponent { @Output() diff --git a/frontend/src/app/shared/components/table-header.component.ts b/frontend/src/app/shared/components/table-header.component.ts index 62a5c83a8..dfd84a007 100644 --- a/frontend/src/app/shared/components/table-header.component.ts +++ b/frontend/src/app/shared/components/table-header.component.ts @@ -5,14 +5,21 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { TranslatePipe } from '@app/framework'; import { LanguageDto, Query, SortMode, Types } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-table-header', styleUrls: ['./table-header.component.scss'], templateUrl: './table-header.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + TranslatePipe, + ], }) export class TableHeaderComponent { @Output() diff --git a/frontend/src/app/shared/components/team-form.component.ts b/frontend/src/app/shared/components/team-form.component.ts index a8a6aa0ca..e3de79eca 100644 --- a/frontend/src/app/shared/components/team-form.component.ts +++ b/frontend/src/app/shared/components/team-form.component.ts @@ -5,14 +5,32 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ControlErrorsComponent, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, ModalDialogComponent, TooltipDirective, TransformInputDirective, TranslatePipe } from '@app/framework'; import { ApiUrlConfig, CreateTeamForm, TeamsState } from '@app/shared/internal'; @Component({ + standalone: true, selector: 'sqx-team-form', styleUrls: ['./team-form.component.scss'], templateUrl: './team-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + ControlErrorsComponent, + FocusOnInitDirective, + FormAlertComponent, + FormErrorComponent, + FormHintComponent, + FormsModule, + ModalDialogComponent, + ReactiveFormsModule, + TooltipDirective, + TransformInputDirective, + TranslatePipe, + ], }) export class TeamFormComponent { @Output() diff --git a/frontend/src/app/shared/components/tour-guide.component.ts b/frontend/src/app/shared/components/tour-guide.component.ts index 0efc39806..3f1ae48a5 100644 --- a/frontend/src/app/shared/components/tour-guide.component.ts +++ b/frontend/src/app/shared/components/tour-guide.component.ts @@ -5,7 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ScrollActiveDirective, TranslatePipe } from '@app/framework'; import { fadeAnimation, StatefulComponent, Subscriptions, TaskSnapshot, TourService, TourState } from '@app/shared/internal'; interface State { @@ -14,6 +16,7 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-tour-guide', styleUrls: ['./tour-guide.component.scss'], templateUrl: './tour-guide.component.html', @@ -21,6 +24,13 @@ interface State { fadeAnimation, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + NgFor, + NgIf, + ScrollActiveDirective, + TranslatePipe, + ], }) export class TourGuideComponent extends StatefulComponent<State> implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/tour-hint.directive.ts b/frontend/src/app/shared/components/tour-hint.directive.ts index 05efc6908..dd4ebcd5d 100644 --- a/frontend/src/app/shared/components/tour-hint.directive.ts +++ b/frontend/src/app/shared/components/tour-hint.directive.ts @@ -14,6 +14,7 @@ import { FloatingPlacement, StepDefinition, Subscriptions, TourService, TourStat @Directive({ selector: '[hintText]', + standalone: true, }) export class TourHintDirective implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shared/components/watching-users.component.ts b/frontend/src/app/shared/components/watching-users.component.ts index c3f2aace2..d10fe2991 100644 --- a/frontend/src/app/shared/components/watching-users.component.ts +++ b/frontend/src/app/shared/components/watching-users.component.ts @@ -5,14 +5,27 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf, SlicePipe } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { StringColorPipe, TooltipDirective } from '@app/framework'; import { CollaborationService, Profile } from '@app/shared/internal'; +import { UserPicturePipe } from './pipes'; @Component({ + standalone: true, selector: 'sqx-watching-users', styleUrls: ['./watching-users.component.scss'], templateUrl: './watching-users.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + NgFor, + NgIf, + SlicePipe, + StringColorPipe, + TooltipDirective, + UserPicturePipe, + ], }) export class WatchingUsersComponent { constructor( diff --git a/frontend/src/app/shared/declarations.ts b/frontend/src/app/shared/declarations.ts deleted file mode 100644 index c2771b215..000000000 --- a/frontend/src/app/shared/declarations.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './components/app-form.component'; -export * from './components/assets/asset-dialog.component'; -export * from './components/assets/asset-folder-dialog.component'; -export * from './components/assets/asset-folder-dropdown-item.component'; -export * from './components/assets/asset-folder-dropdown.component'; -export * from './components/assets/asset-folder.component'; -export * from './components/assets/asset-history.component'; -export * from './components/assets/asset-path.component'; -export * from './components/assets/asset-selector.component'; -export * from './components/assets/asset-text-editor.component'; -export * from './components/assets/asset-uploader.component'; -export * from './components/assets/asset.component'; -export * from './components/assets/assets-list.component'; -export * from './components/assets/image-cropper.component'; -export * from './components/assets/image-focus-point.component'; -export * from './components/assets/pipes'; -export * from './components/cards/api-calls-card.component'; -export * from './components/cards/api-calls-summary-card.component'; -export * from './components/cards/api-performance-card.component'; -export * from './components/cards/api-traffic-card.component'; -export * from './components/cards/api-traffic-summary-card.component'; -export * from './components/cards/asset-uploads-count-card.component'; -export * from './components/cards/asset-uploads-size-card.component'; -export * from './components/cards/asset-uploads-size-summary-card.component'; -export * from './components/cards/iframe-card.component'; -export * from './components/cards/random-cat-card.component'; -export * from './components/cards/random-dog-card.component'; -export * from './components/cards/support-card.component'; -export * from './components/chat-dialog.component'; -export * from './components/comments/comment.component'; -export * from './components/comments/comments.component'; -export * from './components/contents/content-list-cell.directive'; -export * from './components/contents/content-list-field.component'; -export * from './components/contents/content-list-header.component'; -export * from './components/contents/content-status.component'; -export * from './components/contents/content-value-editor.component'; -export * from './components/contents/content-value.component'; -export * from './components/contents/translation-status.component'; -export * from './components/cursors.component'; -export * from './components/cursors.directive'; -export * from './components/focus-marker.component'; -export * from './components/forms/geolocation-editor.component'; -export * from './components/forms/rich-editor.component'; -export * from './components/help/help-markdown.pipe'; -export * from './components/help/help.component'; -export * from './components/history/history-list.component'; -export * from './components/history/history.component'; -export * from './components/history/pipes'; -export * from './components/notifo.component'; -export * from './components/pipes'; -export * from './components/references/content-selector-item.component'; -export * from './components/references/content-selector.component'; -export * from './components/references/reference-input.component'; -export * from './components/schema-category.component'; -export * from './components/search/queries/filter-comparison.component'; -export * from './components/search/queries/filter-logical.component'; -export * from './components/search/queries/filter-node.component'; -export * from './components/search/queries/query-path.component'; -export * from './components/search/queries/query.component'; -export * from './components/search/queries/query.pipes'; -export * from './components/search/queries/sorting.component'; -export * from './components/search/query-list.component'; -export * from './components/search/search-form.component'; -export * from './components/search/shared-queries.component'; -export * from './components/table-header.component'; -export * from './components/team-form.component'; -export * from './components/tour-guide.component'; -export * from './components/tour-hint.directive'; -export * from './components/watching-users.component'; -export * from './guards/app-must-exist.guard'; -export * from './guards/content-must-exist.guard'; -export * from './guards/load-apps.guard'; -export * from './guards/load-languages.guard'; -export * from './guards/load-schemas.guard'; -export * from './guards/load-settings.guard'; -export * from './guards/load-teams.guard'; -export * from './guards/must-be-authenticated.guard'; -export * from './guards/must-be-not-authenticated.guard'; -export * from './guards/rule-must-exist.guard'; -export * from './guards/schema-must-exist-published.guard'; -export * from './guards/schema-must-exist.guard'; -export * from './guards/schema-must-not-be-singleton.guard'; -export * from './guards/team-must-exist.guard'; -export * from './guards/unset-app.guard'; -export * from './guards/unset-team.guard'; -export * from './internal'; diff --git a/frontend/src/app/shared/guards/app-must-exist.guard.spec.ts b/frontend/src/app/shared/guards/app-must-exist.guard.spec.ts index c060d33ba..cff70a829 100644 --- a/frontend/src/app/shared/guards/app-must-exist.guard.spec.ts +++ b/frontend/src/app/shared/guards/app-must-exist.guard.spec.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { AppsState, UIState } from '@app/shared/internal'; -import { AppMustExistGuard } from './app-must-exist.guard'; +import { appMustExistGuard } from './app-must-exist.guard'; describe('AppMustExistGuard', () => { const route: any = { @@ -20,36 +21,57 @@ describe('AppMustExistGuard', () => { let router: IMock<Router>; let appsState: IMock<AppsState>; - let appGuard: AppMustExistGuard; let uiState: IMock<UIState>; beforeEach(() => { uiState = Mock.ofType<UIState>(); router = Mock.ofType<Router>(); appsState = Mock.ofType<AppsState>(); - appGuard = new AppMustExistGuard(appsState.object, router.object, uiState.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: AppsState, + useValue: appsState.object, + }, + { + provide: Router, + useValue: router.object, + }, + { + provide: UIState, + useValue: uiState.object, + }, + ], + }); }); - it('should navigate to 404 page if app is not found', async () => { + bit('should navigate to 404 page if app is not found', async () => { appsState.setup(x => x.select('my-app')) .returns(() => of(null)); - const result = await firstValueFrom(appGuard.canActivate(route)); + const result = await firstValueFrom(appMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should return true if app is found', async () => { + bit('should return true if app is found', async () => { uiState.setup(x => x.loadApp('my-app')) .returns(() => of(<any>{})); appsState.setup(x => x.select('my-app')) .returns(() => of(<any>{})); - const result = await firstValueFrom(appGuard.canActivate(route)); + const result = await firstValueFrom(appMustExistGuard(route)); expect(result).toBeTruthy(); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/app-must-exist.guard.ts b/frontend/src/app/shared/guards/app-must-exist.guard.ts index c87906c25..b9588c316 100644 --- a/frontend/src/app/shared/guards/app-must-exist.guard.ts +++ b/frontend/src/app/shared/guards/app-must-exist.guard.ts @@ -5,41 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable, of } from 'rxjs'; +import { of } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; -import { UIState } from './../internal'; -import { AppsState } from './../state/apps.state'; +import { AppsState, UIState } from '@app/shared/internal'; -@Injectable() -export class AppMustExistGuard { - constructor( - private readonly appsState: AppsState, - private readonly router: Router, - private readonly uiState: UIState, - ) { - } +export const appMustExistGuard = (route: ActivatedRouteSnapshot) => { + const appsState = inject(AppsState); + const appName = route.params['appName']; + const router = inject(Router); + const uiState = inject(UIState); - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const appName = route.params['appName']; + const result = + appsState.select(appName).pipe( + tap(app => { + if (!app) { + router.navigate(['/404']); + } + }), + switchMap(app => { + if (app) { + return uiState.loadApp(appName).pipe(map(() => app)); + } else { + return of(app); + } + }), + map(app => !!app)); - const result = - this.appsState.select(appName).pipe( - tap(app => { - if (!app) { - this.router.navigate(['/404']); - } - }), - switchMap(app => { - if (app) { - return this.uiState.loadApp(appName).pipe(map(() => app)); - } else { - return of(app); - } - }), - map(app => !!app)); - - return result; - } -} + return result; +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/content-must-exist.guard.spec.ts b/frontend/src/app/shared/guards/content-must-exist.guard.spec.ts index c14f8455d..e249ff2c6 100644 --- a/frontend/src/app/shared/guards/content-must-exist.guard.spec.ts +++ b/frontend/src/app/shared/guards/content-must-exist.guard.spec.ts @@ -5,24 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { ContentDto, ContentsState } from '@app/shared/internal'; -import { ContentMustExistGuard } from './content-must-exist.guard'; +import { contentMustExistGuard } from './content-must-exist.guard'; describe('ContentMustExistGuard', () => { let router: IMock<Router>; let contentsState: IMock<ContentsState>; - let contentGuard: ContentMustExistGuard; beforeEach(() => { router = Mock.ofType<Router>(); contentsState = Mock.ofType<ContentsState>(); - contentGuard = new ContentMustExistGuard(contentsState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: ContentsState, + useValue: contentsState.object, + }, + { + provide: Router, + useValue: router.object, + }, + ], + }); }); - it('should load content and return true if found', async () => { + bit('should load content and return true if found', async () => { contentsState.setup(x => x.select('123')) .returns(() => of(<ContentDto>{})); @@ -32,14 +44,14 @@ describe('ContentMustExistGuard', () => { }, }; - const result = await firstValueFrom(contentGuard.canActivate(route)); + const result = await firstValueFrom(contentMustExistGuard(route)); expect(result).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should load content and return false if not found', async () => { + bit('should load content and return false if not found', async () => { contentsState.setup(x => x.select('123')) .returns(() => of(null)); @@ -49,14 +61,14 @@ describe('ContentMustExistGuard', () => { }, }; - const result = await firstValueFrom(contentGuard.canActivate(route)); + const result = await firstValueFrom(contentMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should unset content if content id is undefined', async () => { + bit('should unset content if content id is undefined', async () => { contentsState.setup(x => x.select(null)) .returns(() => of(null)); @@ -66,14 +78,14 @@ describe('ContentMustExistGuard', () => { }, }; - const result = await firstValueFrom(contentGuard.canActivate(route)); + const result = await firstValueFrom(contentMustExistGuard(route)); expect(result!).toBeTruthy(); contentsState.verify(x => x.select(null), Times.once()); }); - it('should unset content if content id is <new>', async () => { + bit('should unset content if content id is <new>', async () => { contentsState.setup(x => x.select(null)) .returns(() => of(null)); @@ -83,10 +95,16 @@ describe('ContentMustExistGuard', () => { }, }; - const result = await firstValueFrom(contentGuard.canActivate(route)); + const result = await firstValueFrom(contentMustExistGuard(route)); expect(result!).toBeTruthy(); contentsState.verify(x => x.select(null), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/content-must-exist.guard.ts b/frontend/src/app/shared/guards/content-must-exist.guard.ts index d9dff5e17..8e471b5ff 100644 --- a/frontend/src/app/shared/guards/content-must-exist.guard.ts +++ b/frontend/src/app/shared/guards/content-must-exist.guard.ts @@ -5,39 +5,31 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { allParams } from '@app/framework'; -import { ContentsState } from './../state/contents.state'; +import { ContentsState } from '@app/shared/internal'; -@Injectable() -export class ContentMustExistGuard { - constructor( - private readonly contentsState: ContentsState, - private readonly router: Router, - ) { - } - - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const contentId = allParams(route)['contentId']; +export const contentMustExistGuard = (route: ActivatedRouteSnapshot) => { + const contentsState = inject(ContentsState); + const contentId = allParams(route)['contentId']; + const router = inject(Router); - if (!contentId || contentId === 'new') { - return this.contentsState.select(null).pipe(map(u => u === null)); - } + if (!contentId || contentId === 'new') { + return contentsState.select(null).pipe(map(u => u === null)); + } - const decoded = decodeURIComponent(contentId); + const decoded = decodeURIComponent(contentId); - const result = - this.contentsState.select(decoded).pipe( - tap(content => { - if (!content) { - this.router.navigate(['/404']); - } - }), - map(content => !!content)); + const result = + contentsState.select(decoded).pipe( + tap(content => { + if (!content) { + router.navigate(['/404']); + } + }), + map(content => !!content)); - return result; - } -} + return result; +}; diff --git a/frontend/src/app/shared/guards/load-apps.guard.spec.ts b/frontend/src/app/shared/guards/load-apps.guard.spec.ts index 29979c586..aea171e13 100644 --- a/frontend/src/app/shared/guards/load-apps.guard.spec.ts +++ b/frontend/src/app/shared/guards/load-apps.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { AppsState } from '@app/shared/internal'; -import { LoadAppsGuard } from './load-apps.guard'; +import { loadAppsGuard } from './load-apps.guard'; describe('LoadAppsGuard', () => { let appsState: IMock<AppsState>; - let appGuard: LoadAppsGuard; beforeEach(() => { appsState = Mock.ofType<AppsState>(); - appGuard = new LoadAppsGuard(appsState.object); + + TestBed.configureTestingModule({ providers: [{ provide: AppsState, useValue: appsState.object }] }); }); - it('should load apps', async () => { + bit('should load apps', async () => { appsState.setup(x => x.load()) .returns(() => of(null)); - const result = await firstValueFrom(appGuard.canActivate()); + const result = await firstValueFrom(loadAppsGuard()); expect(result).toBeTruthy(); appsState.verify(x => x.load(), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/load-apps.guard.ts b/frontend/src/app/shared/guards/load-apps.guard.ts index f75125016..0658de3c7 100644 --- a/frontend/src/app/shared/guards/load-apps.guard.ts +++ b/frontend/src/app/shared/guards/load-apps.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { AppsState } from './../state/apps.state'; +import { AppsState } from '../state/apps.state'; -@Injectable() -export class LoadAppsGuard { - constructor( - private readonly appsState: AppsState, - ) { - } +export const loadAppsGuard = () => { + const appsState = inject(AppsState); - public canActivate(): Observable<boolean> { - return this.appsState.load().pipe(map(() => true)); - } -} + return appsState.load().pipe(map(() => true)); +}; diff --git a/frontend/src/app/shared/guards/load-languages.guard.spec.ts b/frontend/src/app/shared/guards/load-languages.guard.spec.ts index 7d7513a85..c3390e85b 100644 --- a/frontend/src/app/shared/guards/load-languages.guard.spec.ts +++ b/frontend/src/app/shared/guards/load-languages.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { LanguagesState } from '@app/shared/internal'; -import { LoadLanguagesGuard } from './load-languages.guard'; +import { loadLanguagesGuard } from './load-languages.guard'; describe('LoadLanguagesGuard', () => { let languagesState: IMock<LanguagesState>; - let languageGuard: LoadLanguagesGuard; beforeEach(() => { languagesState = Mock.ofType<LanguagesState>(); - languageGuard = new LoadLanguagesGuard(languagesState.object); + + TestBed.configureTestingModule({ providers: [{ provide: LanguagesState, useValue: languagesState.object }] }); }); - it('should load languages', async () => { + bit('should load languages', async () => { languagesState.setup(x => x.load()) .returns(() => of(null)); - const result = await firstValueFrom(languageGuard.canActivate()); + const result = await firstValueFrom(loadLanguagesGuard()); expect(result).toBeTruthy(); languagesState.verify(x => x.load(), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/load-languages.guard.ts b/frontend/src/app/shared/guards/load-languages.guard.ts index 7adc18a0a..c7943419f 100644 --- a/frontend/src/app/shared/guards/load-languages.guard.ts +++ b/frontend/src/app/shared/guards/load-languages.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { LanguagesState } from './../state/languages.state'; +import { LanguagesState } from '@app/shared/internal'; -@Injectable() -export class LoadLanguagesGuard { - constructor( - private readonly languagesState: LanguagesState, - ) { - } +export const loadLanguagesGuard = () => { + const languagesState = inject(LanguagesState); - public canActivate(): Observable<boolean> { - return this.languagesState.load().pipe(map(_ => true)); - } -} + return languagesState.load().pipe(map(_ => true)); +}; diff --git a/frontend/src/app/shared/guards/load-schemas.guard.spec.ts b/frontend/src/app/shared/guards/load-schemas.guard.spec.ts index 6bea07f9d..190d0dc55 100644 --- a/frontend/src/app/shared/guards/load-schemas.guard.spec.ts +++ b/frontend/src/app/shared/guards/load-schemas.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { SchemasState } from '@app/shared/internal'; -import { LoadSchemasGuard } from './load-schemas.guard'; +import { loadSchemasGuard } from './load-schemas.guard'; describe('LoadSchemasGuard', () => { let schemasState: IMock<SchemasState>; - let schemaGuard: LoadSchemasGuard; beforeEach(() => { schemasState = Mock.ofType<SchemasState>(); - schemaGuard = new LoadSchemasGuard(schemasState.object); + + TestBed.configureTestingModule({ providers: [{ provide: SchemasState, useValue: schemasState.object }] }); }); - it('should load schemas', async () => { + bit('should load schemas', async () => { schemasState.setup(x => x.load()) .returns(() => of(null)); - const result = await firstValueFrom(schemaGuard.canActivate()); + const result = await firstValueFrom(loadSchemasGuard()); expect(result).toBeTruthy(); schemasState.verify(x => x.load(), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/load-schemas.guard.ts b/frontend/src/app/shared/guards/load-schemas.guard.ts index aec382595..39d5f9c1a 100644 --- a/frontend/src/app/shared/guards/load-schemas.guard.ts +++ b/frontend/src/app/shared/guards/load-schemas.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { SchemasState } from './../state/schemas.state'; +import { SchemasState } from '@app/shared/internal'; -@Injectable() -export class LoadSchemasGuard { - constructor( - private readonly schemasState: SchemasState, - ) { - } +export const loadSchemasGuard = () => { + const schemasState = inject(SchemasState); - public canActivate(): Observable<boolean> { - return this.schemasState.load().pipe(map(_ => true)); - } -} + return schemasState.load().pipe(map(_ => true)); +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/load-settings.guard.spec.ts b/frontend/src/app/shared/guards/load-settings.guard.spec.ts index 8c9c78831..fd3b03cca 100644 --- a/frontend/src/app/shared/guards/load-settings.guard.spec.ts +++ b/frontend/src/app/shared/guards/load-settings.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { UIState } from '@app/shared/internal'; -import { LoadSettingsGuard } from './load-settings.guard'; +import { loadSettingsGuard } from './load-settings.guard'; describe('LoadAppsGuard', () => { - let settingsState: IMock<UIState>; - let settingsGuard: LoadSettingsGuard; + let uiState: IMock<UIState>; beforeEach(() => { - settingsState = Mock.ofType<UIState>(); - settingsGuard = new LoadSettingsGuard(settingsState.object); + uiState = Mock.ofType<UIState>(); + + TestBed.configureTestingModule({ providers: [{ provide: UIState, useValue: uiState.object }] }); }); - it('should load apps', async () => { - settingsState.setup(x => x.load()) + bit('should load apps', async () => { + uiState.setup(x => x.load()) .returns(() => of(null as any)); - const result = await firstValueFrom(settingsGuard.canActivate()); + const result = await firstValueFrom(loadSettingsGuard()); expect(result).toBeTruthy(); - settingsState.verify(x => x.load(), Times.once()); + uiState.verify(x => x.load(), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/load-settings.guard.ts b/frontend/src/app/shared/guards/load-settings.guard.ts index ed0b53601..470c09c5d 100644 --- a/frontend/src/app/shared/guards/load-settings.guard.ts +++ b/frontend/src/app/shared/guards/load-settings.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { UIState } from './../state/ui.state'; +import { UIState } from '../state/ui.state'; -@Injectable() -export class LoadSettingsGuard { - constructor( - private readonly uiState: UIState, - ) { - } +export const loadSettingsGuard = () => { + const uiState = inject(UIState); - public canActivate(): Observable<boolean> { - return this.uiState.load().pipe(map(() => true)); - } -} + return uiState.load().pipe(map(() => true)); +}; diff --git a/frontend/src/app/shared/guards/load-teams.guard.spec.ts b/frontend/src/app/shared/guards/load-teams.guard.spec.ts index b0303b98f..1caa617a3 100644 --- a/frontend/src/app/shared/guards/load-teams.guard.spec.ts +++ b/frontend/src/app/shared/guards/load-teams.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { TeamsState } from '@app/shared/internal'; -import { LoadTeamsGuard } from './load-teams.guard'; +import { loadTeamsGuard } from './load-teams.guard'; describe('LoadTeamsGuard', () => { let teamsState: IMock<TeamsState>; - let teamGuard: LoadTeamsGuard; beforeEach(() => { teamsState = Mock.ofType<TeamsState>(); - teamGuard = new LoadTeamsGuard(teamsState.object); + + TestBed.configureTestingModule({ providers: [{ provide: TeamsState, useValue: teamsState.object }] }); }); - it('should load teams', async () => { + bit('should load teams', async () => { teamsState.setup(x => x.load()) .returns(() => of(null)); - const result = await firstValueFrom(teamGuard.canActivate()); + const result = await firstValueFrom(loadTeamsGuard()); expect(result).toBeTruthy(); teamsState.verify(x => x.load(), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/load-teams.guard.ts b/frontend/src/app/shared/guards/load-teams.guard.ts index 50c6b6bb8..da4505c76 100644 --- a/frontend/src/app/shared/guards/load-teams.guard.ts +++ b/frontend/src/app/shared/guards/load-teams.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { TeamsState } from './../state/teams.state'; +import { TeamsState } from '../state/teams.state'; -@Injectable() -export class LoadTeamsGuard { - constructor( - private readonly teamsState: TeamsState, - ) { - } +export const loadTeamsGuard = () => { + const teamsState = inject(TeamsState); - public canActivate(): Observable<boolean> { - return this.teamsState.load().pipe(map(() => true)); - } -} + return teamsState.load().pipe(map(() => true)); +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/must-be-authenticated.guard.spec.ts b/frontend/src/app/shared/guards/must-be-authenticated.guard.spec.ts index 29fcd7e4d..48320c6da 100644 --- a/frontend/src/app/shared/guards/must-be-authenticated.guard.spec.ts +++ b/frontend/src/app/shared/guards/must-be-authenticated.guard.spec.ts @@ -11,63 +11,81 @@ import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { AuthService, UIOptions } from '@app/shared/internal'; -import { MustBeAuthenticatedGuard } from './must-be-authenticated.guard'; +import { mustBeAuthenticatedGuard } from './must-be-authenticated.guard'; describe('MustBeAuthenticatedGuard', () => { - let router: IMock<Router>; - let location: IMock<Location>; - let authService: IMock<AuthService>; - let authGuard: MustBeAuthenticatedGuard; - const uiOptions = new UIOptions({}); + let authService: IMock<AuthService>; + let location: IMock<Location>; + let router: IMock<Router>; beforeEach(() => { uiOptions.value.redirectToLogin = false; - + authService = Mock.ofType<AuthService>(); location = Mock.ofType<Location>(); location.setup(x => x.path(true)).returns(() => '/my-path'); - router = Mock.ofType<Router>(); - TestBed.configureTestingModule({ providers: [{ provide: UIOptions, useValue: uiOptions }] }); - TestBed.runInInjectionContext(() => { - authService = Mock.ofType<AuthService>(); - authGuard = new MustBeAuthenticatedGuard(authService.object, location.object, router.object); + TestBed.configureTestingModule({ + providers: [ + { + provide: AuthService, + useValue: authService.object, + }, + { + provide: Location, + useValue: location.object, + }, + { + provide: Router, + useValue: router.object, + }, + { + provide: UIOptions, + useValue: uiOptions, + }, + ], }); }); - it('should navigate to default page if not authenticated', async () => { + bit('should navigate to default page if not authenticated', async () => { authService.setup(x => x.userChanges) .returns(() => of(null)); - const result = await firstValueFrom(authGuard.canActivate()); + const result = await firstValueFrom(mustBeAuthenticatedGuard()); expect(result).toBeFalsy(); router.verify(x => x.navigate([''], { queryParams: { redirectPath: '/my-path' } }), Times.once()); }); - it('should return true if authenticated', async () => { + bit('should return true if authenticated', async () => { authService.setup(x => x.userChanges) .returns(() => of(<any>{})); - const result = await firstValueFrom(authGuard.canActivate()); + const result = await firstValueFrom(mustBeAuthenticatedGuard()); expect(result!).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should login redirect if redirect enabled', async () => { + bit('should login redirect if redirect enabled', async () => { uiOptions.value.redirectToLogin = true; authService.setup(x => x.userChanges) .returns(() => of(null)); - const result = await firstValueFrom(authGuard.canActivate()); + const result = await firstValueFrom(mustBeAuthenticatedGuard()); expect(result!).toBeFalsy(); authService.verify(x => x.loginRedirect('/my-path'), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/must-be-authenticated.guard.ts b/frontend/src/app/shared/guards/must-be-authenticated.guard.ts index 44a9db1f8..90d50541c 100644 --- a/frontend/src/app/shared/guards/must-be-authenticated.guard.ts +++ b/frontend/src/app/shared/guards/must-be-authenticated.guard.ts @@ -6,40 +6,32 @@ */ import { Location } from '@angular/common'; -import { inject, Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, take, tap } from 'rxjs/operators'; import { UIOptions } from '@app/framework'; -import { AuthService } from './../services/auth.service'; +import { AuthService } from '../services/auth.service'; -@Injectable() -export class MustBeAuthenticatedGuard { - private readonly uiOptions = inject(UIOptions); +export const mustBeAuthenticatedGuard = () => { + const authService = inject(AuthService); + const location = inject(Location); + const options = inject(UIOptions); + const router = inject(Router); - constructor( - private readonly authService: AuthService, - private readonly location: Location, - private readonly router: Router, - ) { - } + return authService.userChanges.pipe( + take(1), + tap(user => { + if (user) { + return; + } - public canActivate(): Observable<boolean> { - return this.authService.userChanges.pipe( - take(1), - tap(user => { - if (user) { - return; - } + const redirectPath = location.path(true); - const redirectPath = this.location.path(true); - - if (this.uiOptions.value.redirectToLogin) { - this.authService.loginRedirect(redirectPath); - } else { - this.router.navigate([''], { queryParams: { redirectPath } }); - } - }), - map(user => !!user)); - } -} + if (options.value.redirectToLogin) { + authService.loginRedirect(redirectPath); + } else { + router.navigate([''], { queryParams: { redirectPath } }); + } + }), + map(user => !!user)); +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.spec.ts b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.spec.ts index 73e041e60..f3e883637 100644 --- a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.spec.ts +++ b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.spec.ts @@ -11,76 +11,94 @@ import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { AuthService, UIOptions } from '@app/shared/internal'; -import { MustBeNotAuthenticatedGuard } from './must-be-not-authenticated.guard'; +import { mustBeNotAuthenticatedGuard } from './must-be-not-authenticated.guard'; describe('MustBeNotAuthenticatedGuard', () => { - let router: IMock<Router>; - let location: IMock<Location>; - let authService: IMock<AuthService>; - let authGuard: MustBeNotAuthenticatedGuard; - const uiOptions = new UIOptions({}); + let authService: IMock<AuthService>; + let location: IMock<Location>; + let router: IMock<Router>; beforeEach(() => { uiOptions.value.redirectToLogin = false; - + authService = Mock.ofType<AuthService>(); location = Mock.ofType<Location>(); location.setup(x => x.path(true)).returns(() => '/my-path'); - router = Mock.ofType<Router>(); - TestBed.configureTestingModule({ providers: [{ provide: UIOptions, useValue: uiOptions }] }); - TestBed.runInInjectionContext(() => { - authService = Mock.ofType<AuthService>(); - authGuard = new MustBeNotAuthenticatedGuard(authService.object, location.object, router.object); + TestBed.configureTestingModule({ + providers: [ + { + provide: AuthService, + useValue: authService.object, + }, + { + provide: Location, + useValue: location.object, + }, + { + provide: Router, + useValue: router.object, + }, + { + provide: UIOptions, + useValue: uiOptions, + }, + ], }); }); - it('should navigate to app page if authenticated', async () => { + bit('should navigate to app page if authenticated', async () => { authService.setup(x => x.userChanges) .returns(() => of(<any>{})); - const result = await firstValueFrom(authGuard.canActivate({} as any)); + const result = await firstValueFrom(mustBeNotAuthenticatedGuard({} as any)); expect(result!).toBeFalsy(); router.verify(x => x.navigate(['app'], { queryParams: { redirectPath: '/my-path' } }), Times.once()); }); - it('should return true if not authenticated', async () => { + bit('should return true if not authenticated', async () => { authService.setup(x => x.userChanges) .returns(() => of(null)); - const result = await firstValueFrom(authGuard.canActivate({} as any)); + const result = await firstValueFrom(mustBeNotAuthenticatedGuard({} as any)); expect(result).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should login redirect and return false if redirect enabled', async () => { + bit('should login redirect and return false if redirect enabled', async () => { uiOptions.value.redirectToLogin = true; authService.setup(x => x.userChanges) .returns(() => of(null)); - const result = await firstValueFrom(authGuard.canActivate({ queryParams: {} } as any)); + const result = await firstValueFrom(mustBeNotAuthenticatedGuard({ queryParams: {} } as any)); expect(result).toBeFalsy(); authService.verify(x => x.loginRedirect('/my-path'), Times.once()); }); - it('should not redirect after logout', async () => { + bit('should not redirect after logout', async () => { uiOptions.value.redirectToLogin = true; authService.setup(x => x.userChanges) .returns(() => of(null)); - const result = await firstValueFrom(authGuard.canActivate({ queryParams: { logout: true } } as any)); + const result = await firstValueFrom(mustBeNotAuthenticatedGuard({ queryParams: { logout: true } } as any)); expect(result).toBeTruthy(); authService.verify(x => x.loginRedirect('/my-path'), Times.never()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts index 94a2237d3..929d5f778 100644 --- a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts +++ b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts @@ -6,38 +6,30 @@ */ import { Location } from '@angular/common'; -import { inject, Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, take, tap } from 'rxjs/operators'; import { UIOptions } from '@app/framework'; -import { AuthService } from './../services/auth.service'; +import { AuthService } from '../services/auth.service'; -@Injectable() -export class MustBeNotAuthenticatedGuard { - private readonly options = inject(UIOptions); +export const mustBeNotAuthenticatedGuard = (route: ActivatedRouteSnapshot) => { + const authService = inject(AuthService); + const location = inject(Location); + const options = inject(UIOptions); + const router = inject(Router); - constructor( - private readonly authService: AuthService, - private readonly location: Location, - private readonly router: Router, - ) { - } + const redirect = options.value.redirectToLogin && !route.queryParams.logout; - public canActivate(snapshot: ActivatedRouteSnapshot): Observable<boolean> { - const redirect = this.options.value.redirectToLogin && !snapshot.queryParams.logout; + return authService.userChanges.pipe( + take(1), + tap(user => { + const redirectPath = location.path(true); - return this.authService.userChanges.pipe( - take(1), - tap(user => { - const redirectPath = this.location.path(true); - - if (!user && redirect) { - this.authService.loginRedirect(redirectPath); - } else if (user) { - this.router.navigate(['app'], { queryParams: { redirectPath } }); - } - }), - map(user => !user && !redirect)); - } -} + if (!user && redirect) { + authService.loginRedirect(redirectPath); + } else if (user) { + router.navigate(['app'], { queryParams: { redirectPath } }); + } + }), + map(user => !user && !redirect)); +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/rule-must-exist.guard.spec.ts b/frontend/src/app/shared/guards/rule-must-exist.guard.spec.ts index 211e35b30..44f43acea 100644 --- a/frontend/src/app/shared/guards/rule-must-exist.guard.spec.ts +++ b/frontend/src/app/shared/guards/rule-must-exist.guard.spec.ts @@ -5,24 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { RuleDto, RulesState } from '@app/shared/internal'; -import { RuleMustExistGuard } from './rule-must-exist.guard'; +import { ruleMustExistGuard } from './rule-must-exist.guard'; describe('RuleMustExistGuard', () => { let router: IMock<Router>; let rulesState: IMock<RulesState>; - let ruleGuard: RuleMustExistGuard; beforeEach(() => { router = Mock.ofType<Router>(); rulesState = Mock.ofType<RulesState>(); - ruleGuard = new RuleMustExistGuard(rulesState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: RulesState, + useValue: rulesState.object, + }, + ], + }); }); - it('should load rule and return true if found', async () => { + bit('should load rule and return true if found', async () => { rulesState.setup(x => x.select('123')) .returns(() => of(<RuleDto>{})); @@ -32,14 +44,14 @@ describe('RuleMustExistGuard', () => { }, }; - const result = await firstValueFrom(ruleGuard.canActivate(route)); + const result = await firstValueFrom(ruleMustExistGuard(route)); expect(result).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should load rule and return false if not found', async () => { + bit('should load rule and return false if not found', async () => { rulesState.setup(x => x.select('123')) .returns(() => of(null)); @@ -49,14 +61,14 @@ describe('RuleMustExistGuard', () => { }, }; - const result = await firstValueFrom(ruleGuard.canActivate(route)); + const result = await firstValueFrom(ruleMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should unset rule if rule id is undefined', async () => { + bit('should unset rule if rule id is undefined', async () => { rulesState.setup(x => x.select(null)) .returns(() => of(null)); @@ -66,14 +78,14 @@ describe('RuleMustExistGuard', () => { }, }; - const result = await firstValueFrom(ruleGuard.canActivate(route)); + const result = await firstValueFrom(ruleMustExistGuard(route)); expect(result).toBeTruthy(); rulesState.verify(x => x.select(null), Times.once()); }); - it('should unset rule if rule id is <new>', async () => { + bit('should unset rule if rule id is <new>', async () => { rulesState.setup(x => x.select(null)) .returns(() => of(null)); @@ -83,10 +95,16 @@ describe('RuleMustExistGuard', () => { }, }; - const result = await firstValueFrom(ruleGuard.canActivate(route)); + const result = await firstValueFrom(ruleMustExistGuard(route)); expect(result).toBeTruthy(); rulesState.verify(x => x.select(null), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/rule-must-exist.guard.ts b/frontend/src/app/shared/guards/rule-must-exist.guard.ts index d990d4aea..bae95b494 100644 --- a/frontend/src/app/shared/guards/rule-must-exist.guard.ts +++ b/frontend/src/app/shared/guards/rule-must-exist.guard.ts @@ -5,37 +5,29 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { allParams } from '@app/framework'; -import { RulesState } from './../state/rules.state'; +import { RulesState } from '../state/rules.state'; -@Injectable() -export class RuleMustExistGuard { - constructor( - private readonly rulesState: RulesState, - private readonly router: Router, - ) { - } - - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const ruleId = allParams(route)['ruleId']; +export const ruleMustExistGuard = (route: ActivatedRouteSnapshot) => { + const rulesState = inject(RulesState); + const ruleId = allParams(route)['ruleId']; + const router = inject(Router); - if (!ruleId || ruleId === 'new') { - return this.rulesState.select(null).pipe(map(u => u === null)); - } + if (!ruleId || ruleId === 'new') { + return rulesState.select(null).pipe(map(u => u === null)); + } - const result = - this.rulesState.select(ruleId).pipe( - tap(rule => { - if (!rule) { - this.router.navigate(['/404']); - } - }), - map(rule => !!rule)); + const result = + rulesState.select(ruleId).pipe( + tap(rule => { + if (!rule) { + router.navigate(['/404']); + } + }), + map(rule => !!rule)); - return result; - } -} + return result; +}; diff --git a/frontend/src/app/shared/guards/schema-must-exist-published.guard.spec.ts b/frontend/src/app/shared/guards/schema-must-exist-published.guard.spec.ts index 7fe33821e..49acc0901 100644 --- a/frontend/src/app/shared/guards/schema-must-exist-published.guard.spec.ts +++ b/frontend/src/app/shared/guards/schema-must-exist-published.guard.spec.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { SchemaDto, SchemasState } from '@app/shared/internal'; -import { SchemaMustExistPublishedGuard } from './schema-must-exist-published.guard'; +import { schemaMustExistPublishedGuard } from './schema-must-exist-published.guard'; describe('SchemaMustExistPublishedGuard', () => { const route: any = { @@ -18,57 +19,74 @@ describe('SchemaMustExistPublishedGuard', () => { }, }; - let schemasState: IMock<SchemasState>; let router: IMock<Router>; - let schemaGuard: SchemaMustExistPublishedGuard; + let schemasState: IMock<SchemasState>; beforeEach(() => { router = Mock.ofType<Router>(); schemasState = Mock.ofType<SchemasState>(); - schemaGuard = new SchemaMustExistPublishedGuard(schemasState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: SchemasState, + useValue: schemasState.object, + }, + ], + }); }); - it('should load schema and return true if published', async () => { + bit('should load schema and return true if published', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(<SchemaDto>{ isPublished: true })); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistPublishedGuard(route)); expect(result).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should load schema and return false if component', async () => { + bit('should load schema and return false if component', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(<SchemaDto>{ isPublished: true, type: 'Component' })); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistPublishedGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should load schema and return false if not found', async () => { + bit('should load schema and return false if not found', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(null)); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistPublishedGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should load schema and return false if not published', async () => { + bit('should load schema and return false if not published', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(<SchemaDto>{ isPublished: false, type: 'Default' })); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistPublishedGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/schema-must-exist-published.guard.ts b/frontend/src/app/shared/guards/schema-must-exist-published.guard.ts index 9a51f773b..5720dd429 100644 --- a/frontend/src/app/shared/guards/schema-must-exist-published.guard.ts +++ b/frontend/src/app/shared/guards/schema-must-exist-published.guard.ts @@ -5,33 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { allParams } from '@app/framework'; -import { SchemasState } from './../state/schemas.state'; +import { SchemasState } from '../state/schemas.state'; -@Injectable() -export class SchemaMustExistPublishedGuard { - constructor( - private readonly schemasState: SchemasState, - private readonly router: Router, - ) { - } +export const schemaMustExistPublishedGuard = (route: ActivatedRouteSnapshot) => { + const schemasState = inject(SchemasState); + const schemaName = allParams(route)['schemaName']; + const router = inject(Router); - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const schemaName = allParams(route)['schemaName']; + const result = + schemasState.select(schemaName).pipe( + tap(schema => { + if (!schema || !schema.isPublished || schema.type === 'Component') { + router.navigate(['/404']); + } + }), + map(schema => schema?.isPublished === true && schema.type !== 'Component')); - const result = - this.schemasState.select(schemaName).pipe( - tap(schema => { - if (!schema || !schema.isPublished || schema.type === 'Component') { - this.router.navigate(['/404']); - } - }), - map(schema => schema?.isPublished === true && schema.type !== 'Component')); - - return result; - } -} + return result; +}; diff --git a/frontend/src/app/shared/guards/schema-must-exist.guard.spec.ts b/frontend/src/app/shared/guards/schema-must-exist.guard.spec.ts index 695b7c9c9..e0eaccd81 100644 --- a/frontend/src/app/shared/guards/schema-must-exist.guard.spec.ts +++ b/frontend/src/app/shared/guards/schema-must-exist.guard.spec.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { SchemaDto, SchemasState } from '@app/shared/internal'; -import { SchemaMustExistGuard } from './schema-must-exist.guard'; +import { schemaMustExistGuard } from './schema-must-exist.guard'; describe('SchemaMustExistGuard', () => { const route: any = { @@ -18,33 +19,50 @@ describe('SchemaMustExistGuard', () => { }, }; - let schemasState: IMock<SchemasState>; let router: IMock<Router>; - let schemaGuard: SchemaMustExistGuard; + let schemasState: IMock<SchemasState>; beforeEach(() => { router = Mock.ofType<Router>(); schemasState = Mock.ofType<SchemasState>(); - schemaGuard = new SchemaMustExistGuard(schemasState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: SchemasState, + useValue: schemasState.object, + }, + ], + }); }); - it('should load schema and return true if found', async () => { + bit('should load schema and return true if found', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(<SchemaDto>{})); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistGuard(route)); expect(result).toBeTruthy(); }); - it('should load schema and return false if not found', async () => { + bit('should load schema and return false if not found', async () => { schemasState.setup(x => x.select('123')) .returns(() => of(null)); - const result = await firstValueFrom(schemaGuard.canActivate(route)); + const result = await firstValueFrom(schemaMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/schema-must-exist.guard.ts b/frontend/src/app/shared/guards/schema-must-exist.guard.ts index c7f246af6..ed1ca6908 100644 --- a/frontend/src/app/shared/guards/schema-must-exist.guard.ts +++ b/frontend/src/app/shared/guards/schema-must-exist.guard.ts @@ -5,33 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { allParams } from '@app/framework'; -import { SchemasState } from './../state/schemas.state'; +import { SchemasState } from '../state/schemas.state'; -@Injectable() -export class SchemaMustExistGuard { - constructor( - private readonly schemasState: SchemasState, - private readonly router: Router, - ) { - } +export const schemaMustExistGuard = (route: ActivatedRouteSnapshot) => { + const schemasState = inject(SchemasState); + const schemaName = allParams(route)['schemaName']; + const router = inject(Router); - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const schemaName = allParams(route)['schemaName']; + const result = + schemasState.select(schemaName).pipe( + tap(schema => { + if (!schema) { + router.navigate(['/404']); + } + }), + map(schema => !!schema)); - const result = - this.schemasState.select(schemaName).pipe( - tap(schema => { - if (!schema) { - this.router.navigate(['/404']); - } - }), - map(schema => !!schema)); - - return result; - } -} + return result; +}; diff --git a/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts b/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts index ec161474c..d8dbe105b 100644 --- a/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts +++ b/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.spec.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router, RouterStateSnapshot, UrlSegment } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { SchemaDto, SchemasState } from '@app/shared/internal'; -import { SchemaMustNotBeSingletonGuard } from './schema-must-not-be-singleton.guard'; +import { schemaMustNotBeSingletonGuard } from './schema-must-not-be-singleton.guard'; describe('SchemaMustNotBeSingletonGuard', () => { const route: any = { @@ -23,52 +24,69 @@ describe('SchemaMustNotBeSingletonGuard', () => { ], }; - let schemasState: IMock<SchemasState>; let router: IMock<Router>; - let schemaGuard: SchemaMustNotBeSingletonGuard; + let schemasState: IMock<SchemasState>; beforeEach(() => { router = Mock.ofType<Router>(); schemasState = Mock.ofType<SchemasState>(); - schemaGuard = new SchemaMustNotBeSingletonGuard(schemasState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: SchemasState, + useValue: schemasState.object, + }, + ], + }); }); - it('should subscribe to schema and return true if default', async () => { + bit('should subscribe to schema and return true if default', async () => { const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' }; schemasState.setup(x => x.selectedSchema) .returns(() => of(<SchemaDto>{ id: '123', type: 'Default' })); - const result = await firstValueFrom(schemaGuard.canActivate(route, state)); + const result = await firstValueFrom(schemaMustNotBeSingletonGuard(route, state)); expect(result).toBeTruthy(); router.verify(x => x.navigate(It.isAny()), Times.never()); }); - it('should redirect to content if singleton', async () => { + bit('should redirect to content if singleton', async () => { const state: RouterStateSnapshot = <any>{ url: 'schemas/name/' }; schemasState.setup(x => x.selectedSchema) .returns(() => of(<SchemaDto>{ id: '123', type: 'Singleton' })); - const result = await firstValueFrom(schemaGuard.canActivate(route, state)); + const result = await firstValueFrom(schemaMustNotBeSingletonGuard(route, state)); expect(result).toBeFalsy(); router.verify(x => x.navigate([state.url, '123']), Times.once()); }); - it('should redirect to content if singleton on new page', async () => { + bit('should redirect to content if singleton on new page', async () => { const state: RouterStateSnapshot = <any>{ url: 'schemas/name/new/' }; schemasState.setup(x => x.selectedSchema) .returns(() => of(<SchemaDto>{ id: '123', type: 'Singleton' })); - const result = await firstValueFrom(schemaGuard.canActivate(route, state)); + const result = await firstValueFrom(schemaMustNotBeSingletonGuard(route, state)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['schemas/name/', '123']), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.ts b/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.ts index 67cd471ca..c3eef547f 100644 --- a/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.ts +++ b/frontend/src/app/shared/guards/schema-must-not-be-singleton.guard.ts @@ -5,39 +5,32 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, take, tap } from 'rxjs/operators'; import { defined } from '@app/framework'; -import { SchemasState } from './../state/schemas.state'; +import { SchemasState } from '../state/schemas.state'; -@Injectable() -export class SchemaMustNotBeSingletonGuard { - constructor( - private readonly schemasState: SchemasState, - private readonly router: Router, - ) { - } +export const schemaMustNotBeSingletonGuard = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + const schemasState = inject(SchemasState); + const router = inject(Router); - public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { - const result = - this.schemasState.selectedSchema.pipe( - defined(), - take(1), - tap(schema => { - if (schema.type === 'Singleton') { - if (state.url.includes('/new')) { - const parentUrl = state.url.slice(0, state.url.indexOf(route.url[route.url.length - 1].path)); + const result = + schemasState.selectedSchema.pipe( + defined(), + take(1), + tap(schema => { + if (schema.type === 'Singleton') { + if (state.url.includes('/new')) { + const parentUrl = state.url.slice(0, state.url.indexOf(route.url[route.url.length - 1].path)); - this.router.navigate([parentUrl, schema.id]); - } else { - this.router.navigate([state.url, schema.id]); - } + router.navigate([parentUrl, schema.id]); + } else { + router.navigate([state.url, schema.id]); } - }), - map(schema => schema.type === 'Default')); + } + }), + map(schema => schema.type === 'Default')); - return result; - } -} + return result; +}; diff --git a/frontend/src/app/shared/guards/team-must-exist.guard.spec.ts b/frontend/src/app/shared/guards/team-must-exist.guard.spec.ts index d07dc929b..ec372fccb 100644 --- a/frontend/src/app/shared/guards/team-must-exist.guard.spec.ts +++ b/frontend/src/app/shared/guards/team-must-exist.guard.spec.ts @@ -5,11 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { TeamsState } from '@app/shared/internal'; -import { TeamMustExistGuard } from './team-must-exist.guard'; +import { teamMustExistGuard } from './team-must-exist.guard'; describe('TeamMustExistGuard', () => { const route: any = { @@ -20,31 +21,48 @@ describe('TeamMustExistGuard', () => { let router: IMock<Router>; let teamsState: IMock<TeamsState>; - let teamGuard: TeamMustExistGuard; beforeEach(() => { router = Mock.ofType<Router>(); teamsState = Mock.ofType<TeamsState>(); - teamGuard = new TeamMustExistGuard(teamsState.object, router.object); + + TestBed.configureTestingModule({ + providers: [ + { + provide: Router, + useValue: router.object, + }, + { + provide: TeamsState, + useValue: teamsState.object, + }, + ], + }); }); - it('should navigate to 404 page if team is not found', async () => { + bit('should navigate to 404 page if team is not found', async () => { teamsState.setup(x => x.select('my-team')) .returns(() => of(null)); - const result = await firstValueFrom(teamGuard.canActivate(route)); + const result = await firstValueFrom(teamMustExistGuard(route)); expect(result).toBeFalsy(); router.verify(x => x.navigate(['/404']), Times.once()); }); - it('should return true if team is found', async () => { + bit('should return true if team is found', async () => { teamsState.setup(x => x.select('my-team')) .returns(() => of(<any>{})); - const result = await firstValueFrom(teamGuard.canActivate(route)); + const result = await firstValueFrom(teamMustExistGuard(route)); expect(result).toBeTruthy(); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/team-must-exist.guard.ts b/frontend/src/app/shared/guards/team-must-exist.guard.ts index 6eb91eea7..de81a0c53 100644 --- a/frontend/src/app/shared/guards/team-must-exist.guard.ts +++ b/frontend/src/app/shared/guards/team-must-exist.guard.ts @@ -5,32 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; -import { TeamsState } from './../state/teams.state'; +import { TeamsState } from '../state/teams.state'; -@Injectable() -export class TeamMustExistGuard { - constructor( - private readonly teamsState: TeamsState, - private readonly router: Router, - ) { - } +export const teamMustExistGuard = (route: ActivatedRouteSnapshot) => { + const teamsState = inject(TeamsState); + const teamName = route.params['teamName']; + const router = inject(Router); - public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { - const teamName = route.params['teamName']; + const result = + teamsState.select(teamName).pipe( + tap(team => { + if (!team) { + router.navigate(['/404']); + } + }), + map(team => !!team)); - const result = - this.teamsState.select(teamName).pipe( - tap(team => { - if (!team) { - this.router.navigate(['/404']); - } - }), - map(team => !!team)); - - return result; - } -} + return result; +}; \ No newline at end of file diff --git a/frontend/src/app/shared/guards/unset-app.guard.spec.ts b/frontend/src/app/shared/guards/unset-app.guard.spec.ts index a23916013..768ff64e6 100644 --- a/frontend/src/app/shared/guards/unset-app.guard.spec.ts +++ b/frontend/src/app/shared/guards/unset-app.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { AppsState } from '@app/shared/internal'; -import { UnsetAppGuard } from './unset-app.guard'; +import { unsetAppGuard } from './unset-app.guard'; describe('UnsetAppGuard', () => { let appsState: IMock<AppsState>; - let appGuard: UnsetAppGuard; beforeEach(() => { appsState = Mock.ofType<AppsState>(); - appGuard = new UnsetAppGuard(appsState.object); + + TestBed.configureTestingModule({ providers: [{ provide: AppsState, useValue: appsState.object }] }); }); - it('should unselect app', async () => { + bit('should unselect app', async () => { appsState.setup(x => x.select(null)) .returns(() => of(null)); - const result = await firstValueFrom(appGuard.canActivate()); + const result = await firstValueFrom(unsetAppGuard()); expect(result).toBeTruthy(); appsState.verify(x => x.select(null), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} diff --git a/frontend/src/app/shared/guards/unset-app.guard.ts b/frontend/src/app/shared/guards/unset-app.guard.ts index aa202990d..294628213 100644 --- a/frontend/src/app/shared/guards/unset-app.guard.ts +++ b/frontend/src/app/shared/guards/unset-app.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { AppsState } from './../state/apps.state'; +import { AppsState } from '../state/apps.state'; -@Injectable() -export class UnsetAppGuard { - constructor( - private readonly appsState: AppsState, - ) { - } +export const unsetAppGuard = () => { + const appsState = inject(AppsState); - public canActivate(): Observable<boolean> { - return this.appsState.select(null).pipe(map(a => a === null)); - } -} + return appsState.select(null).pipe(map(a => a === null)); +}; diff --git a/frontend/src/app/shared/guards/unset-team.guard.spec.ts b/frontend/src/app/shared/guards/unset-team.guard.spec.ts index 9522b6434..e33cd2d8a 100644 --- a/frontend/src/app/shared/guards/unset-team.guard.spec.ts +++ b/frontend/src/app/shared/guards/unset-team.guard.spec.ts @@ -5,28 +5,35 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { TeamsState } from '@app/shared/internal'; -import { UnsetTeamGuard } from './unset-team.guard'; +import { unsetTeamGuard } from './unset-team.guard'; describe('UnsetTeamGuard', () => { let teamsState: IMock<TeamsState>; - let teamGuard: UnsetTeamGuard; beforeEach(() => { teamsState = Mock.ofType<TeamsState>(); - teamGuard = new UnsetTeamGuard(teamsState.object); + + TestBed.configureTestingModule({ providers: [{ provide: TeamsState, useValue: teamsState.object }] }); }); - it('should unselect team', async () => { + bit('should unselect team', async () => { teamsState.setup(x => x.select(null)) .returns(() => of(null)); - const result = await firstValueFrom(teamGuard.canActivate()); + const result = await firstValueFrom(unsetTeamGuard()); expect(result).toBeTruthy(); teamsState.verify(x => x.select(null), Times.once()); }); }); + +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} \ No newline at end of file diff --git a/frontend/src/app/shared/guards/unset-team.guard.ts b/frontend/src/app/shared/guards/unset-team.guard.ts index 0a6bcb144..2d4204e36 100644 --- a/frontend/src/app/shared/guards/unset-team.guard.ts +++ b/frontend/src/app/shared/guards/unset-team.guard.ts @@ -5,20 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; - -import { Observable } from 'rxjs'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; -import { TeamsState } from './../state/teams.state'; +import { TeamsState } from '../state/teams.state'; -@Injectable() -export class UnsetTeamGuard { - constructor( - private readonly teamsState: TeamsState, - ) { - } +export const unsetTeamGuard = () => { + const teamsState = inject(TeamsState); - public canActivate(): Observable<boolean> { - return this.teamsState.select(null).pipe(map(a => a === null)); - } -} + return teamsState.select(null).pipe(map(a => a === null)); +}; diff --git a/frontend/src/app/shared/index.ts b/frontend/src/app/shared/index.ts index 898be9a7c..c2771b215 100644 --- a/frontend/src/app/shared/index.ts +++ b/frontend/src/app/shared/index.ts @@ -5,5 +5,89 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -export * from './declarations'; -export * from './module'; +export * from './components/app-form.component'; +export * from './components/assets/asset-dialog.component'; +export * from './components/assets/asset-folder-dialog.component'; +export * from './components/assets/asset-folder-dropdown-item.component'; +export * from './components/assets/asset-folder-dropdown.component'; +export * from './components/assets/asset-folder.component'; +export * from './components/assets/asset-history.component'; +export * from './components/assets/asset-path.component'; +export * from './components/assets/asset-selector.component'; +export * from './components/assets/asset-text-editor.component'; +export * from './components/assets/asset-uploader.component'; +export * from './components/assets/asset.component'; +export * from './components/assets/assets-list.component'; +export * from './components/assets/image-cropper.component'; +export * from './components/assets/image-focus-point.component'; +export * from './components/assets/pipes'; +export * from './components/cards/api-calls-card.component'; +export * from './components/cards/api-calls-summary-card.component'; +export * from './components/cards/api-performance-card.component'; +export * from './components/cards/api-traffic-card.component'; +export * from './components/cards/api-traffic-summary-card.component'; +export * from './components/cards/asset-uploads-count-card.component'; +export * from './components/cards/asset-uploads-size-card.component'; +export * from './components/cards/asset-uploads-size-summary-card.component'; +export * from './components/cards/iframe-card.component'; +export * from './components/cards/random-cat-card.component'; +export * from './components/cards/random-dog-card.component'; +export * from './components/cards/support-card.component'; +export * from './components/chat-dialog.component'; +export * from './components/comments/comment.component'; +export * from './components/comments/comments.component'; +export * from './components/contents/content-list-cell.directive'; +export * from './components/contents/content-list-field.component'; +export * from './components/contents/content-list-header.component'; +export * from './components/contents/content-status.component'; +export * from './components/contents/content-value-editor.component'; +export * from './components/contents/content-value.component'; +export * from './components/contents/translation-status.component'; +export * from './components/cursors.component'; +export * from './components/cursors.directive'; +export * from './components/focus-marker.component'; +export * from './components/forms/geolocation-editor.component'; +export * from './components/forms/rich-editor.component'; +export * from './components/help/help-markdown.pipe'; +export * from './components/help/help.component'; +export * from './components/history/history-list.component'; +export * from './components/history/history.component'; +export * from './components/history/pipes'; +export * from './components/notifo.component'; +export * from './components/pipes'; +export * from './components/references/content-selector-item.component'; +export * from './components/references/content-selector.component'; +export * from './components/references/reference-input.component'; +export * from './components/schema-category.component'; +export * from './components/search/queries/filter-comparison.component'; +export * from './components/search/queries/filter-logical.component'; +export * from './components/search/queries/filter-node.component'; +export * from './components/search/queries/query-path.component'; +export * from './components/search/queries/query.component'; +export * from './components/search/queries/query.pipes'; +export * from './components/search/queries/sorting.component'; +export * from './components/search/query-list.component'; +export * from './components/search/search-form.component'; +export * from './components/search/shared-queries.component'; +export * from './components/table-header.component'; +export * from './components/team-form.component'; +export * from './components/tour-guide.component'; +export * from './components/tour-hint.directive'; +export * from './components/watching-users.component'; +export * from './guards/app-must-exist.guard'; +export * from './guards/content-must-exist.guard'; +export * from './guards/load-apps.guard'; +export * from './guards/load-languages.guard'; +export * from './guards/load-schemas.guard'; +export * from './guards/load-settings.guard'; +export * from './guards/load-teams.guard'; +export * from './guards/must-be-authenticated.guard'; +export * from './guards/must-be-not-authenticated.guard'; +export * from './guards/rule-must-exist.guard'; +export * from './guards/schema-must-exist-published.guard'; +export * from './guards/schema-must-exist.guard'; +export * from './guards/schema-must-not-be-singleton.guard'; +export * from './guards/team-must-exist.guard'; +export * from './guards/unset-app.guard'; +export * from './guards/unset-team.guard'; +export * from './internal'; diff --git a/frontend/src/app/shared/interceptors/auth.interceptor.spec.ts b/frontend/src/app/shared/interceptors/auth.interceptor.spec.ts index a8923d001..448773d30 100644 --- a/frontend/src/app/shared/interceptors/auth.interceptor.spec.ts +++ b/frontend/src/app/shared/interceptors/auth.interceptor.spec.ts @@ -8,14 +8,13 @@ /* eslint-disable deprecation/deprecation */ import { Location } from '@angular/common'; -import { HTTP_INTERCEPTORS, HttpClient, HttpHeaders } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { inject, TestBed } from '@angular/core/testing'; +import { HttpErrorResponse, HttpEventType, HttpHeaders, HttpRequest } from '@angular/common/http'; +import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; -import { of, onErrorResumeNextWith } from 'rxjs'; +import { firstValueFrom, lastValueFrom, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { ApiUrlConfig, AuthService } from '@app/shared/internal'; -import { AuthInterceptor } from './auth.interceptor'; +import { authInterceptor } from './auth.interceptor'; describe('AuthInterceptor', () => { let authService: IMock<AuthService>; @@ -24,137 +23,186 @@ describe('AuthInterceptor', () => { beforeEach(() => { authService = Mock.ofType<AuthService>(); - location = Mock.ofType<Location>(); location.setup(x => x.path()).returns(() => '/my-path'); - router = Mock.ofType<Router>(); TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule, - ], providers: [ - { provide: Router, useFactory: () => router.object }, - { provide: Location, useFactory: () => location.object }, - { provide: AuthService, useValue: authService.object }, - { provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') }, { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true, + provide: AuthService, + useValue: authService.object, + }, + { + provide: ApiUrlConfig, + useValue: new ApiUrlConfig('http://service/p/'), + }, + { + provide: Location, + useValue: location.object, + }, + { + provide: Router, + useValue: router.object, }, ], }); }); - afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { - httpMock.verify(); - })); - - it('should append headers to request', - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authorization: 'token1' })); + bit('should append headers to request', async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authorization: 'token1' })); - http.get('http://service/p/apps').subscribe(); + const initialRequest = new HttpRequest('GET', 'http://service/p/apps', { + headers: undefined, + }); - const req = httpMock.expectOne('http://service/p/apps'); + const invokedRequest: HttpRequest<any>[] = []; + await firstValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('Authorization')).toEqual('token1'); - expect(req.request.headers.get('Accept')).toBeNull(); - expect(req.request.headers.get('Accept-Language')).toBeNull(); - expect(req.request.headers.get('Pragma')).toEqual('no-cache'); + return of({ type: HttpEventType.Sent }); })); - it('should not append headers for no auth headers', - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authToauthorizationken: 'token1' })); + const req = invokedRequest[0]; + expect(invokedRequest.length).toEqual(1); + expect(req.method).toEqual('GET'); + expect(req.headers.get('Authorization')).toEqual('token1'); + expect(req.headers.get('Accept')).toBeNull(); + expect(req.headers.get('Accept-Language')).toBeNull(); + expect(req.headers.get('Pragma')).toEqual('no-cache'); + }); + + bit('should not append headers for no auth headers', async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authToauthorizationken: 'token1' })); - http.get('http://service/p/apps', { headers: new HttpHeaders().set('NoAuth', '') }).subscribe(); + const initialRequest = new HttpRequest('GET', 'http://service/p/apps', { + headers: new HttpHeaders().set('NoAuth', '1'), + }); - const req = httpMock.expectOne('http://service/p/apps'); + const invokedRequest: HttpRequest<any>[] = []; + await firstValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('Authorization')).toBeNull(); - expect(req.request.headers.get('Accept')).toBeNull(); - expect(req.request.headers.get('Accept-Language')).toBeNull(); - expect(req.request.headers.get('Pragma')).toBeNull(); + return of({ type: HttpEventType.Sent }); })); - it('should not append headers for other requests', - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authorization: 'token1' })); + const req = invokedRequest[0]; + expect(invokedRequest.length).toEqual(1); + expect(req.method).toEqual('GET'); + expect(req.headers.get('Authorization')).toBeNull(); + expect(req.headers.get('Accept')).toBeNull(); + expect(req.headers.get('Accept-Language')).toBeNull(); + expect(req.headers.get('Pragma')).toBeNull(); + }); - http.get('http://cloud/p/apps').subscribe(); + bit('should not append headers for other requests', async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authorization: 'token1' })); + + const initialRequest = new HttpRequest('GET', 'http://cloud/p/apps', { + headers: undefined, + }); - const req = httpMock.expectOne('http://cloud/p/apps'); + const invokedRequest: HttpRequest<any>[] = []; + await firstValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('Authorization')).toBeNull(); - expect(req.request.headers.get('Accept')).toBeNull(); - expect(req.request.headers.get('Accept-Language')).toBeNull(); - expect(req.request.headers.get('Pragma')).toBeNull(); + return of({ type: HttpEventType.Sent }); })); - it('should logout for 401 status code after retry', - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authorization: 'token1' })); + const req = invokedRequest[0]; + expect(invokedRequest.length).toEqual(1); + expect(req.method).toEqual('GET'); + expect(req.headers.get('Authorization')).toBeNull(); + expect(req.headers.get('Accept')).toBeNull(); + expect(req.headers.get('Accept-Language')).toBeNull(); + expect(req.headers.get('Pragma')).toBeNull(); + }); - authService.setup(x => x.loginSilent()) - .returns(() => ofPromise(<any>{ authorization: 'token2' })); + bit('should logout for 401 status code after retry', async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authorization: 'token1' })); - http.get('http://service/p/apps').pipe(onErrorResumeNextWith()).subscribe(); + authService.setup(x => x.loginSilent()) + .returns(() => ofPromise(<any>{ authorization: 'token2' })); - httpMock.expectOne('http://service/p/apps').flush({}, { status: 401, statusText: '401' }); - httpMock.expectOne('http://service/p/apps').flush({}, { status: 401, statusText: '401' }); + const initialRequest = new HttpRequest('GET', 'http://service/p/apps', { + headers: undefined, + }); - expect().nothing(); + const invokedRequest: HttpRequest<any>[] = []; + await lastValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - authService.verify(x => x.logoutRedirect('/my-path'), Times.once()); - })); + return throwError(() => ({ status: 401, statusText: '401' } as HttpErrorResponse)); + }).pipe(onErrorResumeNextWith()), { defaultValue: null }); + + const req1 = invokedRequest[0]; + const req2 = invokedRequest[1]; + expect(invokedRequest.length).toEqual(2); + expect(req1.headers.get('Authorization')).toEqual('token1'); + expect(req2.headers.get('Authorization')).toEqual('token2'); + + authService.verify(x => x.logoutRedirect('/my-path'), Times.once()); + }); const AUTH_ERRORS = [403]; AUTH_ERRORS.forEach(status => { - it(`should redirect for ${status} status code`, - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authorization: 'token1' })); + bit(`should redirect for ${status} status code`, async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authorization: 'token1' })); - http.get('http://service/p/apps').pipe(onErrorResumeNextWith()).subscribe(); + const initialRequest = new HttpRequest('GET', 'http://service/p/apps', { + headers: undefined, + }); - httpMock.expectOne('http://service/p/apps').flush({}, { status, statusText: `${status}` }); + const invokedRequest: HttpRequest<any>[] = []; + await firstValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - expect().nothing(); + return throwError(() => ({ status } as HttpErrorResponse)); + }), { defaultValue: null }); - router.verify(x => x.navigate(['/forbidden'], { replaceUrl: true }), Times.once()); - })); + expect().nothing(); + + router.verify(x => x.navigate(['/forbidden'], { replaceUrl: true }), Times.once()); + }); }); const SERVER_ERRORS = [500, 404, 405]; SERVER_ERRORS.forEach(status => { - it(`should not logout for ${status} status code`, - inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { - authService.setup(x => x.userChanges) - .returns(() => of(<any>{ authorization: 'token1' })); + bit(`should not logout for ${status} status code`, async () => { + authService.setup(x => x.userChanges) + .returns(() => of(<any>{ authorization: 'token1' })); - http.get('http://service/p/apps').pipe(onErrorResumeNextWith()).subscribe(); + const initialRequest = new HttpRequest('GET', 'http://service/p/apps', { + headers: undefined, + }); - httpMock.expectOne('http://service/p/apps').flush({}, { status, statusText: `${status}` }); + const invokedRequest: HttpRequest<any>[] = []; + await firstValueFrom(authInterceptor(initialRequest, request => { + invokedRequest.push(request); - expect().nothing(); + return throwError(() => ({ status } as HttpErrorResponse)); + }).pipe(onErrorResumeNextWith()), { defaultValue: null }); - authService.verify(x => x.logoutRedirect('/my-path'), Times.never()); - })); + expect().nothing(); + + authService.verify(x => x.logoutRedirect('/my-path'), Times.never()); + }); }); }); +function bit(name: string, assertion: (() => PromiseLike<any>) | (() => void)) { + it(name, () => { + return TestBed.runInInjectionContext(() => assertion()); + }); +} + function ofPromise(value: any): Promise<any> { return { then: (onfullfilled: (value: any) => void) => { diff --git a/frontend/src/app/shared/interceptors/auth.interceptor.ts b/frontend/src/app/shared/interceptors/auth.interceptor.ts index 41b3f95e0..9e1b03afb 100644 --- a/frontend/src/app/shared/interceptors/auth.interceptor.ts +++ b/frontend/src/app/shared/interceptors/auth.interceptor.ts @@ -6,61 +6,45 @@ */ import { Location } from '@angular/common'; -import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { HttpErrorResponse, HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http'; +import { inject } from '@angular/core'; import { Router } from '@angular/router'; import { EMPTY, from, Observable, throwError } from 'rxjs'; import { catchError, switchMap, take } from 'rxjs/operators'; import { ApiUrlConfig, ErrorDto } from '@app/framework'; -import { AuthService, Profile } from './../services/auth.service'; +import { AuthService, Profile } from '../services/auth.service'; -@Injectable() -export class AuthInterceptor implements HttpInterceptor { - private readonly baseUrl: string; +type InternalFunction = (user: Profile | null, renew: boolean) => Observable<HttpEvent<any>>; - constructor(apiUrlConfig: ApiUrlConfig, - private readonly authService: AuthService, - private readonly location: Location, - private readonly router: Router, - ) { - this.baseUrl = apiUrlConfig.buildUrl(''); - } - - public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { - if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) { - return this.authService.userChanges.pipe( - take(1), - switchMap(user => { - return this.makeRequest(req, next, user, true); - })); - } else { - return next.handle(req); - } - } +export const authInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn) => { + const authService = inject(AuthService); + const baseUrl = inject(ApiUrlConfig).buildUrl(''); + const location = inject(Location); + const router = inject(Router); - private makeRequest(req: HttpRequest<any>, next: HttpHandler, user: Profile | null, renew = false): Observable<HttpEvent<any>> { + const makeRequest:InternalFunction = (user, renew) => { const token = user?.authorization || ''; req = req.clone({ headers: req.headers.set('Authorization', token).set('Pragma', 'no-cache'), }); - return next.handle(req).pipe( + return next(req).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401 && renew) { - return from(this.authService.loginSilent()).pipe( + return from(authService.loginSilent()).pipe( catchError(() => { - this.authService.logoutRedirect(this.location.path()); + authService.logoutRedirect(location.path()); return EMPTY; }), - switchMap(u => this.makeRequest(req, next, u))); + switchMap(u => makeRequest(u, false))); } else if (error.status === 401 || error.status === 403) { if (req.method === 'GET') { if (error.status === 401) { - this.authService.logoutRedirect(this.location.path()); + authService.logoutRedirect(location.path()); } else { - this.router.navigate(['/forbidden'], { replaceUrl: true }); + router.navigate(['/forbidden'], { replaceUrl: true }); } return EMPTY; @@ -71,5 +55,15 @@ export class AuthInterceptor implements HttpInterceptor { return throwError(() => error); })); + }; + + if (req.url.indexOf(baseUrl) === 0 && !req.headers.has('NoAuth')) { + return authService.userChanges.pipe( + take(1), + switchMap(user => { + return makeRequest(user, true); + })); + } else { + return next(req); } -} +}; diff --git a/frontend/src/app/shared/module.ts b/frontend/src/app/shared/module.ts deleted file mode 100644 index 0e6891694..000000000 --- a/frontend/src/app/shared/module.ts +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { DragDropModule } from '@angular/cdk/drag-drop'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ModuleWithProviders, NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { MentionModule } from 'angular-mentions'; -import { NgChartsModule } from 'ng2-charts'; -import { NgxDocViewerModule } from 'ngx-doc-viewer'; -import { SqxFrameworkModule } from '@app/framework'; -import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, ApiTrafficSummaryCardComponent, AppFormComponent, AppLanguagesService, AppMustExistGuard, AppsService, AppsState, AssetComponent, AssetDialogComponent, AssetFolderComponent, AssetFolderDialogComponent, AssetFolderDropdownComponent, AssetFolderDropdownItemComponent, AssetHistoryComponent, AssetPathComponent, AssetPreviewUrlPipe, AssetScriptsState, AssetSelectorComponent, AssetsListComponent, AssetsService, AssetsState, AssetTextEditorComponent, AssetUploaderComponent, AssetUploaderState, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, AssetUrlPipe, AuthInterceptor, AuthService, AutoSaveService, BackupsService, BackupsState, buildTasks, ChatDialogComponent, ClientsService, ClientsState, CommentComponent, CommentsComponent, ContentListCellDirective, ContentListCellResizeDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthDirective, ContentMustExistGuard, ContentsColumnsPipe, ContentSelectorComponent, ContentSelectorItemComponent, ContentsService, ContentsState, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, ContributorsService, ContributorsState, CursorsComponent, CursorsDirective, FileIconPipe, FilterComparisonComponent, FilterLogicalComponent, FilterNodeComponent, FilterOperatorPipe, FocusMarkerComponent, GeolocationEditorComponent, HelpComponent, HelpMarkdownPipe, HelpService, HistoryComponent, HistoryListComponent, HistoryMessagePipe, HistoryService, IFrameCardComponent, ImageCropperComponent, ImageFocusPointComponent, LanguagesService, LanguagesState, LoadAppsGuard, LoadLanguagesGuard, LoadSchemasGuard, LoadSettingsGuard, LoadTeamsGuard, MustBeAuthenticatedGuard, MustBeNotAuthenticatedGuard, NewsService, NotifoComponent, PlansService, PlansState, PreviewableType, QueryComponent, QueryListComponent, QueryPathComponent, RandomCatCardComponent, RandomDogCardComponent, ReferenceInputComponent, RichEditorComponent, RolesService, RolesState, RuleEventsState, RuleMustExistGuard, RuleSimulatorState, RulesService, RulesState, SavedQueriesComponent, SchemaCategoryComponent, SchemaMustExistGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SchemasService, SchemasState, SchemaTagSource, ScriptNamePipe, SearchFormComponent, SearchService, SortingComponent, StockPhotoService, SupportCardComponent, TableHeaderComponent, TASK_CONFIGURATION, TeamFormComponent, TeamMustExistGuard, TeamsService, TeamsState, TemplatesService, TemplatesState, TourGuideComponent, TourHintDirective, TourState, TranslationsService, TranslationStatusComponent, UIService, UIState, UnsetAppGuard, UnsetTeamGuard, UsagesService, UserDtoPicture, UserIdPicturePipe, UserNamePipe, UserNameRefPipe, UserPicturePipe, UserPictureRefPipe, UsersProviderService, UsersService, WatchingUsersComponent, WorkflowsService, WorkflowsState } from './declarations'; - -@NgModule({ - imports: [ - DragDropModule, - MentionModule, - NgChartsModule, - NgxDocViewerModule, - RouterModule, - SqxFrameworkModule, - ], - declarations: [ - ApiCallsCardComponent, - ApiCallsSummaryCardComponent, - ApiPerformanceCardComponent, - ApiTrafficCardComponent, - ApiTrafficSummaryCardComponent, - AppFormComponent, - AssetComponent, - AssetDialogComponent, - AssetFolderComponent, - AssetFolderDialogComponent, - AssetFolderDropdownComponent, - AssetFolderDropdownItemComponent, - AssetHistoryComponent, - AssetPathComponent, - AssetPreviewUrlPipe, - AssetSelectorComponent, - AssetsListComponent, - AssetTextEditorComponent, - AssetUploaderComponent, - AssetUploadsCountCardComponent, - AssetUploadsSizeCardComponent, - AssetUploadsSizeSummaryCardComponent, - AssetUrlPipe, - ChatDialogComponent, - CommentComponent, - CommentsComponent, - ContentListCellDirective, - ContentListCellResizeDirective, - ContentListFieldComponent, - ContentListHeaderComponent, - ContentListWidthDirective, - ContentsColumnsPipe, - ContentSelectorComponent, - ContentSelectorItemComponent, - ContentStatusComponent, - ContentValueComponent, - ContentValueEditorComponent, - CursorsComponent, - CursorsDirective, - FileIconPipe, - FilterComparisonComponent, - FilterLogicalComponent, - FilterNodeComponent, - FilterOperatorPipe, - FocusMarkerComponent, - GeolocationEditorComponent, - HelpComponent, - HelpMarkdownPipe, - HistoryComponent, - HistoryListComponent, - HistoryMessagePipe, - IFrameCardComponent, - ImageCropperComponent, - ImageFocusPointComponent, - NotifoComponent, - PreviewableType, - QueryComponent, - QueryListComponent, - QueryPathComponent, - RandomCatCardComponent, - RandomDogCardComponent, - ReferenceInputComponent, - RichEditorComponent, - SavedQueriesComponent, - SchemaCategoryComponent, - ScriptNamePipe, - SearchFormComponent, - SortingComponent, - SupportCardComponent, - TableHeaderComponent, - TeamFormComponent, - TourGuideComponent, - TourHintDirective, - TranslationStatusComponent, - UserDtoPicture, - UserIdPicturePipe, - UserNamePipe, - UserNameRefPipe, - UserPicturePipe, - UserPictureRefPipe, - WatchingUsersComponent, - ], - exports: [ - ApiCallsCardComponent, - ApiCallsSummaryCardComponent, - ApiPerformanceCardComponent, - ApiTrafficCardComponent, - ApiTrafficSummaryCardComponent, - AppFormComponent, - AssetComponent, - AssetDialogComponent, - AssetFolderComponent, - AssetFolderDialogComponent, - AssetFolderDropdownComponent, - AssetPathComponent, - AssetPreviewUrlPipe, - AssetSelectorComponent, - AssetsListComponent, - AssetUploaderComponent, - AssetUploadsCountCardComponent, - AssetUploadsSizeCardComponent, - AssetUploadsSizeSummaryCardComponent, - AssetUrlPipe, - ChatDialogComponent, - CommentComponent, - CommentsComponent, - ContentListCellDirective, - ContentListCellResizeDirective, - ContentListFieldComponent, - ContentListHeaderComponent, - ContentListWidthDirective, - ContentsColumnsPipe, - ContentSelectorComponent, - ContentSelectorItemComponent, - ContentStatusComponent, - ContentValueComponent, - ContentValueEditorComponent, - CursorsComponent, - CursorsDirective, - DragDropModule, - FileIconPipe, - FocusMarkerComponent, - GeolocationEditorComponent, - HelpComponent, - HelpMarkdownPipe, - HistoryComponent, - HistoryListComponent, - HistoryMessagePipe, - IFrameCardComponent, - NotifoComponent, - PreviewableType, - QueryListComponent, - RandomCatCardComponent, - RandomDogCardComponent, - ReferenceInputComponent, - RichEditorComponent, - RouterModule, - SavedQueriesComponent, - SchemaCategoryComponent, - ScriptNamePipe, - SearchFormComponent, - SupportCardComponent, - TableHeaderComponent, - TeamFormComponent, - TourGuideComponent, - TourHintDirective, - TranslationStatusComponent, - UserDtoPicture, - UserIdPicturePipe, - UserNamePipe, - UserNameRefPipe, - UserPicturePipe, - UserPictureRefPipe, - WatchingUsersComponent, - ], -}) -export class SqxSharedModule { - public static forRoot(): ModuleWithProviders<SqxSharedModule> { - return { - ngModule: SqxSharedModule, - providers: [ - AppLanguagesService, - AppMustExistGuard, - AppsService, - AppsState, - AssetScriptsState, - AssetsService, - AssetsState, - AssetUploaderState, - AuthService, - AutoSaveService, - BackupsService, - BackupsState, - ClientsService, - ClientsState, - ContentMustExistGuard, - ContentsService, - ContentsState, - ContributorsService, - ContributorsState, - HelpService, - HistoryService, - LanguagesService, - LanguagesState, - LoadAppsGuard, - LoadLanguagesGuard, - LoadSchemasGuard, - LoadSettingsGuard, - LoadTeamsGuard, - MustBeAuthenticatedGuard, - MustBeNotAuthenticatedGuard, - NewsService, - PlansService, - PlansState, - RolesService, - RolesState, - RuleEventsState, - RuleMustExistGuard, - RuleSimulatorState, - RulesService, - RulesState, - SchemaMustExistGuard, - SchemaMustExistPublishedGuard, - SchemaMustNotBeSingletonGuard, - SchemasService, - SchemasState, - SchemaTagSource, - SearchService, - StockPhotoService, - TeamMustExistGuard, - TemplatesService, - TemplatesState, - TeamsState, - TeamsService, - TourState, - TranslationsService, - UIService, - UIState, - UnsetAppGuard, - UnsetTeamGuard, - UsagesService, - UsersProviderService, - UsersService, - WorkflowsService, - WorkflowsState, - { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true, - }, - { - provide: TASK_CONFIGURATION, - useFactory: buildTasks, - multi: false, - }, - ], - }; - } -} diff --git a/frontend/src/app/shared/services/app-languages.service.ts b/frontend/src/app/shared/services/app-languages.service.ts index dd36f0ac8..697b58b91 100644 --- a/frontend/src/app/shared/services/app-languages.service.ts +++ b/frontend/src/app/shared/services/app-languages.service.ts @@ -56,7 +56,9 @@ export type UpdateAppLanguageDto = Readonly<{ falback?: ReadonlyArray<string>; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AppLanguagesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/apps.service.ts b/frontend/src/app/shared/services/apps.service.ts index fad552698..3347fbe92 100644 --- a/frontend/src/app/shared/services/apps.service.ts +++ b/frontend/src/app/shared/services/apps.service.ts @@ -157,7 +157,9 @@ export type UpdateAppDto = Readonly<{ description?: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AppsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/assets.service.ts b/frontend/src/app/shared/services/assets.service.ts index c0ba350fc..474bbe867 100644 --- a/frontend/src/app/shared/services/assets.service.ts +++ b/frontend/src/app/shared/services/assets.service.ts @@ -213,7 +213,9 @@ export type AssetsByQuery = Readonly<{ parentId?: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AssetsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/auth.service.ts b/frontend/src/app/shared/services/auth.service.ts index 6a27789e8..922e3cfc4 100644 --- a/frontend/src/app/shared/services/auth.service.ts +++ b/frontend/src/app/shared/services/auth.service.ts @@ -74,7 +74,9 @@ export class Profile { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AuthService { private readonly userManager!: UserManager; private readonly user$ = new ReplaySubject<Profile | null>(1); diff --git a/frontend/src/app/shared/services/autosave.service.ts b/frontend/src/app/shared/services/autosave.service.ts index b4179689d..6fb14281d 100644 --- a/frontend/src/app/shared/services/autosave.service.ts +++ b/frontend/src/app/shared/services/autosave.service.ts @@ -10,7 +10,9 @@ import { LocalStoreService, Version } from '@app/framework'; export declare type AutoSaveKey = { schemaId: string; schemaVersion: Version; contentId?: string }; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AutoSaveService { constructor( private readonly localStore: LocalStoreService, diff --git a/frontend/src/app/shared/services/backups.service.ts b/frontend/src/app/shared/services/backups.service.ts index 83d6c10e1..f3fea0507 100644 --- a/frontend/src/app/shared/services/backups.service.ts +++ b/frontend/src/app/shared/services/backups.service.ts @@ -67,7 +67,9 @@ export type StartRestoreDto = Readonly<{ newAppName?: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class BackupsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/clients.service.ts b/frontend/src/app/shared/services/clients.service.ts index ab2eb335b..c04132f32 100644 --- a/frontend/src/app/shared/services/clients.service.ts +++ b/frontend/src/app/shared/services/clients.service.ts @@ -70,7 +70,9 @@ export type UpdateClientDto = Readonly<{ apiCallsLimit?: number; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ClientsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/contents.service.ts b/frontend/src/app/shared/services/contents.service.ts index 41b54c2e1..3ae68866e 100644 --- a/frontend/src/app/shared/services/contents.service.ts +++ b/frontend/src/app/shared/services/contents.service.ts @@ -10,7 +10,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiUrlConfig, DateTime, ErrorDto, hasAnyLink, HTTP, mapVersioned, pretifyError, Resource, ResourceLinks, Version, Versioned } from '@app/framework'; -import { StatusInfo } from './../state/contents.state'; +import { StatusInfo } from '../state/contents.state'; import { Query, sanitize } from './query'; import { parseField, RootFieldDto } from './schemas.service'; @@ -204,7 +204,9 @@ export type ContentsByQuery = Readonly<{ type FullQuery = ContentsByIds | ContentsBySchedule | ContentsByReferences | ContentsByReferencing; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ContentsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/contributors.service.ts b/frontend/src/app/shared/services/contributors.service.ts index c2d0aa663..e620e68f0 100644 --- a/frontend/src/app/shared/services/contributors.service.ts +++ b/frontend/src/app/shared/services/contributors.service.ts @@ -10,9 +10,12 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ApiUrlConfig, HTTP, mapVersioned, pretifyError, Resource, Version } from '@app/framework'; import { AssignContributorDto, ContributorsDto, parseContributors } from './shared'; + export * from './shared'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ContributorsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/help.service.ts b/frontend/src/app/shared/services/help.service.ts index 1bc0c0d7c..3b09fcf13 100644 --- a/frontend/src/app/shared/services/help.service.ts +++ b/frontend/src/app/shared/services/help.service.ts @@ -27,7 +27,9 @@ export interface SDKEntry { logo: string; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class HelpService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/history.service.ts b/frontend/src/app/shared/services/history.service.ts index 883ee3b4a..dfcbb7f3e 100644 --- a/frontend/src/app/shared/services/history.service.ts +++ b/frontend/src/app/shared/services/history.service.ts @@ -70,7 +70,9 @@ export function formatHistoryMessage(message: string, users: UsersProviderServic return from(format(message)); } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class HistoryService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/languages.service.ts b/frontend/src/app/shared/services/languages.service.ts index 7ead961e8..4ff2d7c31 100644 --- a/frontend/src/app/shared/services/languages.service.ts +++ b/frontend/src/app/shared/services/languages.service.ts @@ -19,7 +19,9 @@ export class LanguageDto { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class LanguagesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/news.service.ts b/frontend/src/app/shared/services/news.service.ts index 17a26e3c2..fa8706c1e 100644 --- a/frontend/src/app/shared/services/news.service.ts +++ b/frontend/src/app/shared/services/news.service.ts @@ -26,7 +26,9 @@ export type FeaturesDto = Readonly<{ version: number; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class NewsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/plans.service.ts b/frontend/src/app/shared/services/plans.service.ts index f04757227..ffb77599e 100644 --- a/frontend/src/app/shared/services/plans.service.ts +++ b/frontend/src/app/shared/services/plans.service.ts @@ -10,9 +10,12 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ApiUrlConfig, HTTP, mapVersioned, pretifyError, Version, Versioned } from '@app/framework'; import { ChangePlanDto, parsePlans, PlanChangedDto, PlansDto } from './shared'; + export * from './shared'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class PlansService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/roles.service.ts b/frontend/src/app/shared/services/roles.service.ts index 08d2584e2..b7d23d49e 100644 --- a/frontend/src/app/shared/services/roles.service.ts +++ b/frontend/src/app/shared/services/roles.service.ts @@ -54,7 +54,9 @@ export type UpdateRoleDto = Readonly<{ properties: {}; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RolesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/rules.service.ts b/frontend/src/app/shared/services/rules.service.ts index e510b62af..3929e9c1c 100644 --- a/frontend/src/app/shared/services/rules.service.ts +++ b/frontend/src/app/shared/services/rules.service.ts @@ -259,7 +259,9 @@ export type RuleTrigger = Readonly<{ [key: string]: any; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RulesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/schemas.service.ts b/frontend/src/app/shared/services/schemas.service.ts index 056a86fb3..22bcf52cc 100644 --- a/frontend/src/app/shared/services/schemas.service.ts +++ b/frontend/src/app/shared/services/schemas.service.ts @@ -502,7 +502,9 @@ export type UpdateSchemaDto = Readonly<{ tags?: ReadonlyArray<string>; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class SchemasService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/schemas.spec.ts b/frontend/src/app/shared/services/schemas.spec.ts index c3d4c4070..995b46af4 100644 --- a/frontend/src/app/shared/services/schemas.spec.ts +++ b/frontend/src/app/shared/services/schemas.spec.ts @@ -6,7 +6,7 @@ */ import { createProperties, META_FIELDS, SchemaPropertiesDto } from '@app/shared/internal'; -import { TestValues } from './../state/_test-helpers'; +import { TestValues } from '../state/_test-helpers'; const { createField, diff --git a/frontend/src/app/shared/services/search.service.ts b/frontend/src/app/shared/services/search.service.ts index c7dab84c4..18abae193 100644 --- a/frontend/src/app/shared/services/search.service.ts +++ b/frontend/src/app/shared/services/search.service.ts @@ -27,7 +27,9 @@ export class SearchResultDto { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class SearchService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/stock-photo.service.ts b/frontend/src/app/shared/services/stock-photo.service.ts index 2a766f5ca..9fc01d6a4 100644 --- a/frontend/src/app/shared/services/stock-photo.service.ts +++ b/frontend/src/app/shared/services/stock-photo.service.ts @@ -20,7 +20,9 @@ export class StockPhotoDto { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class StockPhotoService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/teams.service.ts b/frontend/src/app/shared/services/teams.service.ts index d644bd25e..5e1f8d63a 100644 --- a/frontend/src/app/shared/services/teams.service.ts +++ b/frontend/src/app/shared/services/teams.service.ts @@ -47,7 +47,9 @@ export type UpdateTeamDto = Readonly<{ name?: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TeamsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/templates.service.ts b/frontend/src/app/shared/services/templates.service.ts index 8009c6ae4..61a323ba6 100644 --- a/frontend/src/app/shared/services/templates.service.ts +++ b/frontend/src/app/shared/services/templates.service.ts @@ -39,7 +39,9 @@ export type TemplatesDto = Readonly<{ items: ReadonlyArray<TemplateDto>; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TemplatesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/translations.service.ts b/frontend/src/app/shared/services/translations.service.ts index 3cfc0d911..d70c5b9eb 100644 --- a/frontend/src/app/shared/services/translations.service.ts +++ b/frontend/src/app/shared/services/translations.service.ts @@ -35,7 +35,9 @@ export type TranslateDto = Readonly<{ prompt: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TranslationsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/ui.service.ts b/frontend/src/app/shared/services/ui.service.ts index f5b97fc90..0d9d45973 100644 --- a/frontend/src/app/shared/services/ui.service.ts +++ b/frontend/src/app/shared/services/ui.service.ts @@ -16,7 +16,9 @@ export type UISettingsDto = Readonly<{ canCreateApps?: boolean; }> & Record<string, any>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class UIService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/usages.service.ts b/frontend/src/app/shared/services/usages.service.ts index 33fc66c8d..b4eada00a 100644 --- a/frontend/src/app/shared/services/usages.service.ts +++ b/frontend/src/app/shared/services/usages.service.ts @@ -53,7 +53,9 @@ export class CurrentStorageDto { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class UsagesService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/users-provider.service.ts b/frontend/src/app/shared/services/users-provider.service.ts index 69948aec1..e4aa71402 100644 --- a/frontend/src/app/shared/services/users-provider.service.ts +++ b/frontend/src/app/shared/services/users-provider.service.ts @@ -10,7 +10,9 @@ import { catchError, map, Observable, of, share, shareReplay } from 'rxjs'; import { AuthService } from './auth.service'; import { UserDto, UsersService } from './users.service'; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class UsersProviderService { private readonly caches: { [id: string]: Observable<UserDto> } = {}; diff --git a/frontend/src/app/shared/services/users.service.ts b/frontend/src/app/shared/services/users.service.ts index 298d3f6a9..d91f819f1 100644 --- a/frontend/src/app/shared/services/users.service.ts +++ b/frontend/src/app/shared/services/users.service.ts @@ -24,7 +24,9 @@ export type UpdateProfileDto = Readonly<{ answers: { [question: string]: string }; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class UsersService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/services/workflows.service.ts b/frontend/src/app/shared/services/workflows.service.ts index 2add7f69d..f4a6e3055 100644 --- a/frontend/src/app/shared/services/workflows.service.ts +++ b/frontend/src/app/shared/services/workflows.service.ts @@ -255,7 +255,9 @@ export type CreateWorkflowDto = Readonly<{ name: string; }>; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class WorkflowsService { constructor( private readonly http: HttpClient, diff --git a/frontend/src/app/shared/state/_test-helpers.ts b/frontend/src/app/shared/state/_test-helpers.ts index 0335d9035..f8b7a9dda 100644 --- a/frontend/src/app/shared/state/_test-helpers.ts +++ b/frontend/src/app/shared/state/_test-helpers.ts @@ -7,7 +7,7 @@ import { of } from 'rxjs'; import { Mock } from 'typemoq'; -import { AppsState, AuthService, DateTime, FieldPropertiesDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDto, SchemaPropertiesDto, TeamsState, Version } from './../'; +import { AppsState, AuthService, DateTime, FieldPropertiesDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDto, SchemaPropertiesDto, TeamsState, Version } from '../'; const app = 'my-app'; const creation = DateTime.today().addDays(-2); diff --git a/frontend/src/app/shared/state/apps.forms.ts b/frontend/src/app/shared/state/apps.forms.ts index 475829e48..7dd57ffa0 100644 --- a/frontend/src/app/shared/state/apps.forms.ts +++ b/frontend/src/app/shared/state/apps.forms.ts @@ -9,7 +9,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, TemplatedFormArray, ValidatorsEx } from '@app/framework'; -import { AppDto, AppSettingsDto, CreateAppDto, TransferToTeamDto, UpdateAppDto, UpdateAppSettingsDto } from './../services/apps.service'; +import { AppDto, AppSettingsDto, CreateAppDto, TransferToTeamDto, UpdateAppDto, UpdateAppSettingsDto } from '../services/apps.service'; export class CreateAppForm extends Form<ExtendedFormGroup, CreateAppDto> { constructor() { diff --git a/frontend/src/app/shared/state/apps.state.spec.ts b/frontend/src/app/shared/state/apps.state.spec.ts index fec5f4e7e..9bbc504e1 100644 --- a/frontend/src/app/shared/state/apps.state.spec.ts +++ b/frontend/src/app/shared/state/apps.state.spec.ts @@ -8,7 +8,7 @@ import { firstValueFrom, of, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { AppsService, AppsState, DialogService } from '@app/shared/internal'; -import { createApp, createAppSettings } from './../services/apps.service.spec'; +import { createApp, createAppSettings } from '../services/apps.service.spec'; describe('AppsState', () => { const app1 = createApp(1); diff --git a/frontend/src/app/shared/state/apps.state.ts b/frontend/src/app/shared/state/apps.state.ts index 39d29f5f3..1c0f81a30 100644 --- a/frontend/src/app/shared/state/apps.state.ts +++ b/frontend/src/app/shared/state/apps.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { debug, DialogService, shareSubscribed, State, Types } from '@app/framework'; -import { AppDto, AppSettingsDto, AppsService, CreateAppDto, UpdateAppDto, UpdateAppSettingsDto } from './../services/apps.service'; +import { AppDto, AppSettingsDto, AppsService, CreateAppDto, UpdateAppDto, UpdateAppSettingsDto } from '../services/apps.service'; interface Snapshot { // All apps, loaded once. @@ -22,7 +22,9 @@ interface Snapshot { selectedSettings: AppSettingsDto | null; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AppsState extends State<Snapshot> { public apps = this.project(x => x.apps); diff --git a/frontend/src/app/shared/state/asset-scripts.state.spec.ts b/frontend/src/app/shared/state/asset-scripts.state.spec.ts index 353829740..da36baa3b 100644 --- a/frontend/src/app/shared/state/asset-scripts.state.spec.ts +++ b/frontend/src/app/shared/state/asset-scripts.state.spec.ts @@ -8,8 +8,8 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, versioned } from '@app/shared/internal'; -import { AppsService, AssetScriptsPayload } from './../services/apps.service'; -import { createAssetScripts } from './../services/apps.service.spec'; +import { AppsService, AssetScriptsPayload } from '../services/apps.service'; +import { createAssetScripts } from '../services/apps.service.spec'; import { TestValues } from './_test-helpers'; import { AssetScriptsState } from './asset-scripts.state'; diff --git a/frontend/src/app/shared/state/asset-scripts.state.ts b/frontend/src/app/shared/state/asset-scripts.state.ts index 67a7485b5..3e8ef5c18 100644 --- a/frontend/src/app/shared/state/asset-scripts.state.ts +++ b/frontend/src/app/shared/state/asset-scripts.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, Resource, shareSubscribed, State, Version } from '@app/framework'; -import { AppsService, AssetScripts, AssetScriptsPayload } from './../services/apps.service'; +import { AppsService, AssetScripts, AssetScriptsPayload } from '../services/apps.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -26,7 +26,9 @@ interface Snapshot extends LoadingState { resource: Resource; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AssetScriptsState extends State<Snapshot> { public scripts = this.project(x => x.scripts); diff --git a/frontend/src/app/shared/state/asset-uploader.state.spec.ts b/frontend/src/app/shared/state/asset-uploader.state.spec.ts index 750dc38ac..4bed6d611 100644 --- a/frontend/src/app/shared/state/asset-uploader.state.spec.ts +++ b/frontend/src/app/shared/state/asset-uploader.state.spec.ts @@ -8,7 +8,7 @@ import { lastValueFrom, NEVER, Observable, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, Mock } from 'typemoq'; import { AssetsService, AssetUploaderState, DialogService } from '@app/shared/internal'; -import { createAsset } from './../services/assets.service.spec'; +import { createAsset } from '../services/assets.service.spec'; import { TestValues } from './_test-helpers'; describe('AssetUploaderState', () => { diff --git a/frontend/src/app/shared/state/asset-uploader.state.ts b/frontend/src/app/shared/state/asset-uploader.state.ts index b0d218b87..580ca9c56 100644 --- a/frontend/src/app/shared/state/asset-uploader.state.ts +++ b/frontend/src/app/shared/state/asset-uploader.state.ts @@ -8,7 +8,7 @@ import { Injectable } from '@angular/core'; import { Observable, shareReplay, Subject, takeUntil } from 'rxjs'; import { debug, DialogService, MathHelper, State, Types } from '@app/framework'; -import { AssetDto, AssetsService } from './../services/assets.service'; +import { AssetDto, AssetsService } from '../services/assets.service'; import { AppsState } from './apps.state'; export interface Upload { @@ -35,7 +35,9 @@ interface Snapshot { export class UploadCanceled {} -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AssetUploaderState extends State<Snapshot> { public uploads = this.project(x => x.uploads); diff --git a/frontend/src/app/shared/state/assets.forms.ts b/frontend/src/app/shared/state/assets.forms.ts index 256cac51a..d7b1a1e1e 100644 --- a/frontend/src/app/shared/state/assets.forms.ts +++ b/frontend/src/app/shared/state/assets.forms.ts @@ -8,7 +8,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import slugify from 'slugify'; import { ExtendedFormGroup, Form, Mutable, TemplatedFormArray, Types } from '@app/framework'; -import { AnnotateAssetDto, AssetDto, AssetFolderDto, MoveAssetItemDto, RenameAssetFolderDto, RenameAssetTagDto } from './../services/assets.service'; +import { AnnotateAssetDto, AssetDto, AssetFolderDto, MoveAssetItemDto, RenameAssetFolderDto, RenameAssetTagDto } from '../services/assets.service'; export class AnnotateAssetForm extends Form<ExtendedFormGroup, AnnotateAssetDto, AssetDto> { public get metadata() { diff --git a/frontend/src/app/shared/state/assets.state.spec.ts b/frontend/src/app/shared/state/assets.state.spec.ts index 9a04f2109..e504cb1be 100644 --- a/frontend/src/app/shared/state/assets.state.spec.ts +++ b/frontend/src/app/shared/state/assets.state.spec.ts @@ -9,7 +9,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { ErrorDto } from '@app/framework'; import { AssetsService, AssetsState, DialogService, MathHelper, versioned } from '@app/shared/internal'; -import { createAsset, createAssetFolder } from './../services/assets.service.spec'; +import { createAsset, createAssetFolder } from '../services/assets.service.spec'; import { TestValues } from './_test-helpers'; describe('AssetsState', () => { diff --git a/frontend/src/app/shared/state/assets.state.ts b/frontend/src/app/shared/state/assets.state.ts index 38fe19164..03822b97d 100644 --- a/frontend/src/app/shared/state/assets.state.ts +++ b/frontend/src/app/shared/state/assets.state.ts @@ -9,8 +9,8 @@ import { Injectable } from '@angular/core'; import { EMPTY, forkJoin, Observable, of, throwError } from 'rxjs'; import { catchError, finalize, switchMap, tap } from 'rxjs/operators'; import { compareStrings, debug, DialogService, ErrorDto, getPagingInfo, ListState, MathHelper, shareSubscribed, State, Types } from '@app/framework'; -import { AnnotateAssetDto, AssetDto, AssetFolderDto, AssetFoldersDto, AssetsService, RenameAssetFolderDto } from './../services/assets.service'; -import { Query } from './../services/query'; +import { AnnotateAssetDto, AssetDto, AssetFolderDto, AssetFoldersDto, AssetsService, RenameAssetFolderDto } from '../services/assets.service'; +import { Query } from '../services/query'; import { AppsState } from './apps.state'; export type AssetPathItem = { id: string; folderName: string }; @@ -582,7 +582,9 @@ function getParent(path: ReadonlyArray<AssetPathItem>) { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class AssetsState extends AssetsStateBase { constructor( appsState: AppsState, assetsService: AssetsService, dialogs: DialogService, diff --git a/frontend/src/app/shared/state/backups.forms.ts b/frontend/src/app/shared/state/backups.forms.ts index 331815411..47c7b419e 100644 --- a/frontend/src/app/shared/state/backups.forms.ts +++ b/frontend/src/app/shared/state/backups.forms.ts @@ -9,7 +9,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, hasNoValue$, ValidatorsEx } from '@app/framework'; -import { StartRestoreDto } from './../services/backups.service'; +import { StartRestoreDto } from '../services/backups.service'; export class RestoreForm extends Form<ExtendedFormGroup, StartRestoreDto> { public get url() { diff --git a/frontend/src/app/shared/state/backups.state.spec.ts b/frontend/src/app/shared/state/backups.state.spec.ts index f86f34b47..573acbe75 100644 --- a/frontend/src/app/shared/state/backups.state.spec.ts +++ b/frontend/src/app/shared/state/backups.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { BackupsService, BackupsState, DialogService } from '@app/shared/internal'; -import { createBackup } from './../services/backups.service.spec'; +import { createBackup } from '../services/backups.service.spec'; import { TestValues } from './_test-helpers'; describe('BackupsState', () => { diff --git a/frontend/src/app/shared/state/backups.state.ts b/frontend/src/app/shared/state/backups.state.ts index e417acc64..3adc277f0 100644 --- a/frontend/src/app/shared/state/backups.state.ts +++ b/frontend/src/app/shared/state/backups.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State } from '@app/framework'; -import { BackupDto, BackupsService } from './../services/backups.service'; +import { BackupDto, BackupsService } from '../services/backups.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -20,7 +20,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class BackupsState extends State<Snapshot> { public backups = this.project(x => x.backups); diff --git a/frontend/src/app/shared/state/clients.forms.ts b/frontend/src/app/shared/state/clients.forms.ts index 5b5bb2b5a..5bdab85b5 100644 --- a/frontend/src/app/shared/state/clients.forms.ts +++ b/frontend/src/app/shared/state/clients.forms.ts @@ -9,7 +9,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, hasNoValue$, ValidatorsEx } from '@app/framework'; -import { ClientDto, CreateClientDto, UpdateClientDto } from './../services/clients.service'; +import { ClientDto, CreateClientDto, UpdateClientDto } from '../services/clients.service'; export class RenameClientForm extends Form<ExtendedFormGroup, UpdateClientDto, ClientDto> { constructor() { diff --git a/frontend/src/app/shared/state/clients.state.spec.ts b/frontend/src/app/shared/state/clients.state.spec.ts index 6cc7d9dee..d4196e4fd 100644 --- a/frontend/src/app/shared/state/clients.state.spec.ts +++ b/frontend/src/app/shared/state/clients.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { ClientsPayload, ClientsService, ClientsState, DialogService, versioned } from '@app/shared/internal'; -import { createClients } from './../services/clients.service.spec'; +import { createClients } from '../services/clients.service.spec'; import { TestValues } from './_test-helpers'; describe('ClientsState', () => { diff --git a/frontend/src/app/shared/state/clients.state.ts b/frontend/src/app/shared/state/clients.state.ts index 0a791bebb..4809bdb1c 100644 --- a/frontend/src/app/shared/state/clients.state.ts +++ b/frontend/src/app/shared/state/clients.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State, Version } from '@app/framework'; -import { ClientDto, ClientsPayload, ClientsService, CreateClientDto, UpdateClientDto } from './../services/clients.service'; +import { ClientDto, ClientsPayload, ClientsService, CreateClientDto, UpdateClientDto } from '../services/clients.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -23,7 +23,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ClientsState extends State<Snapshot> { public clients = this.project(x => x.clients); diff --git a/frontend/src/app/shared/state/contents.form-rules.ts b/frontend/src/app/shared/state/contents.form-rules.ts index 807613993..e67999066 100644 --- a/frontend/src/app/shared/state/contents.form-rules.ts +++ b/frontend/src/app/shared/state/contents.form-rules.ts @@ -9,7 +9,7 @@ /* eslint-disable no-useless-return */ import { Types } from '@app/framework'; -import { FieldRule, SchemaDto } from './../services/schemas.service'; +import { FieldRule, SchemaDto } from '../services/schemas.service'; export type RuleContext = { data: any; user?: any }; export type RuleForm = { fieldPath: string }; diff --git a/frontend/src/app/shared/state/contents.forms-helpers.ts b/frontend/src/app/shared/state/contents.forms-helpers.ts index a0b3d181c..af10a5ea7 100644 --- a/frontend/src/app/shared/state/contents.forms-helpers.ts +++ b/frontend/src/app/shared/state/contents.forms-helpers.ts @@ -11,10 +11,10 @@ import { AbstractControl, ValidatorFn } from '@angular/forms'; import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { isValidValue, Language } from './../internal'; -import { AppLanguageDto } from './../services/app-languages.service'; -import { FieldDto, RootFieldDto, SchemaDto } from './../services/schemas.service'; -import { fieldInvariant } from './../services/schemas.types'; +import { isValidValue, Language } from '../internal'; +import { AppLanguageDto } from '../services/app-languages.service'; +import { FieldDto, RootFieldDto, SchemaDto } from '../services/schemas.service'; +import { fieldInvariant } from '../services/schemas.types'; import { CompiledRules, RuleContext, RulesProvider } from './contents.form-rules'; export type TranslationStatus = { [language: string]: number }; diff --git a/frontend/src/app/shared/state/contents.forms.spec.ts b/frontend/src/app/shared/state/contents.forms.spec.ts index 24b5877bc..3ca23b0ee 100644 --- a/frontend/src/app/shared/state/contents.forms.spec.ts +++ b/frontend/src/app/shared/state/contents.forms.spec.ts @@ -10,7 +10,7 @@ import { AbstractControl, UntypedFormArray } from '@angular/forms'; import { MathHelper } from '@app/framework'; import { AppLanguageDto, createProperties, EditContentForm, getContentValue, HtmlValue, LanguageDto, RootFieldDto } from '@app/shared/internal'; -import { FieldRule, SchemaDto } from './../services/schemas.service'; +import { FieldRule, SchemaDto } from '../services/schemas.service'; import { TestValues } from './_test-helpers'; import { ComponentForm, FieldArrayForm } from './contents.forms'; import { contentsTranslationStatus, contentTranslationStatus, fieldTranslationStatus, PartitionConfig } from './contents.forms-helpers'; diff --git a/frontend/src/app/shared/state/contents.forms.ts b/frontend/src/app/shared/state/contents.forms.ts index 022c87f01..a599cd17a 100644 --- a/frontend/src/app/shared/state/contents.forms.ts +++ b/frontend/src/app/shared/state/contents.forms.ts @@ -10,10 +10,10 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { debounceTimeSafe, ExtendedFormGroup, Form, FormArrayTemplate, TemplatedFormArray, Types, value$ } from '@app/framework'; import { FormGroupTemplate, TemplatedFormGroup } from '@app/framework/angular/forms/templated-form-group'; -import { AppLanguageDto } from './../services/app-languages.service'; -import { LanguageDto } from './../services/languages.service'; -import { FieldDto, RootFieldDto, SchemaDto, TableField } from './../services/schemas.service'; -import { ComponentFieldPropertiesDto, fieldInvariant } from './../services/schemas.types'; +import { AppLanguageDto } from '../services/app-languages.service'; +import { LanguageDto } from '../services/languages.service'; +import { FieldDto, RootFieldDto, SchemaDto, TableField } from '../services/schemas.service'; +import { ComponentFieldPropertiesDto, fieldInvariant } from '../services/schemas.types'; import { ComponentRulesProvider, RootRulesProvider, RulesProvider } from './contents.form-rules'; import { AbstractContentForm, AbstractContentFormState, contentTranslationStatus, FieldSection, fieldTranslationStatus, FormGlobals, groupFields, PartitionConfig } from './contents.forms-helpers'; import { FieldDefaultValue, FieldsValidators } from './contents.forms.visitors'; diff --git a/frontend/src/app/shared/state/contents.forms.visitors.ts b/frontend/src/app/shared/state/contents.forms.visitors.ts index d3d2ae047..61e394646 100644 --- a/frontend/src/app/shared/state/contents.forms.visitors.ts +++ b/frontend/src/app/shared/state/contents.forms.visitors.ts @@ -7,10 +7,10 @@ import { ValidatorFn, Validators } from '@angular/forms'; import { DateTime, Types, ValidatorsEx } from '@app/framework'; -import { ContentDto, ContentReferencesValue } from './../services/contents.service'; -import { LanguageDto } from './../services/languages.service'; -import { FieldDto, RootFieldDto } from './../services/schemas.service'; -import { ArrayFieldPropertiesDto, AssetPreviewMode, AssetsFieldPropertiesDto, BooleanFieldPropertiesDto, ComponentFieldPropertiesDto, ComponentsFieldPropertiesDto, DateTimeFieldPropertiesDto, fieldInvariant, FieldPropertiesVisitor, GeolocationFieldPropertiesDto, JsonFieldPropertiesDto, NumberFieldPropertiesDto, ReferencesFieldPropertiesDto, StringFieldPropertiesDto, TagsFieldPropertiesDto, UIFieldPropertiesDto } from './../services/schemas.types'; +import { ContentDto, ContentReferencesValue } from '../services/contents.service'; +import { LanguageDto } from '../services/languages.service'; +import { FieldDto, RootFieldDto } from '../services/schemas.service'; +import { ArrayFieldPropertiesDto, AssetPreviewMode, AssetsFieldPropertiesDto, BooleanFieldPropertiesDto, ComponentFieldPropertiesDto, ComponentsFieldPropertiesDto, DateTimeFieldPropertiesDto, fieldInvariant, FieldPropertiesVisitor, GeolocationFieldPropertiesDto, JsonFieldPropertiesDto, NumberFieldPropertiesDto, ReferencesFieldPropertiesDto, StringFieldPropertiesDto, TagsFieldPropertiesDto, UIFieldPropertiesDto } from '../services/schemas.types'; export class HtmlValue { constructor( diff --git a/frontend/src/app/shared/state/contents.state.ts b/frontend/src/app/shared/state/contents.state.ts index eacbd92bc..000ea305b 100644 --- a/frontend/src/app/shared/state/contents.state.ts +++ b/frontend/src/app/shared/state/contents.state.ts @@ -9,8 +9,8 @@ import { Injectable } from '@angular/core'; import { EMPTY, Observable, of } from 'rxjs'; import { catchError, finalize, map, switchMap, tap } from 'rxjs/operators'; import { debug, DialogService, ErrorDto, getPagingInfo, ListState, shareSubscribed, State, Types, Version, Versioned } from '@app/framework'; -import { BulkResultDto, BulkUpdateJobDto, ContentDto, ContentsDto, ContentsService } from './../services/contents.service'; -import { Query } from './../services/query'; +import { BulkResultDto, BulkUpdateJobDto, ContentDto, ContentsDto, ContentsService } from '../services/contents.service'; +import { Query } from '../services/query'; import { AppsState } from './apps.state'; import { SavedQuery } from './queries'; import { SchemasState } from './schemas.state'; @@ -449,7 +449,9 @@ function isReferrerError(error?: ErrorDto) { return error?.errorCode === 'OBJECT_REFERENCED'; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ContentsState extends ContentsStateBase { constructor(appsState: AppsState, contentsService: ContentsService, dialogs: DialogService, private readonly schemasState: SchemasState, diff --git a/frontend/src/app/shared/state/contributors.forms.ts b/frontend/src/app/shared/state/contributors.forms.ts index e2383ae64..4deade78b 100644 --- a/frontend/src/app/shared/state/contributors.forms.ts +++ b/frontend/src/app/shared/state/contributors.forms.ts @@ -8,8 +8,8 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { debounceTime, map, shareReplay } from 'rxjs/operators'; import { ExtendedFormGroup, Form, hasNoValue$, Types, value$ } from '@app/framework'; -import { AssignContributorDto } from './../services/contributors.service'; -import { UserDto } from './../services/users.service'; +import { AssignContributorDto } from '../services/shared'; +import { UserDto } from '../services/users.service'; export class AssignContributorForm extends Form<ExtendedFormGroup, AssignContributorDto> { public get user() { diff --git a/frontend/src/app/shared/state/contributors.state.spec.ts b/frontend/src/app/shared/state/contributors.state.spec.ts index 15a62acbf..95f509a10 100644 --- a/frontend/src/app/shared/state/contributors.state.spec.ts +++ b/frontend/src/app/shared/state/contributors.state.spec.ts @@ -9,7 +9,7 @@ import { catchError, EMPTY, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { ErrorDto } from '@app/framework'; import { ContributorDto, ContributorsPayload, ContributorsService, ContributorsState, DialogService, versioned } from '@app/shared/internal'; -import { createContributors } from './../services/contributors.service.spec'; +import { createContributors } from '../services/contributors.service.spec'; import { TestValues } from './_test-helpers'; describe('ContributorsState', () => { diff --git a/frontend/src/app/shared/state/contributors.state.ts b/frontend/src/app/shared/state/contributors.state.ts index 25b3ad3aa..cdefd3cbd 100644 --- a/frontend/src/app/shared/state/contributors.state.ts +++ b/frontend/src/app/shared/state/contributors.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { EMPTY, Observable, throwError } from 'rxjs'; import { catchError, finalize, tap } from 'rxjs/operators'; import { debug, DialogService, ErrorDto, getPagingInfo, ListState, shareMapSubscribed, shareSubscribed, State, Types, Version } from '@app/framework'; -import { AssignContributorDto, ContributorDto, ContributorsPayload, ContributorsService } from './../services/contributors.service'; +import { AssignContributorDto, ContributorDto, ContributorsPayload, ContributorsService } from '../services/contributors.service'; import { AppsState } from './apps.state'; interface Snapshot extends ListState<string> { @@ -26,7 +26,9 @@ interface Snapshot extends ListState<string> { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class ContributorsState extends State<Snapshot> { public contributors = this.project(x => x.contributors); diff --git a/frontend/src/app/shared/state/languages.forms.ts b/frontend/src/app/shared/state/languages.forms.ts index 5909fd3c0..574bb5b38 100644 --- a/frontend/src/app/shared/state/languages.forms.ts +++ b/frontend/src/app/shared/state/languages.forms.ts @@ -7,7 +7,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, value$ } from '@app/framework'; -import { AppLanguageDto, UpdateAppLanguageDto } from './../services/app-languages.service'; +import { AppLanguageDto, UpdateAppLanguageDto } from '../services/app-languages.service'; export class EditLanguageForm extends Form<ExtendedFormGroup, UpdateAppLanguageDto, AppLanguageDto> { public get isMaster() { diff --git a/frontend/src/app/shared/state/languages.state.spec.ts b/frontend/src/app/shared/state/languages.state.spec.ts index 1b5e9d509..60bdf8b7d 100644 --- a/frontend/src/app/shared/state/languages.state.spec.ts +++ b/frontend/src/app/shared/state/languages.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { AppLanguagesPayload, AppLanguagesService, DialogService, LanguageDto, LanguagesService, LanguagesState, versioned } from '@app/shared/internal'; -import { createLanguages } from './../services/app-languages.service.spec'; +import { createLanguages } from '../services/app-languages.service.spec'; import { TestValues } from './_test-helpers'; describe('LanguagesState', () => { diff --git a/frontend/src/app/shared/state/languages.state.ts b/frontend/src/app/shared/state/languages.state.ts index cd43da3ee..0ae52cc13 100644 --- a/frontend/src/app/shared/state/languages.state.ts +++ b/frontend/src/app/shared/state/languages.state.ts @@ -9,8 +9,8 @@ import { Injectable } from '@angular/core'; import { forkJoin, Observable } from 'rxjs'; import { finalize, map, shareReplay, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareMapSubscribed, shareSubscribed, State, Version } from '@app/framework'; -import { AppLanguageDto, AppLanguagesPayload, AppLanguagesService, UpdateAppLanguageDto } from './../services/app-languages.service'; -import { LanguageDto, LanguagesService } from './../services/languages.service'; +import { AppLanguageDto, AppLanguagesPayload, AppLanguagesService, UpdateAppLanguageDto } from '../services/app-languages.service'; +import { LanguageDto, LanguagesService } from '../services/languages.service'; import { AppsState } from './apps.state'; export interface SnapshotLanguage { @@ -37,11 +37,13 @@ interface Snapshot extends LoadingState { // The app version. version: Version; - // Inedicates if the user can add a language. + // Indicates if the user can add a language. canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class LanguagesState extends State<Snapshot> { private cachedLanguage$?: Observable<ReadonlyArray<LanguageDto>>; diff --git a/frontend/src/app/shared/state/plans.state.ts b/frontend/src/app/shared/state/plans.state.ts index ae34b9714..a26e63a3f 100644 --- a/frontend/src/app/shared/state/plans.state.ts +++ b/frontend/src/app/shared/state/plans.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State, Version } from '@app/framework'; -import { PlanDto, PlanLockedReason, PlansService, ReferralDto } from './../services/plans.service'; +import { PlanDto, PlanLockedReason, PlansService, ReferralDto } from '../services/plans.service'; import { AppsState } from './apps.state'; export interface PlanInfo { @@ -43,7 +43,9 @@ interface Snapshot extends LoadingState { version: Version; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class PlansState extends State<Snapshot> { public plans = this.project(x => x.plans); diff --git a/frontend/src/app/shared/state/queries.ts b/frontend/src/app/shared/state/queries.ts index 1b68f3e60..9c8f0806c 100644 --- a/frontend/src/app/shared/state/queries.ts +++ b/frontend/src/app/shared/state/queries.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; import { compareStrings, Types } from '@app/framework'; -import { deserializeQuery, equalsQuery, Query } from './../services/query'; +import { deserializeQuery, equalsQuery, Query } from '../services/query'; import { UIState } from './ui.state'; export interface SavedQuery { diff --git a/frontend/src/app/shared/state/resolvers.spec.ts b/frontend/src/app/shared/state/resolvers.spec.ts index 5d265f273..3f21da6d9 100644 --- a/frontend/src/app/shared/state/resolvers.spec.ts +++ b/frontend/src/app/shared/state/resolvers.spec.ts @@ -9,8 +9,8 @@ import { TestBed } from '@angular/core/testing'; import { firstValueFrom, of, throwError } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { UIOptions } from '@app/framework'; -import { ContentsService } from './../services/contents.service'; -import { createContent } from './../services/contents.service.spec'; +import { ContentsService } from '../services/contents.service'; +import { createContent } from '../services/contents.service.spec'; import { TestValues } from './_test-helpers'; import { ResolveContents } from './resolvers'; diff --git a/frontend/src/app/shared/state/resolvers.ts b/frontend/src/app/shared/state/resolvers.ts index 6093fa60f..3633c4f96 100644 --- a/frontend/src/app/shared/state/resolvers.ts +++ b/frontend/src/app/shared/state/resolvers.ts @@ -8,8 +8,8 @@ import { inject, Injectable } from '@angular/core'; import { from, Observable, of, shareReplay } from 'rxjs'; import { UIOptions } from '@app/framework'; -import { AssetDto, AssetsDto, AssetsService } from './../services/assets.service'; -import { ContentDto, ContentsDto, ContentsService } from './../services/contents.service'; +import { AssetDto, AssetsDto, AssetsService } from '../services/assets.service'; +import { ContentDto, ContentsDto, ContentsService } from '../services/contents.service'; import { AppsState } from './apps.state'; abstract class ResolverBase<T extends { id: string }, TResult extends { items: ReadonlyArray<T> }> { diff --git a/frontend/src/app/shared/state/roles.forms.ts b/frontend/src/app/shared/state/roles.forms.ts index f3cb6f085..82b20839c 100644 --- a/frontend/src/app/shared/state/roles.forms.ts +++ b/frontend/src/app/shared/state/roles.forms.ts @@ -7,7 +7,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, hasNoValue$, TemplatedFormArray } from '@app/framework'; -import { CreateRoleDto, RoleDto, UpdateRoleDto } from './../services/roles.service'; +import { CreateRoleDto, RoleDto, UpdateRoleDto } from '../services/roles.service'; export class EditRoleForm extends Form<TemplatedFormArray, UpdateRoleDto, RoleDto> { public get controls() { diff --git a/frontend/src/app/shared/state/roles.state.spec.ts b/frontend/src/app/shared/state/roles.state.spec.ts index 5908824c8..fba9a1937 100644 --- a/frontend/src/app/shared/state/roles.state.spec.ts +++ b/frontend/src/app/shared/state/roles.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, RolesPayload, RolesService, RolesState, versioned } from '@app/shared/internal'; -import { createRoles } from './../services/roles.service.spec'; +import { createRoles } from '../services/roles.service.spec'; import { TestValues } from './_test-helpers'; describe('RolesState', () => { diff --git a/frontend/src/app/shared/state/roles.state.ts b/frontend/src/app/shared/state/roles.state.ts index b87916288..fac51736b 100644 --- a/frontend/src/app/shared/state/roles.state.ts +++ b/frontend/src/app/shared/state/roles.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State, Version } from '@app/framework'; -import { CreateRoleDto, RoleDto, RolesPayload, RolesService, UpdateRoleDto } from './../services/roles.service'; +import { CreateRoleDto, RoleDto, RolesPayload, RolesService, UpdateRoleDto } from '../services/roles.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -23,7 +23,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RolesState extends State<Snapshot> { public roles = this.project(x => x.roles); diff --git a/frontend/src/app/shared/state/rule-events.state.spec.ts b/frontend/src/app/shared/state/rule-events.state.spec.ts index 203443386..013e82d84 100644 --- a/frontend/src/app/shared/state/rule-events.state.spec.ts +++ b/frontend/src/app/shared/state/rule-events.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, RuleEventsState, RulesService } from '@app/shared/internal'; -import { createRuleEvent } from './../services/rules.service.spec'; +import { createRuleEvent } from '../services/rules.service.spec'; import { TestValues } from './_test-helpers'; describe('RuleEventsState', () => { diff --git a/frontend/src/app/shared/state/rule-events.state.ts b/frontend/src/app/shared/state/rule-events.state.ts index 1a5870458..62f1e1f78 100644 --- a/frontend/src/app/shared/state/rule-events.state.ts +++ b/frontend/src/app/shared/state/rule-events.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { EMPTY, Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, getPagingInfo, ListState, Resource, shareSubscribed, State } from '@app/framework'; -import { RuleEventDto, RulesService } from './../services/rules.service'; +import { RuleEventDto, RulesService } from '../services/rules.service'; import { AppsState } from './apps.state'; interface Snapshot extends ListState { @@ -26,7 +26,9 @@ interface Snapshot extends ListState { resource: Resource; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RuleEventsState extends State<Snapshot> { public ruleEvents = this.project(x => x.ruleEvents); diff --git a/frontend/src/app/shared/state/rule-simulator.state.spec.ts b/frontend/src/app/shared/state/rule-simulator.state.spec.ts index 47e740eff..60cae88c9 100644 --- a/frontend/src/app/shared/state/rule-simulator.state.spec.ts +++ b/frontend/src/app/shared/state/rule-simulator.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, RulesService } from '@app/shared/internal'; -import { createSimulatedRuleEvent } from './../services/rules.service.spec'; +import { createSimulatedRuleEvent } from '../services/rules.service.spec'; import { TestValues } from './_test-helpers'; import { RuleSimulatorState } from './rule-simulator.state'; diff --git a/frontend/src/app/shared/state/rule-simulator.state.ts b/frontend/src/app/shared/state/rule-simulator.state.ts index e65c3f2cb..4c3591458 100644 --- a/frontend/src/app/shared/state/rule-simulator.state.ts +++ b/frontend/src/app/shared/state/rule-simulator.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { EMPTY, Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, ListState, shareSubscribed, State } from '@app/framework'; -import { RulesService, SimulatedRuleEventDto } from './../services/rules.service'; +import { RulesService, SimulatedRuleEventDto } from '../services/rules.service'; import { AppsState } from './apps.state'; interface Snapshot extends ListState { @@ -26,7 +26,9 @@ interface Snapshot extends ListState { action?: any; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RuleSimulatorState extends State<Snapshot> { public simulatedRuleEvents = this.project(x => x.simulatedRuleEvents); diff --git a/frontend/src/app/shared/state/rules.forms.ts b/frontend/src/app/shared/state/rules.forms.ts index 6d465c69a..ff1bb082b 100644 --- a/frontend/src/app/shared/state/rules.forms.ts +++ b/frontend/src/app/shared/state/rules.forms.ts @@ -7,7 +7,7 @@ import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, TemplatedFormArray, ValidatorsEx } from '@app/framework'; -import { RuleElementDto, TriggerType } from './../services/rules.service'; +import { RuleElementDto, TriggerType } from '../services/rules.service'; export class ActionForm extends Form<any, UntypedFormGroup> { constructor(public readonly definition: RuleElementDto, diff --git a/frontend/src/app/shared/state/rules.state.spec.ts b/frontend/src/app/shared/state/rules.state.spec.ts index 44e92e825..aa6f5f08b 100644 --- a/frontend/src/app/shared/state/rules.state.spec.ts +++ b/frontend/src/app/shared/state/rules.state.spec.ts @@ -8,8 +8,8 @@ import { firstValueFrom, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, RulesService, versioned } from '@app/shared/internal'; -import { RuleDto } from './../services/rules.service'; -import { createRule } from './../services/rules.service.spec'; +import { RuleDto } from '../services/rules.service'; +import { createRule } from '../services/rules.service.spec'; import { TestValues } from './_test-helpers'; import { RulesState } from './rules.state'; diff --git a/frontend/src/app/shared/state/rules.state.ts b/frontend/src/app/shared/state/rules.state.ts index 0c5ee9b76..33f20ce65 100644 --- a/frontend/src/app/shared/state/rules.state.ts +++ b/frontend/src/app/shared/state/rules.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { finalize, map, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State } from '@app/framework'; -import { RuleDto, RulesService, UpsertRuleDto } from './../services/rules.service'; +import { RuleDto, RulesService, UpsertRuleDto } from '../services/rules.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -32,7 +32,9 @@ interface Snapshot extends LoadingState { canReadEvents?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class RulesState extends State<Snapshot> { public selectedRule = this.project(x => x.selectedRule); diff --git a/frontend/src/app/shared/state/schema-tag-source.ts b/frontend/src/app/shared/state/schema-tag-source.ts index 50a157578..73e019d56 100644 --- a/frontend/src/app/shared/state/schema-tag-source.ts +++ b/frontend/src/app/shared/state/schema-tag-source.ts @@ -8,7 +8,7 @@ import { Injectable } from '@angular/core'; import { map } from 'rxjs/operators'; import { TagConverter, TagValue } from '@app/framework'; -import { SchemaDto } from './../services/schemas.service'; +import { SchemaDto } from '../services/schemas.service'; import { SchemasState } from './schemas.state'; class SchemaConverter implements TagConverter { @@ -46,7 +46,9 @@ class SchemaConverter implements TagConverter { } } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class SchemaTagSource { public converter = this.schemasState.schemas.pipe( diff --git a/frontend/src/app/shared/state/schemas.forms.ts b/frontend/src/app/shared/state/schemas.forms.ts index f7c46e3d2..760958b7a 100644 --- a/frontend/src/app/shared/state/schemas.forms.ts +++ b/frontend/src/app/shared/state/schemas.forms.ts @@ -10,8 +10,8 @@ import { AbstractControl, UntypedFormControl, Validators } from '@angular/forms'; import { map } from 'rxjs/operators'; import { ExtendedFormGroup, Form, TemplatedFormArray, ValidatorsEx, value$ } from '@app/framework'; -import { AddFieldDto, CreateSchemaDto, FieldRule, SchemaDto, SchemaPropertiesDto, SynchronizeSchemaDto, UpdateSchemaDto } from './../services/schemas.service'; -import { createProperties, FieldPropertiesDto, FieldPropertiesVisitor } from './../services/schemas.types'; +import { AddFieldDto, CreateSchemaDto, FieldRule, SchemaDto, SchemaPropertiesDto, SynchronizeSchemaDto, UpdateSchemaDto } from '../services/schemas.service'; +import { createProperties, FieldPropertiesDto, FieldPropertiesVisitor } from '../services/schemas.types'; type CreateCategoryFormType = { name: string }; diff --git a/frontend/src/app/shared/state/schemas.state.spec.ts b/frontend/src/app/shared/state/schemas.state.spec.ts index 847269d5b..85807711b 100644 --- a/frontend/src/app/shared/state/schemas.state.spec.ts +++ b/frontend/src/app/shared/state/schemas.state.spec.ts @@ -8,7 +8,7 @@ import { firstValueFrom, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, SchemaDto, SchemasService, UpdateSchemaCategoryDto, versioned } from '@app/shared/internal'; -import { createSchema } from './../services/schemas.service.spec'; +import { createSchema } from '../services/schemas.service.spec'; import { TestValues } from './_test-helpers'; import { getCategoryTree, SchemasState } from './schemas.state'; diff --git a/frontend/src/app/shared/state/schemas.state.ts b/frontend/src/app/shared/state/schemas.state.ts index 025963e23..ac83fb5a5 100644 --- a/frontend/src/app/shared/state/schemas.state.ts +++ b/frontend/src/app/shared/state/schemas.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { EMPTY, forkJoin, Observable, of } from 'rxjs'; import { catchError, finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareMapSubscribed, shareSubscribed, State, Version } from '@app/framework'; -import { AddFieldDto, CreateSchemaDto, FieldDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDto, SchemasService, UpdateFieldDto, UpdateSchemaDto, UpdateUIFields } from './../services/schemas.service'; +import { AddFieldDto, CreateSchemaDto, FieldDto, FieldRule, NestedFieldDto, RootFieldDto, SchemaDto, SchemasService, UpdateFieldDto, UpdateSchemaDto, UpdateUIFields } from '../services/schemas.service'; import { AppsState } from './apps.state'; type AnyFieldDto = NestedFieldDto | RootFieldDto; @@ -28,7 +28,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class SchemasState extends State<Snapshot> { public selectedSchema = this.project(x => x.selectedSchema); diff --git a/frontend/src/app/shared/state/table-settings.spec.ts b/frontend/src/app/shared/state/table-settings.spec.ts index 3eb34cc26..e814e62d9 100644 --- a/frontend/src/app/shared/state/table-settings.spec.ts +++ b/frontend/src/app/shared/state/table-settings.spec.ts @@ -9,7 +9,7 @@ import { of } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DateTime, Version } from '@app/framework'; import { createProperties, FieldSizes, META_FIELDS, RootFieldDto, SchemaDto, TableSettings, UIState } from '@app/shared/internal'; -import { FieldWrappings } from './..'; +import { FieldWrappings } from '..'; describe('TableSettings', () => { let uiState: IMock<UIState>; diff --git a/frontend/src/app/shared/state/table-settings.ts b/frontend/src/app/shared/state/table-settings.ts index 838233715..74c0025ec 100644 --- a/frontend/src/app/shared/state/table-settings.ts +++ b/frontend/src/app/shared/state/table-settings.ts @@ -7,7 +7,7 @@ import { map, take } from 'rxjs/operators'; import { State, Types } from '@app/framework'; -import { META_FIELDS, SchemaDto, TableField } from './../services/schemas.service'; +import { META_FIELDS, SchemaDto, TableField } from '../services/schemas.service'; import { UIState } from './ui.state'; const META_FIELD_NAMES = Object.values(META_FIELDS).filter(x => x !== META_FIELDS.empty); diff --git a/frontend/src/app/shared/state/teams.forms.ts b/frontend/src/app/shared/state/teams.forms.ts index 58f5541ed..f12f6d7ef 100644 --- a/frontend/src/app/shared/state/teams.forms.ts +++ b/frontend/src/app/shared/state/teams.forms.ts @@ -9,7 +9,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form } from '@app/framework'; -import { CreateTeamDto, TeamDto, UpdateTeamDto } from './../services/teams.service'; +import { CreateTeamDto, TeamDto, UpdateTeamDto } from '../services/teams.service'; export class CreateTeamForm extends Form<ExtendedFormGroup, CreateTeamDto> { constructor() { diff --git a/frontend/src/app/shared/state/teams.state.spec.ts b/frontend/src/app/shared/state/teams.state.spec.ts index 33f4df9af..61f8cd6c3 100644 --- a/frontend/src/app/shared/state/teams.state.spec.ts +++ b/frontend/src/app/shared/state/teams.state.spec.ts @@ -8,7 +8,7 @@ import { firstValueFrom, of, throwError } from 'rxjs'; import { IMock, Mock, Times } from 'typemoq'; import { DialogService, TeamsService, TeamsState } from '@app/shared/internal'; -import { createTeam } from './../services/teams.service.spec'; +import { createTeam } from '../services/teams.service.spec'; describe('TeamsState', () => { const team1 = createTeam(1); diff --git a/frontend/src/app/shared/state/teams.state.ts b/frontend/src/app/shared/state/teams.state.ts index 92716018a..d4c207271 100644 --- a/frontend/src/app/shared/state/teams.state.ts +++ b/frontend/src/app/shared/state/teams.state.ts @@ -19,7 +19,9 @@ interface Snapshot { selectedTeam?: TeamDto | null; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TeamsState extends State<Snapshot> { public teams = this.project(x => x.teams); diff --git a/frontend/src/app/shared/state/template.state.spec.ts b/frontend/src/app/shared/state/template.state.spec.ts index 2e89ccecc..f626ac755 100644 --- a/frontend/src/app/shared/state/template.state.spec.ts +++ b/frontend/src/app/shared/state/template.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, TemplatesService, TemplatesState } from '@app/shared/internal'; -import { createTemplate } from './../services/templates.service.spec'; +import { createTemplate } from '../services/templates.service.spec'; describe('TemplatesState', () => { const template1 = createTemplate(12); diff --git a/frontend/src/app/shared/state/templates.state.ts b/frontend/src/app/shared/state/templates.state.ts index d9e983bb6..3b3677ce8 100644 --- a/frontend/src/app/shared/state/templates.state.ts +++ b/frontend/src/app/shared/state/templates.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State } from '@app/framework'; -import { TemplateDto, TemplatesService } from './../services/templates.service'; +import { TemplateDto, TemplatesService } from '../services/templates.service'; interface Snapshot extends LoadingState { // The current templates. @@ -19,7 +19,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TemplatesState extends State<Snapshot> { public templates = this.project(x => x.templates); diff --git a/frontend/src/app/shared/state/tour.state.ts b/frontend/src/app/shared/state/tour.state.ts index 2ef793de8..3612ef24d 100644 --- a/frontend/src/app/shared/state/tour.state.ts +++ b/frontend/src/app/shared/state/tour.state.ts @@ -30,7 +30,9 @@ interface Snapshot { status?: 'Ready' | 'Started' | 'Completed'; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class TourState extends State<Snapshot> { private readonly isDisabled = inject(UIOptions).value.hideOnboarding; diff --git a/frontend/src/app/shared/state/tour.tasks.ts b/frontend/src/app/shared/state/tour.tasks.ts index cb5893e06..038928472 100644 --- a/frontend/src/app/shared/state/tour.tasks.ts +++ b/frontend/src/app/shared/state/tour.tasks.ts @@ -8,7 +8,7 @@ import { inject, InjectionToken } from '@angular/core'; import { filter, Observable, take } from 'rxjs'; import { MessageBus, StepDefinition, waitForAnchor } from '@app/framework'; -import { ClientTourStated, QueryExecuted } from './../utils/messages'; +import { ClientTourStated, QueryExecuted } from '../utils/messages'; import { AppsState } from './apps.state'; import { AssetsState } from './assets.state'; import { ContentsState } from './contents.state'; diff --git a/frontend/src/app/shared/state/ui.state.ts b/frontend/src/app/shared/state/ui.state.ts index f04b956a5..ee0b1ac86 100644 --- a/frontend/src/app/shared/state/ui.state.ts +++ b/frontend/src/app/shared/state/ui.state.ts @@ -9,8 +9,8 @@ import { Injectable } from '@angular/core'; import { combineLatest } from 'rxjs'; import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators'; import { debug, hasAnyLink, shareSubscribed, State, Types } from '@app/framework'; -import { UIService } from './../services/ui.service'; -import { UsersService } from './../services/users.service'; +import { UIService } from '../services/ui.service'; +import { UsersService } from '../services/users.service'; type Settings = { canCreateApps?: boolean; canCreateTeams?: boolean; [key: string]: any }; @@ -40,7 +40,9 @@ interface Snapshot { appName?: string; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class UIState extends State<Snapshot> { public settings = this.project(mergeSettings); diff --git a/frontend/src/app/shared/state/workflows.forms.ts b/frontend/src/app/shared/state/workflows.forms.ts index a62baed38..568b35916 100644 --- a/frontend/src/app/shared/state/workflows.forms.ts +++ b/frontend/src/app/shared/state/workflows.forms.ts @@ -7,7 +7,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { ExtendedFormGroup, Form, hasNoValue$ } from '@app/framework'; -import { CreateWorkflowDto } from './../services/workflows.service'; +import { CreateWorkflowDto } from '../services/workflows.service'; export class AddWorkflowForm extends Form<ExtendedFormGroup, CreateWorkflowDto> { public get name() { diff --git a/frontend/src/app/shared/state/workflows.state.spec.ts b/frontend/src/app/shared/state/workflows.state.spec.ts index 722854742..e94ad0ac7 100644 --- a/frontend/src/app/shared/state/workflows.state.spec.ts +++ b/frontend/src/app/shared/state/workflows.state.spec.ts @@ -8,7 +8,7 @@ import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { DialogService, versioned, WorkflowsPayload, WorkflowsService, WorkflowsState } from '@app/shared/internal'; -import { createWorkflows } from './../services/workflows.service.spec'; +import { createWorkflows } from '../services/workflows.service.spec'; import { TestValues } from './_test-helpers'; describe('WorkflowsState', () => { diff --git a/frontend/src/app/shared/state/workflows.state.ts b/frontend/src/app/shared/state/workflows.state.ts index 19d2db909..d23e7e5ca 100644 --- a/frontend/src/app/shared/state/workflows.state.ts +++ b/frontend/src/app/shared/state/workflows.state.ts @@ -9,7 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { finalize, tap } from 'rxjs/operators'; import { debug, DialogService, LoadingState, shareSubscribed, State, Version } from '@app/framework'; -import { WorkflowDto, WorkflowsPayload, WorkflowsService } from './../services/workflows.service'; +import { WorkflowDto, WorkflowsPayload, WorkflowsService } from '../services/workflows.service'; import { AppsState } from './apps.state'; interface Snapshot extends LoadingState { @@ -26,7 +26,9 @@ interface Snapshot extends LoadingState { canCreate?: boolean; } -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class WorkflowsState extends State<Snapshot> { public workflows = this.project(x => x.workflows); diff --git a/frontend/src/app/shared/utils/editor-utils.spec.ts b/frontend/src/app/shared/utils/editor-utils.spec.ts index 00c4aa41f..8bdec4b8b 100644 --- a/frontend/src/app/shared/utils/editor-utils.spec.ts +++ b/frontend/src/app/shared/utils/editor-utils.spec.ts @@ -8,7 +8,7 @@ /* eslint-disable no-template-curly-in-string */ import { Version } from '@app/framework'; -import { AppSettingsDto } from './../services/apps.service'; +import { AppSettingsDto } from '../services/apps.service'; import { computeEditorUrl } from './editor-utils'; describe('EditorUtils', () => { diff --git a/frontend/src/app/shared/utils/editor-utils.ts b/frontend/src/app/shared/utils/editor-utils.ts index 505ffed2f..526eb787c 100644 --- a/frontend/src/app/shared/utils/editor-utils.ts +++ b/frontend/src/app/shared/utils/editor-utils.ts @@ -6,7 +6,7 @@ */ import { interpolate } from '@app/framework'; -import { AppSettingsDto } from './../services/apps.service'; +import { AppSettingsDto } from '../services/apps.service'; export function computeEditorUrl(url?: string | null, settings?: AppSettingsDto | null) { if (!url) { diff --git a/frontend/src/app/shell/declarations.ts b/frontend/src/app/shell/declarations.ts deleted file mode 100644 index 2536639fb..000000000 --- a/frontend/src/app/shell/declarations.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -export * from './pages/app/app-area.component'; -export * from './pages/app/left-menu.component'; -export * from './pages/forbidden/forbidden-page.component'; -export * from './pages/home/home-page.component'; -export * from './pages/internal/apps-menu.component'; -export * from './pages/internal/internal-area.component'; -export * from './pages/internal/feedback-menu.component'; -export * from './pages/internal/logo.component'; -export * from './pages/internal/notification-dropdown.component'; -export * from './pages/internal/notifications-menu.component'; -export * from './pages/internal/profile-menu.component'; -export * from './pages/internal/search-menu.component'; -export * from './pages/internal/teams-area.component'; -export * from './pages/login/login-page.component'; -export * from './pages/logout/logout-page.component'; -export * from './pages/not-found/not-found-page.component'; diff --git a/frontend/src/app/shell/index.ts b/frontend/src/app/shell/index.ts index 898be9a7c..2536639fb 100644 --- a/frontend/src/app/shell/index.ts +++ b/frontend/src/app/shell/index.ts @@ -5,5 +5,19 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -export * from './declarations'; -export * from './module'; +export * from './pages/app/app-area.component'; +export * from './pages/app/left-menu.component'; +export * from './pages/forbidden/forbidden-page.component'; +export * from './pages/home/home-page.component'; +export * from './pages/internal/apps-menu.component'; +export * from './pages/internal/internal-area.component'; +export * from './pages/internal/feedback-menu.component'; +export * from './pages/internal/logo.component'; +export * from './pages/internal/notification-dropdown.component'; +export * from './pages/internal/notifications-menu.component'; +export * from './pages/internal/profile-menu.component'; +export * from './pages/internal/search-menu.component'; +export * from './pages/internal/teams-area.component'; +export * from './pages/login/login-page.component'; +export * from './pages/logout/logout-page.component'; +export * from './pages/not-found/not-found-page.component'; diff --git a/frontend/src/app/shell/module.ts b/frontend/src/app/shell/module.ts deleted file mode 100644 index 53bc74fe7..000000000 --- a/frontend/src/app/shell/module.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { NgModule } from '@angular/core'; -import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AppAreaComponent, AppsMenuComponent, FeedbackMenuComponent, ForbiddenPageComponent, HomePageComponent, InternalAreaComponent, LeftMenuComponent, LoginPageComponent, LogoComponent, LogoutPageComponent, NotFoundPageComponent, NotificationDropdownComponent, NotificationsMenuComponent, ProfileMenuComponent, SearchMenuComponent, TeamsAreaComponent } from './declarations'; - -@NgModule({ - imports: [ - SqxFrameworkModule, - SqxSharedModule, - ], - exports: [ - AppAreaComponent, - HomePageComponent, - ForbiddenPageComponent, - InternalAreaComponent, - NotFoundPageComponent, - ], - declarations: [ - AppAreaComponent, - AppsMenuComponent, - FeedbackMenuComponent, - ForbiddenPageComponent, - HomePageComponent, - InternalAreaComponent, - LeftMenuComponent, - LoginPageComponent, - LogoComponent, - LogoutPageComponent, - NotFoundPageComponent, - NotificationDropdownComponent, - NotificationsMenuComponent, - ProfileMenuComponent, - SearchMenuComponent, - TeamsAreaComponent, - ], -}) -export class SqxShellModule { } diff --git a/frontend/src/app/shell/pages/app/app-area.component.ts b/frontend/src/app/shell/pages/app/app-area.component.ts index 117c69356..7061cf484 100644 --- a/frontend/src/app/shell/pages/app/app-area.component.ts +++ b/frontend/src/app/shell/pages/app/app-area.component.ts @@ -5,13 +5,25 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; -import { AppsState, defined } from '@app/shared'; +import { RouterOutlet } from '@angular/router'; +import { AppsState, defined, LayoutContainerDirective, TitleComponent } from '@app/shared'; +import { LeftMenuComponent } from './left-menu.component'; @Component({ + standalone: true, selector: 'sqx-app-area', styleUrls: ['./app-area.component.scss'], templateUrl: './app-area.component.html', + imports: [ + AsyncPipe, + LayoutContainerDirective, + LeftMenuComponent, + NgIf, + RouterOutlet, + TitleComponent, + ], }) export class AppAreaComponent { public selectedApp = this.appsState.selectedApp.pipe(defined()); diff --git a/frontend/src/app/shell/pages/app/left-menu.component.ts b/frontend/src/app/shell/pages/app/left-menu.component.ts index 85795bdfe..586598dbd 100644 --- a/frontend/src/app/shell/pages/app/left-menu.component.ts +++ b/frontend/src/app/shell/pages/app/left-menu.component.ts @@ -5,14 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { AppDto, Settings } from '@app/shared'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { AppDto, Settings, TourStepDirective, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-left-menu', styleUrls: ['./left-menu.component.scss'], templateUrl: './left-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + RouterLink, + RouterLinkActive, + TourStepDirective, + TranslatePipe, + ], }) export class LeftMenuComponent { @Input({ required: true }) diff --git a/frontend/src/app/shell/pages/forbidden/forbidden-page.component.ts b/frontend/src/app/shell/pages/forbidden/forbidden-page.component.ts index 0c340e9bf..7179dcac6 100644 --- a/frontend/src/app/shell/pages/forbidden/forbidden-page.component.ts +++ b/frontend/src/app/shell/pages/forbidden/forbidden-page.component.ts @@ -7,11 +7,17 @@ import { Location } from '@angular/common'; import { Component } from '@angular/core'; +import { TitleComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-forbidden-page', styleUrls: ['./forbidden-page.component.scss'], templateUrl: './forbidden-page.component.html', + imports: [ + TitleComponent, + TranslatePipe, + ], }) export class ForbiddenPageComponent { constructor( diff --git a/frontend/src/app/shell/pages/home/home-page.component.ts b/frontend/src/app/shell/pages/home/home-page.component.ts index f31d5517f..f6970a69a 100644 --- a/frontend/src/app/shell/pages/home/home-page.component.ts +++ b/frontend/src/app/shell/pages/home/home-page.component.ts @@ -5,15 +5,21 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Location } from '@angular/common'; +import { Location, NgIf } from '@angular/common'; import { Component, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { AuthService, UIOptions } from '@app/shared'; +import { AuthService, FormHintComponent, TranslatePipe, UIOptions } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-home-page', styleUrls: ['./home-page.component.scss'], templateUrl: './home-page.component.html', + imports: [ + FormHintComponent, + NgIf, + TranslatePipe, + ], }) export class HomePageComponent { private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin; diff --git a/frontend/src/app/shell/pages/internal/apps-menu.component.ts b/frontend/src/app/shell/pages/internal/apps-menu.component.ts index bd93e9925..e7c6e47b8 100644 --- a/frontend/src/app/shell/pages/internal/apps-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/apps-menu.component.ts @@ -5,17 +5,32 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppDto, AppsState, DialogModel, ModalModel, TeamDto, TeamsState, Title, TitleService, UIState } from '@app/shared'; +import { AppDto, AppFormComponent, AppsState, DialogModel, DropdownMenuComponent, ModalDirective, ModalModel, ModalPlacementDirective, TeamDto, TeamFormComponent, TeamsState, Title, TitleService, TranslatePipe, UIState } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-apps-menu', styleUrls: ['./apps-menu.component.scss'], templateUrl: './apps-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AppFormComponent, + AsyncPipe, + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + RouterLink, + RouterLinkActive, + TeamFormComponent, + TranslatePipe, + ], }) export class AppsMenuComponent { public addAppDialog = new DialogModel(); diff --git a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts b/frontend/src/app/shell/pages/internal/feedback-menu.component.ts index 49c9c181c..9feaa0ffd 100644 --- a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/feedback-menu.component.ts @@ -5,15 +5,20 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit } from '@angular/core'; import markerSDK, { MarkerSdk } from '@marker.io/browser'; import { UIOptions } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-feedback-menu', styleUrls: ['./feedback-menu.component.scss'], templateUrl: './feedback-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + ], }) export class FeedbackMenuComponent implements OnInit, OnDestroy { private widget?: MarkerSdk; diff --git a/frontend/src/app/shell/pages/internal/internal-area.component.ts b/frontend/src/app/shell/pages/internal/internal-area.component.ts index 9615b2a2c..d90156053 100644 --- a/frontend/src/app/shell/pages/internal/internal-area.component.ts +++ b/frontend/src/app/shell/pages/internal/internal-area.component.ts @@ -5,14 +5,36 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { Component, inject, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink, RouterOutlet } from '@angular/router'; import { DialogService, LoadingService, Notification, Subscriptions, UIOptions } from '@app/shared'; +import { AssetUploaderComponent } from '@app/shared/components/assets/asset-uploader.component'; +import { AppsMenuComponent } from './apps-menu.component'; +import { FeedbackMenuComponent } from './feedback-menu.component'; +import { LogoComponent } from './logo.component'; +import { NotificationsMenuComponent } from './notifications-menu.component'; +import { ProfileMenuComponent } from './profile-menu.component'; +import { SearchMenuComponent } from './search-menu.component'; @Component({ + standalone: true, selector: 'sqx-internal-area', styleUrls: ['./internal-area.component.scss'], templateUrl: './internal-area.component.html', + imports: [ + AppsMenuComponent, + AssetUploaderComponent, + AsyncPipe, + FeedbackMenuComponent, + LogoComponent, + NgIf, + NotificationsMenuComponent, + ProfileMenuComponent, + RouterLink, + RouterOutlet, + SearchMenuComponent, + ], }) export class InternalAreaComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shell/pages/internal/logo.component.ts b/frontend/src/app/shell/pages/internal/logo.component.ts index f1e1d5c33..e2f99cb1a 100644 --- a/frontend/src/app/shell/pages/internal/logo.component.ts +++ b/frontend/src/app/shell/pages/internal/logo.component.ts @@ -6,12 +6,17 @@ */ import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { LoaderComponent } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-logo', styleUrls: ['./logo.component.scss'], templateUrl: './logo.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + LoaderComponent, + ], }) export class LogoComponent { @Input({ transform: booleanAttribute }) diff --git a/frontend/src/app/shell/pages/internal/notification-dropdown.component.ts b/frontend/src/app/shell/pages/internal/notification-dropdown.component.ts index 3c6e1f239..27c05c83d 100644 --- a/frontend/src/app/shell/pages/internal/notification-dropdown.component.ts +++ b/frontend/src/app/shell/pages/internal/notification-dropdown.component.ts @@ -5,12 +5,14 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; -import { AuthService, CollaborationService, Comment, ModalModel, SharedArray, Subscriptions } from '@app/shared'; +import { AuthService, CollaborationService, Comment, CommentComponent, DropdownMenuComponent, ModalDirective, ModalModel, ModalPlacementDirective, SharedArray, Subscriptions, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-notification-dropdown', styleUrls: ['./notification-dropdown.component.scss'], templateUrl: './notification-dropdown.component.html', @@ -18,6 +20,16 @@ import { AuthService, CollaborationService, Comment, ModalModel, SharedArray, Su providers: [ CollaborationService, ], + imports: [ + AsyncPipe, + CommentComponent, + DropdownMenuComponent, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + TranslatePipe, + ], }) export class NotificationDropdownComponent implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shell/pages/internal/notifications-menu.component.ts b/frontend/src/app/shell/pages/internal/notifications-menu.component.ts index e129ec35a..490f4142f 100644 --- a/frontend/src/app/shell/pages/internal/notifications-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/notifications-menu.component.ts @@ -5,14 +5,22 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { AuthService, UIOptions } from '@app/shared'; +import { AuthService, NotifoComponent, UIOptions } from '@app/shared'; +import { NotificationDropdownComponent } from './notification-dropdown.component'; @Component({ + standalone: true, selector: 'sqx-notifications-menu', styleUrls: ['./notifications-menu.component.scss'], templateUrl: './notifications-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + NotificationDropdownComponent, + NotifoComponent, + ], }) export class NotificationsMenuComponent { public isNotifoConfigured = false; diff --git a/frontend/src/app/shell/pages/internal/profile-menu.component.ts b/frontend/src/app/shell/pages/internal/profile-menu.component.ts index af02800c5..7c8e0b6d1 100644 --- a/frontend/src/app/shell/pages/internal/profile-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/profile-menu.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; -import { ApiUrlConfig, AuthService, Cookies, ModalModel, StatefulComponent, Subscriptions, UILanguages, UIOptions, UIState } from '@app/shared'; +import { RouterLink } from '@angular/router'; +import { ApiUrlConfig, AuthService, Cookies, DropdownMenuComponent, ExternalLinkDirective, ModalDirective, ModalModel, ModalPlacementDirective, StatefulComponent, StopClickDirective, Subscriptions, TranslatePipe, UILanguages, UIOptions, UIState, UserIdPicturePipe } from '@app/shared'; interface State { // The display name of the user. @@ -26,10 +28,24 @@ interface State { } @Component({ + standalone: true, selector: 'sqx-profile-menu', styleUrls: ['./profile-menu.component.scss'], templateUrl: './profile-menu.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + DropdownMenuComponent, + ExternalLinkDirective, + ModalDirective, + ModalPlacementDirective, + NgFor, + NgIf, + RouterLink, + StopClickDirective, + TranslatePipe, + UserIdPicturePipe, + ], }) export class ProfileMenuComponent extends StatefulComponent<State> implements OnInit { private readonly subscriptions = new Subscriptions(); diff --git a/frontend/src/app/shell/pages/internal/search-menu.component.ts b/frontend/src/app/shell/pages/internal/search-menu.component.ts index 8c72a651b..711d5476f 100644 --- a/frontend/src/app/shell/pages/internal/search-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/search-menu.component.ts @@ -5,10 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { AsyncPipe, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Injectable, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { Observable, of } from 'rxjs'; -import { ApiUrlConfig, AppsState, AutocompleteComponent, AutocompleteSource, SearchResultDto, SearchService, Types } from '@app/shared/internal'; +import { ApiUrlConfig, AppsState, AutocompleteComponent, AutocompleteSource, SearchResultDto, SearchService, ShortcutComponent, ShortcutDirective, TooltipDirective, TranslatePipe, Types } from '@app/shared'; +import { AutocompleteComponent as AutocompleteComponent_1 } from '../../../framework/angular/forms/editors/autocomplete.component'; @Injectable() export class SearchSource implements AutocompleteSource { @@ -30,6 +33,7 @@ export class SearchSource implements AutocompleteSource { } @Component({ + standalone: true, selector: 'sqx-search-menu', styleUrls: ['./search-menu.component.scss'], templateUrl: './search-menu.component.html', @@ -37,6 +41,16 @@ export class SearchSource implements AutocompleteSource { SearchSource, ], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + AsyncPipe, + AutocompleteComponent_1, + FormsModule, + NgIf, + ShortcutComponent, + ShortcutDirective, + TooltipDirective, + TranslatePipe, + ], }) export class SearchMenuComponent { @ViewChild(AutocompleteComponent, { static: false }) diff --git a/frontend/src/app/shell/pages/internal/teams-area.component.ts b/frontend/src/app/shell/pages/internal/teams-area.component.ts index f521a5f7c..f4329bbe2 100644 --- a/frontend/src/app/shell/pages/internal/teams-area.component.ts +++ b/frontend/src/app/shell/pages/internal/teams-area.component.ts @@ -6,11 +6,16 @@ */ import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; @Component({ + standalone: true, selector: 'sqx-teams-area', styleUrls: ['./teams-area.component.scss'], templateUrl: './teams-area.component.html', + imports: [ + RouterOutlet, + ], }) export class TeamsAreaComponent { } diff --git a/frontend/src/app/shell/pages/login/login-page.component.ts b/frontend/src/app/shell/pages/login/login-page.component.ts index e08975eab..ddf1ebc37 100644 --- a/frontend/src/app/shell/pages/login/login-page.component.ts +++ b/frontend/src/app/shell/pages/login/login-page.component.ts @@ -10,6 +10,7 @@ import { Router } from '@angular/router'; import { AuthService } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-login', template: '', }) diff --git a/frontend/src/app/shell/pages/logout/logout-page.component.ts b/frontend/src/app/shell/pages/logout/logout-page.component.ts index 906282acf..93b254cc7 100644 --- a/frontend/src/app/shell/pages/logout/logout-page.component.ts +++ b/frontend/src/app/shell/pages/logout/logout-page.component.ts @@ -10,6 +10,7 @@ import { Router } from '@angular/router'; import { AuthService } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-logout', template: '', }) diff --git a/frontend/src/app/shell/pages/not-found/not-found-page.component.ts b/frontend/src/app/shell/pages/not-found/not-found-page.component.ts index 53d07e8e0..bc4edf246 100644 --- a/frontend/src/app/shell/pages/not-found/not-found-page.component.ts +++ b/frontend/src/app/shell/pages/not-found/not-found-page.component.ts @@ -7,11 +7,17 @@ import { Location } from '@angular/common'; import { Component } from '@angular/core'; +import { TitleComponent, TranslatePipe } from '@app/shared'; @Component({ + standalone: true, selector: 'sqx-not-found-page', styleUrls: ['./not-found-page.component.scss'], templateUrl: './not-found-page.component.html', + imports: [ + TitleComponent, + TranslatePipe, + ], }) export class NotFoundPageComponent { constructor( diff --git a/frontend/src/app/theme/icomoon/icons/action-Algolia.svg b/frontend/src/app/theme/icomoon/icons/action-Algolia.svg index 4f1539080..7dbaef031 100644 --- a/frontend/src/app/theme/icomoon/icons/action-Algolia.svg +++ b/frontend/src/app/theme/icomoon/icons/action-Algolia.svg @@ -1,17 +1,16 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - viewBox="0 0 95 95" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="algolia-mark-white.svg"> + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + viewBox="0 0 95 95" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="algolia-mark-white.svg"> <metadata id="metadata12"> <rdf:RDF> diff --git a/frontend/src/app/theme/icomoon/icons/action-Fastly.svg b/frontend/src/app/theme/icomoon/icons/action-Fastly.svg index eff311bcf..dd320b263 100644 --- a/frontend/src/app/theme/icomoon/icons/action-Fastly.svg +++ b/frontend/src/app/theme/icomoon/icons/action-Fastly.svg @@ -1,20 +1,19 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="580" - height="670" - viewBox="0 0 118.784 137.17078" - preserveAspectRatio="xMidYMid" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="fastly.svg"> + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="580" + height="670" + viewBox="0 0 118.784 137.17078" + preserveAspectRatio="xMidYMid" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="fastly.svg"> <metadata id="metadata10"> <rdf:RDF> diff --git a/frontend/src/app/theme/icomoon/icons/component.svg b/frontend/src/app/theme/icomoon/icons/component.svg index aeb90e88b..0ec53df0e 100644 --- a/frontend/src/app/theme/icomoon/icons/component.svg +++ b/frontend/src/app/theme/icomoon/icons/component.svg @@ -2,20 +2,19 @@ <!-- Generated by IcoMoon.io --> <svg - version="1.1" - width="28" - height="28" - viewBox="0 0 28 28" - id="svg12" - sodipodi:docname="component.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> + version="1.1" + width="28" + height="28" + viewBox="0 0 28 28" + id="svg12" + sodipodi:docname="component.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> <metadata id="metadata18"> <rdf:RDF> diff --git a/frontend/src/app/theme/icomoon/icons/fastly.svg b/frontend/src/app/theme/icomoon/icons/fastly.svg index eff311bcf..dd320b263 100644 --- a/frontend/src/app/theme/icomoon/icons/fastly.svg +++ b/frontend/src/app/theme/icomoon/icons/fastly.svg @@ -1,20 +1,19 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="580" - height="670" - viewBox="0 0 118.784 137.17078" - preserveAspectRatio="xMidYMid" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="fastly.svg"> + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="580" + height="670" + viewBox="0 0 118.784 137.17078" + preserveAspectRatio="xMidYMid" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="fastly.svg"> <metadata id="metadata10"> <rdf:RDF> diff --git a/frontend/src/app/theme/icomoon/icons/json.svg b/frontend/src/app/theme/icomoon/icons/json.svg index 6e438d0ba..6b12ffefa 100644 --- a/frontend/src/app/theme/icomoon/icons/json.svg +++ b/frontend/src/app/theme/icomoon/icons/json.svg @@ -2,24 +2,23 @@ <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - id="Layer_1" - x="0px" - y="0px" - width="40px" - height="40px" - viewBox="0 0 40 40" - enable-background="new 0 0 40 40" - xml:space="preserve" - inkscape:version="0.91 r13725" - sodipodi:docname="json.svg"><metadata + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="40px" + height="40px" + viewBox="0 0 40 40" + enable-background="new 0 0 40 40" + xml:space="preserve" + inkscape:version="0.91 r13725" + sodipodi:docname="json.svg"><metadata id="metadata13"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs diff --git a/frontend/src/app/theme/icomoon/icons/webhooks.svg b/frontend/src/app/theme/icomoon/icons/webhooks.svg index 4bc746dca..1e9b1014c 100644 --- a/frontend/src/app/theme/icomoon/icons/webhooks.svg +++ b/frontend/src/app/theme/icomoon/icons/webhooks.svg @@ -2,22 +2,21 @@ <!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - id="Layer_1" - x="0px" - y="0px" - viewBox="0 0 32 32" - style="enable-background:new 0 0 32 32;" - xml:space="preserve" - inkscape:version="0.91 r13725" - sodipodi:docname="webhooks.svg"><metadata + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + viewBox="0 0 32 32" + style="enable-background:new 0 0 32 32;" + xml:space="preserve" + inkscape:version="0.91 r13725" + sodipodi:docname="webhooks.svg"><metadata id="metadata7594"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 03239e1ef..09e99e212 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -20,4 +20,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -import 'zone.js/plugins/zone-error'; // Included with Angular CLI. +import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 7ff2fd289..646de517e 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -5,13 +5,138 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; +import { APP_BASE_HREF } from '@angular/common'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { enableProdMode, ErrorHandler } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { ActivatedRouteSnapshot, BaseRouteReuseStrategy, provideRouter, RouteReuseStrategy } from '@angular/router'; +import { TourService as BaseTourService } from 'ngx-ui-tour-core'; +import { APP_ROUTES } from '@app/app.routes'; +import { ApiUrlConfig, authInterceptor, buildTasks, cachingInterceptor, DateHelper, GlobalErrorHandler, loadingInterceptor, LocalizerService, TASK_CONFIGURATION, TitlesConfig, TourService, UIOptions } from '@app/shared'; +import { AppComponent } from './app/app.component'; import { environment } from './environments/environment'; +const options = (window as any)['options'] || {}; + +DateHelper.setlocale(options.more?.culture); + +function basePath() { + const baseElements = document.getElementsByTagName('base'); + + let baseHref: string = null!; + + if (baseElements.length > 0) { + baseHref = baseElements[0].href; + } + + if (baseHref.indexOf('http') === 0) { + baseHref = new URL(baseHref).pathname; + } + + if (!baseHref) { + baseHref = ''; + } + + let path = options.embedPath || '/'; + + while (baseHref.endsWith('/')) { + baseHref = baseHref.substring(0, baseHref.length - 1); + } + + return `${baseHref}${path}`; +} + +function configApiUrl() { + const baseElements = document.getElementsByTagName('base'); + + let baseHref: string = null!; + + if (baseElements.length > 0) { + baseHref = baseElements[0].href; + } + + if (!baseHref) { + baseHref = '/'; + } + + if (baseHref.indexOf('http') === 0) { + return new ApiUrlConfig(baseHref); + } else { + return new ApiUrlConfig(`${window.location.protocol}//${window.location.host}${baseHref}`); + } +} + +function configUIOptions() { + return new UIOptions(options); +} + +function configTitles() { + return new TitlesConfig(undefined, 'i18n:common.product'); +} + +function configLocalizerService() { + return new LocalizerService(environment.textResolver()).logMissingKeys(environment.textLogger); +} + +export class AppRouteReuseStrategy extends BaseRouteReuseStrategy { + public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) { + return (future.routeConfig === curr.routeConfig) || (future.data['reuseId'] && future.data['reuseId'] === curr.data['reuseId']); + } +} + if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +bootstrapApplication(AppComponent, { + providers: [ + provideAnimations(), + provideHttpClient( + withInterceptors([ + loadingInterceptor, + cachingInterceptor, + authInterceptor, + ]), + ), + provideRouter(APP_ROUTES), + { + provide: RouteReuseStrategy, + useClass: AppRouteReuseStrategy, + }, + { + provide: ApiUrlConfig, + useFactory: configApiUrl, + }, + { + provide: LocalizerService, + useFactory: configLocalizerService, + }, + { + provide: TitlesConfig, + useFactory: configTitles, + }, + { + provide: UIOptions, + useFactory: configUIOptions, + }, + { + provide: APP_BASE_HREF, + useValue: basePath(), + }, + { + provide: BaseTourService, + useClass: TourService, + }, + { + provide: ErrorHandler, + useClass: GlobalErrorHandler, + multi: false, + }, + { + provide: TASK_CONFIGURATION, + useFactory: buildTasks, + multi: false, + }, + ], +}); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index ea306ea4e..5f84a111c 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -29,7 +29,7 @@ "paths": { "@app*": [ "src/app*" - ] + ], }, "useDefineForClassFields": false },