diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index 423e6cb84..b984905e7 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -1078,7 +1078,7 @@ "search.addFilter": "Add Filter", "search.addGroup": "Add Group", "search.addSorting": "Add Sorting", - "search.advancedTour": "Click this icon to show the advanced search menu!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "Custom Query", "search.fullTextTour": "Search for content using full text search over all fields and languages!", "search.help": "Read more about filtering in the [Documentation](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/frontend_fr.json b/backend/i18n/frontend_fr.json index 604a65fd8..72c296e96 100644 --- a/backend/i18n/frontend_fr.json +++ b/backend/i18n/frontend_fr.json @@ -1078,7 +1078,7 @@ "search.addFilter": "Ajouter un filtre", "search.addGroup": "Ajouter un groupe", "search.addSorting": "Ajouter un tri", - "search.advancedTour": "Cliquez sur cette icône pour afficher le menu de recherche avancée\u00A0!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "Requête personnalisée", "search.fullTextTour": "Recherchez du contenu à l'aide de la recherche plein texte dans tous les champs et dans toutes les langues\u00A0!", "search.help": "En savoir plus sur le filtrage dans la [Documentation](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index 46c1b12ec..2bc4c3c71 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -1078,7 +1078,7 @@ "search.addFilter": "Aggiungi un Filtro", "search.addGroup": "Aggiungi un Gruppo", "search.addSorting": "Aggiungi ordinamento", - "search.advancedTour": "Fai clic su questa icona per visualizzare il menu della ricerca avanzata!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "Query personalizzata", "search.fullTextTour": "Cerca contenuti utilizzando la ricerca testuale su tutti i campi e le lingue!", "search.help": "Ulteriori informazioni sui filtri su [Documentazione](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/frontend_pt.json b/backend/i18n/frontend_pt.json index 30c4a8d8d..8c7d9e10c 100644 --- a/backend/i18n/frontend_pt.json +++ b/backend/i18n/frontend_pt.json @@ -1078,7 +1078,7 @@ "search.addFilter": "Adicionar filtro", "search.addGroup": "Adicionar Grupo", "search.addSorting": "Adicionar Classificação", - "search.advancedTour": "Clique neste ícone para mostrar o menu de pesquisa avançado!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "Consulta personalizada", "search.fullTextTour": "Pes para obter conteúdos com textos completos para todos os campos e para os direitos de texto.", "search.help": "Ler mais sobre filtros em [Documentação](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/frontend_zh.json b/backend/i18n/frontend_zh.json index 86664426e..3565eada5 100644 --- a/backend/i18n/frontend_zh.json +++ b/backend/i18n/frontend_zh.json @@ -1078,7 +1078,7 @@ "search.addFilter": "添加过滤器", "search.addGroup": "添加组", "search.addSorting": "添加排序", - "search.advancedTour": "单击此图标可显示高级搜索菜单!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "自定义查询", "search.fullTextTour": "使用全文搜索在所有领域和语言中搜索内容!", "search.help": "在 [文档](https://docs.squidex.io/04-guides/02-api.html) 中阅读有关过滤的更多信息。", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index 423e6cb84..b984905e7 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -1078,7 +1078,7 @@ "search.addFilter": "Add Filter", "search.addGroup": "Add Group", "search.addSorting": "Add Sorting", - "search.advancedTour": "Click this icon to show the advanced search menu!", + "search.advancedTour": "Use filters to get exactly the content your are looking for.", "search.customQuery": "Custom Query", "search.fullTextTour": "Search for content using full text search over all fields and languages!", "search.help": "Read more about filtering in the [Documentation](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/source/frontend_fr.json b/backend/i18n/source/frontend_fr.json index f0e32181c..d9935427e 100644 --- a/backend/i18n/source/frontend_fr.json +++ b/backend/i18n/source/frontend_fr.json @@ -945,7 +945,6 @@ "search.addFilter": "Ajouter un filtre", "search.addGroup": "Ajouter un groupe", "search.addSorting": "Ajouter un tri", - "search.advancedTour": "Cliquez sur cette icône pour afficher le menu de recherche avancée\u00A0!", "search.customQuery": "Requête personnalisée", "search.fullTextTour": "Recherchez du contenu à l'aide de la recherche plein texte dans tous les champs et dans toutes les langues\u00A0!", "search.help": "En savoir plus sur le filtrage dans la [Documentation](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/source/frontend_it.json b/backend/i18n/source/frontend_it.json index 5bc5f685c..7fc716a4b 100644 --- a/backend/i18n/source/frontend_it.json +++ b/backend/i18n/source/frontend_it.json @@ -751,7 +751,6 @@ "search.addFilter": "Aggiungi un Filtro", "search.addGroup": "Aggiungi un Gruppo", "search.addSorting": "Aggiungi ordinamento", - "search.advancedTour": "Fai clic su questa icona per visualizzare il menu della ricerca avanzata!", "search.customQuery": "Query personalizzata", "search.fullTextTour": "Cerca contenuti utilizzando la ricerca testuale su tutti i campi e le lingue!", "search.help": "Ulteriori informazioni sui filtri su [Documentazione](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/source/frontend_pt.json b/backend/i18n/source/frontend_pt.json index f4b37534a..328246c18 100644 --- a/backend/i18n/source/frontend_pt.json +++ b/backend/i18n/source/frontend_pt.json @@ -927,7 +927,6 @@ "search.addFilter": "Adicionar filtro", "search.addGroup": "Adicionar Grupo", "search.addSorting": "Adicionar Classificação", - "search.advancedTour": "Clique neste ícone para mostrar o menu de pesquisa avançado!", "search.customQuery": "Consulta personalizada", "search.fullTextTour": "Pes para obter conteúdos com textos completos para todos os campos e para os direitos de texto.", "search.help": "Ler mais sobre filtros em [Documentação](https://docs.squidex.io/04-guides/02-api.html).", diff --git a/backend/i18n/source/frontend_zh.json b/backend/i18n/source/frontend_zh.json index cb64355fb..22e1a17f8 100644 --- a/backend/i18n/source/frontend_zh.json +++ b/backend/i18n/source/frontend_zh.json @@ -759,7 +759,6 @@ "search.addFilter": "添加过滤器", "search.addGroup": "添加组", "search.addSorting": "添加排序", - "search.advancedTour": "单击此图标可显示高级搜索菜单!", "search.customQuery": "自定义查询", "search.fullTextTour": "使用全文搜索在所有领域和语言中搜索内容!", "search.help": "在 [文档](https://docs.squidex.io/04-guides/02-api.html) 中阅读有关过滤的更多信息。", diff --git a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj index 79b1f63d5..b9aba10ec 100644 --- a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj +++ b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj @@ -40,13 +40,13 @@ - - - - - - - + + + + + + + diff --git a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj index 4311fe01a..0271adebd 100644 --- a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj +++ b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj @@ -25,12 +25,12 @@ - - - - - - + + + + + + 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 577fc4a4c..71c034556 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 @@ -20,7 +20,7 @@ - + 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 3d8467ce7..08452b75e 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 @@ -29,8 +29,8 @@ - - + + diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index a46d1aeb5..701b2ef0b 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -24,13 +24,13 @@ - - - - - - - + + + + + + + diff --git a/backend/src/Squidex.Infrastructure/Translations/MissingKeys.cs b/backend/src/Squidex.Infrastructure/Translations/MissingKeys.cs deleted file mode 100644 index 612be3f83..000000000 --- a/backend/src/Squidex.Infrastructure/Translations/MissingKeys.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Translations; - -public sealed class MissingKeys -{ - private const string MissingFileName = "__missing.txt"; - private readonly object lockObject = new object(); - private readonly HashSet missingTranslations; - - public MissingKeys() - { - if (File.Exists(MissingFileName)) - { - var missing = File.ReadAllLines(MissingFileName); - - missingTranslations = new HashSet(missing); - } - else - { - missingTranslations = []; - } - } - - public void Log(string key) - { - lock (lockObject) - { - if (!missingTranslations.Add(key)) - { - File.AppendAllLines(MissingFileName, [key]); - } - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Translations/ResourcesLocalizer.cs b/backend/src/Squidex.Infrastructure/Translations/ResourcesLocalizer.cs index 865723156..c8aca9458 100644 --- a/backend/src/Squidex.Infrastructure/Translations/ResourcesLocalizer.cs +++ b/backend/src/Squidex.Infrastructure/Translations/ResourcesLocalizer.cs @@ -13,11 +13,6 @@ namespace Squidex.Infrastructure.Translations; public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocalizer { -#if DEBUG - private static readonly MissingKeys MissingKeys = new MissingKeys(); - -#endif - public (string Result, bool Found) Get(CultureInfo culture, string key, string fallback, object? args = null) { Guard.NotNull(culture); @@ -38,11 +33,9 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal var sb = new StringBuilder(translation.Length); var span = translation.AsSpan(); - while (span.Length > 0) { var indexOfStart = span.IndexOf('{'); - if (indexOfStart < 0) { break; @@ -51,7 +44,6 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal indexOfStart++; var indexOfEnd = span[indexOfStart..].IndexOf('}'); - if (indexOfEnd < 0) { break; @@ -62,7 +54,6 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal sb.Append(span[.. (indexOfStart - 1)]); var variable = span[indexOfStart..indexOfEnd]; - var shouldLower = false; var shouldUpper = false; @@ -83,7 +74,6 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal var variableName = variable.ToString(); var variableValue = variableName; - var property = argsType.GetProperty(variableName); if (property != null) @@ -129,7 +119,6 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal } sb.Append(span); - return (sb.ToString(), true); } @@ -138,13 +127,6 @@ public sealed class ResourcesLocalizer(ResourceManager resourceManager) : ILocal private string? GetCore(CultureInfo culture, string key) { - var translation = resourceManager.GetString(key, culture); -#if DEBUG - if (translation == null) - { - MissingKeys.Log(key); - } -#endif - return translation; + return resourceManager.GetString(key, culture); } } diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index dc188a9dc..ab33effd7 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -59,17 +59,17 @@ - - - - - - + + + + + + - - - - + + + + @@ -83,11 +83,11 @@ - + - + diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index 9823fd2c2..3bd3afc32 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -476,7 +476,10 @@ "folder": { // The relative or absolute path to the folder to store the assets. - "path": "Assets" + "path": "Assets", + + // To create the subfolder if it does not exist. Set it to false it the Squidex process has limited permissions. + "createFolder": true }, "googleCloud": { // The name of the bucket in google cloud store. @@ -487,7 +490,10 @@ "containerName": "squidex-assets", // The connection string to the azure storage service. - "connectionString": "UseDevelopmentStorage=true" + "connectionString": "UseDevelopmentStorage=true", + + // To create the subfolder if it does not exist. Set it to false it the Squidex process has limited permissions. + "createFolder": true }, "amazonS3": { // The url of the S3 API service. Leave it empty if using the one provided by Amazon @@ -539,6 +545,9 @@ //The host of the ftp service "serverPort": "21", + // To create the subfolder if it does not exist. Set it to false it the Squidex process has limited permissions. + "createFolder": true, + // Credentials. "username": "", "password": "", diff --git a/frontend/angular.json b/frontend/angular.json index 2d8a0b03e..69701a029 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -187,7 +187,8 @@ ], "stylePreprocessorOptions": { "includePaths": [ - "./src/app/theme" + "./src/app/theme", + "node_modules" ] }, "scripts": [] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 42e7f584f..7fe2f97d5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -41,6 +41,8 @@ "mousetrap": "1.6.5", "ng2-charts": "^8.0.0", "ngx-doc-viewer": "15.0.1", + "ngx-inline-filter": "^0.2.1", + "ngx-scrollbar": "^18.0.0", "ngx-ui-tour-core": "14.0.0", "oidc-client-ts": "^3.2.1", "progressbar.js": "1.1.1", @@ -19872,6 +19874,35 @@ "@angular/platform-browser": ">= 10.0.0" } }, + "node_modules/ngx-inline-filter": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ngx-inline-filter/-/ngx-inline-filter-0.2.1.tgz", + "integrity": "sha512-Szqe/ld24c2bWN2QqVC62u/X2tKRdBiyjvQytsRwRZ9iiLIxbz3X8xjMVgSA2UB0FcLV+rQdgJ7/wnlzTYGYKQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^19.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "ngx-scrollbar": "^18.0.0" + } + }, + "node_modules/ngx-scrollbar": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-scrollbar/-/ngx-scrollbar-18.0.0.tgz", + "integrity": "sha512-+ykmY491x+nzXvnecJvZHvDz0YWuX1r7SYMxNG4RVHXm5Z68P/8kd/3ryLD6DXdNWmJawd4NGvqq2ZkUKb/g3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": ">=19.0.0", + "@angular/common": ">=19.0.0", + "@angular/core": ">=19.0.0", + "rxjs": ">=7.0.0" + } + }, "node_modules/ngx-ui-tour-core": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-14.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index b5ce36b5e..89584741c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,6 +48,8 @@ "mousetrap": "1.6.5", "ng2-charts": "^8.0.0", "ngx-doc-viewer": "15.0.1", + "ngx-inline-filter": "^0.2.1", + "ngx-scrollbar": "^18.0.0", "ngx-ui-tour-core": "14.0.0", "oidc-client-ts": "^3.2.1", "progressbar.js": "1.1.1", diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 7e9a6a203..6634f49f1 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -8,6 +8,8 @@ } - + + + diff --git a/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html b/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html index 001b4eb0d..4b67357e8 100644 --- a/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html +++ b/frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html @@ -48,7 +48,7 @@ - + {{ "common.error" | sqxTranslate }} diff --git a/frontend/src/app/features/administration/pages/restore/restore-page.component.html b/frontend/src/app/features/administration/pages/restore/restore-page.component.html index d3b6233ed..0dd9cd3c3 100644 --- a/frontend/src/app/features/administration/pages/restore/restore-page.component.html +++ b/frontend/src/app/features/administration/pages/restore/restore-page.component.html @@ -86,4 +86,4 @@ - + diff --git a/frontend/src/app/features/administration/pages/users/users-page.component.html b/frontend/src/app/features/administration/pages/users/users-page.component.html index 197c01530..25e3ff7d0 100644 --- a/frontend/src/app/features/administration/pages/users/users-page.component.html +++ b/frontend/src/app/features/administration/pages/users/users-page.component.html @@ -75,4 +75,4 @@ - + diff --git a/frontend/src/app/features/api/api-area.component.html b/frontend/src/app/features/api/api-area.component.html index 957c346e3..1b1f13e09 100644 --- a/frontend/src/app/features/api/api-area.component.html +++ b/frontend/src/app/features/api/api-area.component.html @@ -20,4 +20,4 @@ - + diff --git a/frontend/src/app/features/assets/pages/assets-page.component.html b/frontend/src/app/features/assets/pages/assets-page.component.html index e0fe06e56..a7f951f7d 100644 --- a/frontend/src/app/features/assets/pages/assets-page.component.html +++ b/frontend/src/app/features/assets/pages/assets-page.component.html @@ -101,7 +101,8 @@ - + + - + diff --git a/frontend/src/app/features/content/pages/content/references/content-references.component.html b/frontend/src/app/features/content/pages/content/references/content-references.component.html index 3f0ffb37c..8407a91e7 100644 --- a/frontend/src/app/features/content/pages/content/references/content-references.component.html +++ b/frontend/src/app/features/content/pages/content/references/content-references.component.html @@ -31,5 +31,7 @@ } - + + + diff --git a/frontend/src/app/features/content/pages/contents/contents-page.component.html b/frontend/src/app/features/content/pages/contents/contents-page.component.html index 14f2da651..d644e0417 100644 --- a/frontend/src/app/features/content/pages/contents/contents-page.component.html +++ b/frontend/src/app/features/content/pages/contents/contents-page.component.html @@ -1,11 +1,11 @@ - +
@@ -184,4 +184,5 @@
- + + diff --git a/frontend/src/app/features/content/pages/schemas/schemas-page.component.html b/frontend/src/app/features/content/pages/schemas/schemas-page.component.html index 7647975bf..5d7190f36 100644 --- a/frontend/src/app/features/content/pages/schemas/schemas-page.component.html +++ b/frontend/src/app/features/content/pages/schemas/schemas-page.component.html @@ -21,4 +21,4 @@
} - + diff --git a/frontend/src/app/features/rules/pages/rule/rule-page.component.html b/frontend/src/app/features/rules/pages/rule/rule-page.component.html index c7db5dbc3..75a054714 100644 --- a/frontend/src/app/features/rules/pages/rule/rule-page.component.html +++ b/frontend/src/app/features/rules/pages/rule/rule-page.component.html @@ -1,4 +1,4 @@ - +
@@ -31,11 +31,10 @@
@if (rule.isEnabled) { {{ "common.enabled" | sqxTranslate }} - } - - @if (!rule.isEnabled) { + } @else { {{ "common.disabled" | sqxTranslate }} } +
- + diff --git a/frontend/src/app/features/rules/pages/rules/rules-page.component.html b/frontend/src/app/features/rules/pages/rules/rules-page.component.html index edff0880c..61e1b30b3 100644 --- a/frontend/src/app/features/rules/pages/rules/rules-page.component.html +++ b/frontend/src/app/features/rules/pages/rules/rules-page.component.html @@ -72,4 +72,4 @@
- + diff --git a/frontend/src/app/features/rules/shared/history-step.component.html b/frontend/src/app/features/rules/shared/history-step.component.html index f7635da15..43ec7ad3a 100644 --- a/frontend/src/app/features/rules/shared/history-step.component.html +++ b/frontend/src/app/features/rules/shared/history-step.component.html @@ -33,4 +33,6 @@ - + + + diff --git a/frontend/src/app/features/schemas/pages/schema/schema-page.component.html b/frontend/src/app/features/schemas/pages/schema/schema-page.component.html index 41a8733f4..b9d9d7dfe 100644 --- a/frontend/src/app/features/schemas/pages/schema/schema-page.component.html +++ b/frontend/src/app/features/schemas/pages/schema/schema-page.component.html @@ -116,7 +116,8 @@ @if (schema.type !== "Component") { } - + + } @@ -158,4 +159,4 @@ } - + diff --git a/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.html b/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.html index 8ecb2b1d9..2fc50ac3a 100644 --- a/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.html +++ b/frontend/src/app/features/schemas/pages/schemas/schemas-page.component.html @@ -42,4 +42,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html b/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html index a70fb8bc2..166a821ea 100644 --- a/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html +++ b/frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html @@ -60,4 +60,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/clients/clients-page.component.html b/frontend/src/app/features/settings/pages/clients/clients-page.component.html index cfd3f2338..435be9977 100644 --- a/frontend/src/app/features/settings/pages/clients/clients-page.component.html +++ b/frontend/src/app/features/settings/pages/clients/clients-page.component.html @@ -53,4 +53,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/contributors/contributors-page.component.html b/frontend/src/app/features/settings/pages/contributors/contributors-page.component.html index 1340564e5..fbd90a2ee 100644 --- a/frontend/src/app/features/settings/pages/contributors/contributors-page.component.html +++ b/frontend/src/app/features/settings/pages/contributors/contributors-page.component.html @@ -82,4 +82,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/jobs/jobs-page.component.html b/frontend/src/app/features/settings/pages/jobs/jobs-page.component.html index 1136cd799..cca5b2e9b 100644 --- a/frontend/src/app/features/settings/pages/jobs/jobs-page.component.html +++ b/frontend/src/app/features/settings/pages/jobs/jobs-page.component.html @@ -44,4 +44,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/languages/languages-page.component.html b/frontend/src/app/features/settings/pages/languages/languages-page.component.html index 788d3477c..bc559c525 100644 --- a/frontend/src/app/features/settings/pages/languages/languages-page.component.html +++ b/frontend/src/app/features/settings/pages/languages/languages-page.component.html @@ -51,4 +51,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/more/more-page.component.html b/frontend/src/app/features/settings/pages/more/more-page.component.html index 2b7ceab5a..12881e65f 100644 --- a/frontend/src/app/features/settings/pages/more/more-page.component.html +++ b/frontend/src/app/features/settings/pages/more/more-page.component.html @@ -164,4 +164,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/plans/plans-page.component.html b/frontend/src/app/features/settings/pages/plans/plans-page.component.html index 78cdac74d..74fcfa638 100644 --- a/frontend/src/app/features/settings/pages/plans/plans-page.component.html +++ b/frontend/src/app/features/settings/pages/plans/plans-page.component.html @@ -83,4 +83,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/roles/roles-page.component.html b/frontend/src/app/features/settings/pages/roles/roles-page.component.html index 13a9d032a..3e3a9d989 100644 --- a/frontend/src/app/features/settings/pages/roles/roles-page.component.html +++ b/frontend/src/app/features/settings/pages/roles/roles-page.component.html @@ -67,4 +67,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/settings/settings-page.component.html b/frontend/src/app/features/settings/pages/settings/settings-page.component.html index b05f72d9b..99c56f6e3 100644 --- a/frontend/src/app/features/settings/pages/settings/settings-page.component.html +++ b/frontend/src/app/features/settings/pages/settings/settings-page.component.html @@ -180,4 +180,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/templates/templates-page.component.html b/frontend/src/app/features/settings/pages/templates/templates-page.component.html index 20215c5d4..a9a942116 100644 --- a/frontend/src/app/features/settings/pages/templates/templates-page.component.html +++ b/frontend/src/app/features/settings/pages/templates/templates-page.component.html @@ -34,4 +34,4 @@ - + diff --git a/frontend/src/app/features/settings/pages/workflows/workflows-page.component.html b/frontend/src/app/features/settings/pages/workflows/workflows-page.component.html index 0f7dd59a3..47888abf2 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflows-page.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflows-page.component.html @@ -69,4 +69,4 @@ - + diff --git a/frontend/src/app/features/settings/settings-area.component.html b/frontend/src/app/features/settings/settings-area.component.html index 2f7503567..1cb0afbb9 100644 --- a/frontend/src/app/features/settings/settings-area.component.html +++ b/frontend/src/app/features/settings/settings-area.component.html @@ -6,4 +6,4 @@ } - + diff --git a/frontend/src/app/features/teams/pages/auth/auth-page.component.html b/frontend/src/app/features/teams/pages/auth/auth-page.component.html index c8cf6c8f3..7a9d796bd 100644 --- a/frontend/src/app/features/teams/pages/auth/auth-page.component.html +++ b/frontend/src/app/features/teams/pages/auth/auth-page.component.html @@ -142,4 +142,4 @@ - + diff --git a/frontend/src/app/features/teams/pages/contributors/contributors-page.component.html b/frontend/src/app/features/teams/pages/contributors/contributors-page.component.html index a4e40549b..203686e91 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributors-page.component.html +++ b/frontend/src/app/features/teams/pages/contributors/contributors-page.component.html @@ -70,4 +70,4 @@ - + diff --git a/frontend/src/app/features/teams/pages/more/more-page.component.html b/frontend/src/app/features/teams/pages/more/more-page.component.html index eea967691..964354472 100644 --- a/frontend/src/app/features/teams/pages/more/more-page.component.html +++ b/frontend/src/app/features/teams/pages/more/more-page.component.html @@ -78,4 +78,4 @@ - + diff --git a/frontend/src/app/features/teams/pages/plans/plans-page.component.html b/frontend/src/app/features/teams/pages/plans/plans-page.component.html index f238048d5..de3560b65 100644 --- a/frontend/src/app/features/teams/pages/plans/plans-page.component.html +++ b/frontend/src/app/features/teams/pages/plans/plans-page.component.html @@ -74,4 +74,4 @@ - + diff --git a/frontend/src/app/features/teams/shared/settings-area.component.html b/frontend/src/app/features/teams/shared/settings-area.component.html index 864ee53bc..f85307480 100644 --- a/frontend/src/app/features/teams/shared/settings-area.component.html +++ b/frontend/src/app/features/teams/shared/settings-area.component.html @@ -6,4 +6,4 @@ } - + diff --git a/frontend/src/app/framework/angular/dropdown-menu.component.html b/frontend/src/app/framework/angular/dropdown-menu.component.html index 6dbc74306..40b372640 100644 --- a/frontend/src/app/framework/angular/dropdown-menu.component.html +++ b/frontend/src/app/framework/angular/dropdown-menu.component.html @@ -1 +1 @@ - + diff --git a/frontend/src/app/framework/angular/list-view.component.html b/frontend/src/app/framework/angular/list-view.component.html index 0c461d595..1298bcea1 100644 --- a/frontend/src/app/framework/angular/list-view.component.html +++ b/frontend/src/app/framework/angular/list-view.component.html @@ -38,10 +38,18 @@
} - + + + - + + + - + + + - + + + diff --git a/frontend/src/app/framework/angular/modals/dialog-renderer.component.html b/frontend/src/app/framework/angular/modals/dialog-renderer.component.html index 5e837acb8..64f41f3e7 100644 --- a/frontend/src/app/framework/angular/modals/dialog-renderer.component.html +++ b/frontend/src/app/framework/angular/modals/dialog-renderer.component.html @@ -1,4 +1,4 @@ - + @if (snapshot.dialogRequest; as request) { diff --git a/frontend/src/app/framework/angular/modals/root-view.component.html b/frontend/src/app/framework/angular/modals/root-view.component.html index 78b9e812d..fa28f03b8 100644 --- a/frontend/src/app/framework/angular/modals/root-view.component.html +++ b/frontend/src/app/framework/angular/modals/root-view.component.html @@ -1,2 +1,2 @@
- + 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 a2b0f8f17..7db5b7299 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 @@ -6,7 +6,8 @@ */ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { AppLanguageDto, META_FIELDS, Query, SortMode, TableField } from '@app/shared/internal'; +import { SortOrder } from 'ngx-inline-filter'; +import { AppLanguageDto, META_FIELDS, Query, TableField } from '@app/shared/internal'; import { TableHeaderComponent } from '../table-header.component'; @Component({ @@ -34,7 +35,7 @@ export class ContentListHeaderComponent { public language!: AppLanguageDto; public sortPath?: string; - public sortDefault?: SortMode; + public sortDefault?: SortOrder; public ngOnChanges() { const { field, language } = this; diff --git a/frontend/src/app/shared/components/references/content-selector.component.html b/frontend/src/app/shared/components/references/content-selector.component.html index 262508192..01a1752a9 100644 --- a/frontend/src/app/shared/components/references/content-selector.component.html +++ b/frontend/src/app/shared/components/references/content-selector.component.html @@ -106,8 +106,17 @@
- + +
+ @if (canClear) { + + } + + +
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 7f5dbe125..632eb4089 100644 --- a/frontend/src/app/shared/components/references/content-selector.component.ts +++ b/frontend/src/app/shared/components/references/content-selector.component.ts @@ -50,6 +50,9 @@ export class ContentSelectorComponent implements OnInit { @Output() public contentSelect = new EventEmitter>(); + @Output() + public contentClear = new EventEmitter(); + @Input({ transform: numberAttribute }) public maxItems = Number.MAX_VALUE; @@ -59,6 +62,9 @@ export class ContentSelectorComponent implements OnInit { @Input() public schemaIdentifiers?: ReadonlyArray; + @Input({ transform: booleanAttribute }) + public canClear = false; + @Input() public query?: string; @@ -160,6 +166,10 @@ export class ContentSelectorComponent implements OnInit { this.contentSelect.emit([]); } + public emitClear() { + this.contentClear.emit(); + } + public emitSelect() { this.contentSelect.emit(Object.values(this.selectedItems)); } diff --git a/frontend/src/app/shared/components/references/reference-input.component.html b/frontend/src/app/shared/components/references/reference-input.component.html index ea0583a99..cb1da8a88 100644 --- a/frontend/src/app/shared/components/references/reference-input.component.html +++ b/frontend/src/app/shared/components/references/reference-input.component.html @@ -1,10 +1,8 @@ -
- - -
+ + | string> { @@ -54,6 +53,9 @@ export class ReferenceInputComponent extends StatefulControlComponent) { - if (contents.length > 0) { + public select(contents: ReadonlyArray, clear = false) { + if (contents.length > 0 || clear) { this.selectContent(contents[0]); } @@ -133,7 +135,6 @@ export class ReferenceInputComponent extends StatefulControlComponent -
-
- -
-
- -
- -
- -
- -
- -
- @switch (fieldUI) { - @case ("Boolean") { - - } - - @case ("Date") { - - } - - @case ("DateTime") { - - } - - @case ("Number") { - - } - - @case ("Reference") { - - } - - @case ("Select") { - - } - - @case ("Status") { - - {{ status.status }} - - } - - @case ("String") { - @if (!field.schema.extra) { - - } - } - - @case ("User") { - @if (contributorsState.isLoaded | async) { - - - - - - - - - {{ user.contributorName }} - - - } @else { - - } - } - - @case ("Unsupported") { - {{ "common.notSupported" | sqxTranslate }} - } - } -
- -
- -
- -} diff --git a/frontend/src/app/shared/components/search/queries/filter-comparison.component.scss b/frontend/src/app/shared/components/search/queries/filter-comparison.component.scss deleted file mode 100644 index 285960b91..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-comparison.component.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import 'mixins'; -@import 'vars'; - -.path { - width: 10rem; -} - -.operator { - width: 10rem; -} - -.dropdown-user { - @include truncate; - - .user-name { - margin-left: .25rem; - } -} \ No newline at end of file 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 deleted file mode 100644 index 5a7b59e89..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-comparison.component.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { AsyncPipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { DateTimeEditorComponent, DropdownComponent, HighlightPipe, TranslatePipe } from '@app/framework'; -import { AppLanguageDto, ContributorsState, FilterableField, FilterComparison, FilterFieldUI, FilterNegation, getFilterUI, isNegation, QueryModel } 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({ - selector: 'sqx-filter-comparison', - styleUrls: ['./filter-comparison.component.scss'], - templateUrl: './filter-comparison.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - AsyncPipe, - DateTimeEditorComponent, - DropdownComponent, - FilterOperatorPipe, - FormsModule, - HighlightPipe, - QueryPathComponent, - ReferenceInputComponent, - TranslatePipe, - UserDtoPicture, - ], -}) -export class FilterComparisonComponent { - @Output() - public filterChange = new EventEmitter(); - - @Output() - public remove = new EventEmitter(); - - @Input({ required: true }) - public language!: AppLanguageDto; - - @Input({ required: true }) - public languages!: ReadonlyArray; - - @Input({ required: true }) - public model!: QueryModel; - - @Input({ required: true }) - public filter!: FilterComparison | FilterNegation; - - public actualComparison!: FilterComparison; - public actualNegated = false; - - public field?: FilterableField; - public fieldUI?: FilterFieldUI; - public operators: ReadonlyArray = []; - - constructor( - public readonly contributorsState: ContributorsState, - ) { - } - - public ngOnChanges() { - if (isNegation(this.filter)) { - this.actualComparison = this.filter.not; - this.actualNegated = true; - } else { - this.actualComparison = this.filter; - this.actualNegated = false; - } - - this.field = this.model.schema.fields.find(x => x.path === this.actualComparison.path); - this.fieldUI = getFilterUI(this.actualComparison, this.field!); - - this.operators = this.model.operators[this.field?.schema.type!] || []; - - if (!this.operators.includes(this.actualComparison.op)) { - this.actualComparison = { ...this.actualComparison, op: this.operators[0] }; - } - } - - public changeValue(value: any) { - this.change({ value }); - } - - public changeOp(op: string) { - this.change({ op }); - } - - public changePath(path: string) { - this.change({ path, value: null }); - } - - private change(update: Partial) { - this.emitChange({ ...this.actualComparison, ...update }, this.actualNegated); - } - - public toggleNot() { - this.emitChange(this.actualComparison, !this.actualNegated); - } - - private emitChange(filter: FilterComparison, not: boolean) { - this.filterChange.emit(not ? { not: filter } : filter); - } -} diff --git a/frontend/src/app/shared/components/search/queries/filter-logical.component.html b/frontend/src/app/shared/components/search/queries/filter-logical.component.html deleted file mode 100644 index d498b12ba..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-logical.component.html +++ /dev/null @@ -1,47 +0,0 @@ -
-
-
-
- - -
-
- - @if (!isRoot) { -
- -
- } -
- -
- - @for (filter of filters; track $index; let i = $index) { -
- - -
- } - -
- - - @if (level < 1) { - - } -
-
-
diff --git a/frontend/src/app/shared/components/search/queries/filter-logical.component.scss b/frontend/src/app/shared/components/search/queries/filter-logical.component.scss deleted file mode 100644 index 4754390b9..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-logical.component.scss +++ /dev/null @@ -1,33 +0,0 @@ -@import 'mixins'; -@import 'vars'; - -$field-line: #e1e8ef; - -.filters { - padding-left: 3rem; - position: relative; -} - -.filter { - position: relative; - - &-line-v { - @include absolute(-.5rem, auto, 1.8rem, 1.5rem); - border: 0; - border-left: 2px dashed $field-line; - width: 2px; - } - - &-line-h { - @include absolute(1.2rem, auto, auto, -1.5rem); - border: 0; - border-bottom: 2px dashed $field-line; - width: 1.5rem; - } - - &-add { - background: none; - border: 2px dashed $field-line; - padding: .5rem; - } -} \ No newline at end of file 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 deleted file mode 100644 index 8efc4541c..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-logical.component.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, numberAttribute, Output } from '@angular/core'; -import { TranslatePipe } from '@app/framework'; -import { AppLanguageDto, FilterLogical, FilterNode, isLogicalAnd, isLogicalOr, QueryModel } from '@app/shared/internal'; -import { FilterNodeComponent } from './filter-node.component'; - -@Component({ - selector: 'sqx-filter-logical', - styleUrls: ['./filter-logical.component.scss'], - templateUrl: './filter-logical.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - FilterNodeComponent, - TranslatePipe, - ], -}) -export class FilterLogicalComponent { - @Output() - public filterChange = new EventEmitter(); - - @Output() - public remove = new EventEmitter(); - - @Input({ required: true }) - public language!: AppLanguageDto; - - @Input({ required: true }) - public languages!: ReadonlyArray; - - @Input({ transform: numberAttribute }) - public level = 0; - - @Input({ transform: booleanAttribute }) - public isRoot?: boolean | null; - - @Input({ required: true }) - public model!: QueryModel; - - @Input({ required: true }) - public filter!: FilterLogical; - - public get filters() { - return isLogicalAnd(this.filter) ? this.filter.and : this.filter.or; - } - - public get isAnd() { - return isLogicalAnd(this.filter); - } - - public get isOr() { - return isLogicalOr(this.filter); - } - - public addComparison() { - this.addNode({ path: this.model.schema.fields[0].path } as any); - } - - public addLogical() { - this.addNode({ and: [] }); - } - - public addNode(node: FilterNode) { - const filter = this.filter; - - let change: FilterLogical; - if (isLogicalAnd(filter)) { - change = { and: [...filter.and, node] }; - } else { - change = { or: [...filter.or, node] }; - } - - this.emitChange(change); - } - - public removeNode(index: number) { - const filter = this.filter; - - let change: FilterLogical; - if (isLogicalAnd(filter)) { - change = { and: filter.and.filter((_, i) => i !== index) }; - } else { - change = { or: filter.or.filter((_, i) => i !== index) }; - } - - this.emitChange(change); - } - - public replaceNode(index: number, node: FilterNode) { - const filter = this.filter; - - let change: FilterLogical; - if (isLogicalAnd(filter)) { - change = { and: filter.and.map((x, i) => i === index ? node : x) }; - } else { - change = { or: filter.or.map((x, i) => i === index ? node : x) }; - } - - this.emitChange(change); - } - - public toggleType() { - const filter = this.filter; - - let change: FilterLogical; - if (isLogicalAnd(filter)) { - change = { or: filter.and }; - } else { - change = { and: filter.or }; - } - - this.emitChange(change); - } - - private emitChange(filter: FilterLogical) { - this.filterChange.emit(filter); - } -} diff --git a/frontend/src/app/shared/components/search/queries/filter-node.component.html b/frontend/src/app/shared/components/search/queries/filter-node.component.html deleted file mode 100644 index 2b12fad08..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-node.component.html +++ /dev/null @@ -1,18 +0,0 @@ -@if (actualLogical) { - -} @else if (actualComparison) { - -} diff --git a/frontend/src/app/shared/components/search/queries/filter-node.component.scss b/frontend/src/app/shared/components/search/queries/filter-node.component.scss deleted file mode 100644 index 2742d895e..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-node.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'vars'; \ No newline at end of file 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 deleted file mode 100644 index 9e703ab6f..000000000 --- a/frontend/src/app/shared/components/search/queries/filter-node.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -import { ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, numberAttribute, Output } from '@angular/core'; -import { AppLanguageDto, FilterComparison, FilterLogical, FilterNegation, FilterNode, isLogical, QueryModel } from '@app/shared/internal'; -import { FilterComparisonComponent } from './filter-comparison.component'; -import { FilterLogicalComponent } from './filter-logical.component'; - -@Component({ - selector: 'sqx-filter-node', - styleUrls: ['./filter-node.component.scss'], - templateUrl: './filter-node.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - forwardRef(() => FilterComparisonComponent), - forwardRef(() => FilterLogicalComponent), - ], -}) -export class FilterNodeComponent { - @Output() - public filterChange = new EventEmitter(); - - @Output() - public remove = new EventEmitter(); - - @Input({ required: true }) - public language!: AppLanguageDto; - - @Input({ required: true }) - public languages!: ReadonlyArray; - - @Input({ transform: numberAttribute }) - public level = 0; - - @Input({ required: true }) - public model!: QueryModel; - - @Input() - public set filter(value: FilterNode) { - if (isLogical(value)) { - this.actualLogical = value; - } else { - this.actualComparison = value; - } - } - - public actualComparison?: FilterComparison | FilterNegation; - public actualLogical?: FilterLogical; -} diff --git a/frontend/src/app/shared/components/search/queries/query-path.component.html b/frontend/src/app/shared/components/search/queries/query-path.component.html deleted file mode 100644 index a07b4955b..000000000 --- a/frontend/src/app/shared/components/search/queries/query-path.component.html +++ /dev/null @@ -1,21 +0,0 @@ - - -
-
-
{{ field.path }}
-
- -
- {{ field.description }} -
-
-
- {{ field.path }} -
diff --git a/frontend/src/app/shared/components/search/queries/query-path.component.scss b/frontend/src/app/shared/components/search/queries/query-path.component.scss deleted file mode 100644 index 2742d895e..000000000 --- a/frontend/src/app/shared/components/search/queries/query-path.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'vars'; \ No newline at end of file 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 deleted file mode 100644 index b12e107f1..000000000 --- a/frontend/src/app/shared/components/search/queries/query-path.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -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({ - 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() - public pathChange = new EventEmitter(); - - @Input() - public path = ''; - - @Input({ required: true }) - public model!: QueryModel; - - public field?: FilterableField; - - public ngOnChanges() { - this.field = this.model.schema.fields.find(x => x.path === this.path); - } -} diff --git a/frontend/src/app/shared/components/search/queries/query.component.html b/frontend/src/app/shared/components/search/queries/query.component.html deleted file mode 100644 index 9f1ab35bd..000000000 --- a/frontend/src/app/shared/components/search/queries/query.component.html +++ /dev/null @@ -1,15 +0,0 @@ - -

{{ "search.sorting" | sqxTranslate }}

- -@for (sorting of actualQuery.sort; track sorting; let i = $index) { -
- -
-} - diff --git a/frontend/src/app/shared/components/search/queries/query.component.scss b/frontend/src/app/shared/components/search/queries/query.component.scss deleted file mode 100644 index 2742d895e..000000000 --- a/frontend/src/app/shared/components/search/queries/query.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'vars'; \ No newline at end of file diff --git a/frontend/src/app/shared/components/search/queries/query.component.ts b/frontend/src/app/shared/components/search/queries/query.component.ts deleted file mode 100644 index 0b3016910..000000000 --- a/frontend/src/app/shared/components/search/queries/query.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { TranslatePipe } from '@app/framework'; -import { AppLanguageDto, FilterLogical, Query, QueryModel, QuerySorting } from '@app/shared/internal'; -import { FilterLogicalComponent } from './filter-logical.component'; -import { SortingComponent } from './sorting.component'; - -@Component({ - selector: 'sqx-query', - styleUrls: ['./query.component.scss'], - templateUrl: './query.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - FilterLogicalComponent, - SortingComponent, - TranslatePipe, - ], -}) -export class QueryComponent { - @Output() - public queryChange = new EventEmitter(); - - @Input({ required: true }) - public language!: AppLanguageDto; - - @Input({ required: true }) - public languages!: ReadonlyArray; - - @Input({ required: true }) - public model!: QueryModel; - - @Input() - public set query(query: Query | undefined | null) { - if (!query) { - query = {}; - } - - if (!query.filter) { - query = { ...query, filter: { and: [] } }; - } - - if (!query.sort) { - query = { ...query, sort: [] }; - } - - this.actualQuery = query as any; - } - - public actualQuery!: RequireKeys; - - public addSorting() { - const path = Object.keys(this.model.schema.fields)[0]; - - this.change({ sort: [...this.actualQuery.sort, { path, order: 'ascending' }] }); - } - - public replaceSorting(index: number, sorting: QuerySorting) { - this.change({ sort: this.actualQuery.sort.map((x, i) => i === index ? sorting : x) }); - } - - public removeSorting(index: number) { - this.change({ sort: this.actualQuery.sort.filter((_, i) => i !== index) }); - } - - public changeFilter(filter: FilterLogical ) { - this.change({ filter }); - } - - private change(update: Partial) { - this.actualQuery = { ...this.actualQuery, ...update }; - - this.queryChange.emit(this.actualQuery); - } -} - -type RequireKeys = Required> & Omit; \ No newline at end of file diff --git a/frontend/src/app/shared/components/search/queries/query.pipes.ts b/frontend/src/app/shared/components/search/queries/query.pipes.ts deleted file mode 100644 index b670309f7..000000000 --- a/frontend/src/app/shared/components/search/queries/query.pipes.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ - name: 'sqxFilterOperator', - pure: true, -}) -export class FilterOperatorPipe implements PipeTransform { - public transform(value: any) { - if (!value) { - return null; - } - - return `common.queryOperators.${value}`; - } -} \ No newline at end of file diff --git a/frontend/src/app/shared/components/search/queries/sorting.component.html b/frontend/src/app/shared/components/search/queries/sorting.component.html deleted file mode 100644 index 24dc48ad2..000000000 --- a/frontend/src/app/shared/components/search/queries/sorting.component.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- -
- -
- -
- -
-
diff --git a/frontend/src/app/shared/components/search/queries/sorting.component.scss b/frontend/src/app/shared/components/search/queries/sorting.component.scss deleted file mode 100644 index 2742d895e..000000000 --- a/frontend/src/app/shared/components/search/queries/sorting.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'vars'; \ No newline at end of file diff --git a/frontend/src/app/shared/components/search/queries/sorting.component.ts b/frontend/src/app/shared/components/search/queries/sorting.component.ts deleted file mode 100644 index e7ca0d614..000000000 --- a/frontend/src/app/shared/components/search/queries/sorting.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - - -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({ - selector: 'sqx-sorting', - styleUrls: ['./sorting.component.scss'], - templateUrl: './sorting.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - FormsModule, - QueryPathComponent, - ], -}) -export class SortingComponent { - public readonly modes = SORT_MODES; - - @Output() - public sortingChange = new EventEmitter(); - - @Output() - public remove = new EventEmitter(); - - @Input({ required: true }) - public model!: QueryModel; - - @Input({ required: true }) - public sorting!: QuerySorting; - - public changeOrder(order: any) { - this.change({ order }); - } - - public changePath(path: string) { - this.change({ path }); - } - - private change(update: Partial) { - this.sorting = { ...this.sorting, ...update }; - - this.sortingChange.emit(this.sorting); - } -} diff --git a/frontend/src/app/shared/components/search/search-form.component.html b/frontend/src/app/shared/components/search/search-form.component.html index d6584fd5b..09a41c6dd 100644 --- a/frontend/src/app/shared/components/search/search-form.component.html +++ b/frontend/src/app/shared/components/search/search-form.component.html @@ -2,94 +2,71 @@ } -
-
- - @if (queries) { - @if (saveKey | async; as key) { - - } @else { - +
+ + + + @switch (field.args.editor) { + @case ("Date") { + } - } - @if (queryModel) { - - } -
- - - {{ "search.customQuery" | sqxTranslate }} - - @if (queries && queriesTypes) { -
- @if (saveKey | async; as key) { - - } @else { - - } + @case ("DateTime") { + + } -
- - -
-
- } + @case ("Reference") { + + } - @if (showQueries) { - @if (queryModel) { -
- - -
+ @case ("Status") { + + {{ status.status }} + + } + + @case ("User") { + @if (contributorsState.isLoaded | async) { + + + + + + + + + {{ user.contributorName }} + + + } @else { + + } } - } @else if (queries && queriesTypes) { -
- -
} -
- - - -
+ +
+
diff --git a/frontend/src/app/shared/components/search/search-form.component.scss b/frontend/src/app/shared/components/search/search-form.component.scss index 8110d52d3..bdcfd4bf5 100644 --- a/frontend/src/app/shared/components/search/search-form.component.scss +++ b/frontend/src/app/shared/components/search/search-form.component.scss @@ -1,32 +1,70 @@ @import 'mixins'; @import 'vars'; -.search-form { - display: inline-block; -} - -.form-inline { - position: relative; -} - -.form-horizontal { - min-width: 35rem; -} - -.buttons { - @include absolute(8px, 8px); -} - -.buttons2 { - @include absolute(.75rem, 1.75rem); -} - -.link { - font-size: $font-small; - font-weight: normal; - margin-top: 1.5rem; -} - -.col-form-label { - text-align: left; +::ng-deep { + .nf-details { + background-color: $color-white; + + input, select, button { + height: auto; + } + } + + .nf { + svg { + fill: $color-text-decent; + } + + .btn-success { + svg { + fill: $color-white; + } + } + } + + .nf-dropdown { + .nf-dropdown-description { + color: $color-text-decent + } + + .control-dropdown-item.active { + .nf-dropdown-description { + color: $color-white; + } + } + } + + .nf-button { + &.btn-sm { + padding-top: .25rem; + padding-bottom: .25rem; + } + } + + .nf-comparison-inline { + font-size: $font-small; + + * { + font-size: .9rem; + } + + input { + box-shadow: none; + } + + .btn, + .form-control, + .form-select { + background: none !important; + border: 0; + border-radius: 0; + height: calc(1.75rem - 2px); + padding-top: 0.1875rem; + padding-bottom: .25rem; + } + } + + .dropdown-user { + margin-right: .5rem; + } } \ No newline at end of file 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 1bbd1679d..b87099e06 100644 --- a/frontend/src/app/shared/components/search/search-form.component.ts +++ b/frontend/src/app/shared/components/search/search-form.component.ts @@ -6,14 +6,15 @@ */ import { AsyncPipe } from '@angular/common'; -import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output, Type } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BooleanValue, BootstrapClasses, EMPTY_FILTER_MODEL, FieldComponent, FilterField, Input as FilterInput, FilterModel, FilterOptions, NumberValue, StringValue } from 'ngx-inline-filter'; import { Observable } from 'rxjs'; -import { ControlErrorsComponent, FocusOnInitDirective, MarkdownDirective, ModalDialogComponent, ModalDirective, ShortcutComponent, ShortcutDirective, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/framework'; -import { AppLanguageDto, DialogModel, equalsQuery, hasFilter, Queries, Query, QueryModel, SaveQueryForm, TypedSimpleChanges } from '@app/shared/internal'; +import { ControlErrorsComponent, DateTimeEditorComponent, DropdownComponent, FocusOnInitDirective, HighlightPipe, LocalizerService, ModalDialogComponent, ModalDirective, ShortcutComponent, TooltipDirective, TourStepDirective, TranslatePipe } from '@app/framework'; +import { AppLanguageDto, ContributorsState, DialogModel, Queries, Query, QueryModel, sanitize, SaveQueryForm, TypedSimpleChanges } from '@app/shared/internal'; +import { UserDtoPicture } from '../pipes'; +import { ReferenceInputComponent } from '../references/reference-input.component'; import { TourHintDirective } from '../tour-hint.directive'; -import { QueryComponent } from './queries/query.component'; -import { SavedQueriesComponent } from './shared-queries.component'; @Component({ selector: 'sqx-search-form', @@ -23,25 +24,25 @@ import { SavedQueriesComponent } from './shared-queries.component'; imports: [ AsyncPipe, ControlErrorsComponent, + DateTimeEditorComponent, + DropdownComponent, + FilterInput, FocusOnInitDirective, FormsModule, - MarkdownDirective, + HighlightPipe, ModalDialogComponent, ModalDirective, - QueryComponent, + ReferenceInputComponent, ReactiveFormsModule, - SavedQueriesComponent, ShortcutComponent, - ShortcutDirective, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe, + UserDtoPicture, ], }) export class SearchFormComponent { - private previousQuery?: Query | null; - @Output() public queryChange = new EventEmitter(); @@ -69,42 +70,135 @@ export class SearchFormComponent { @Input({ transform: booleanAttribute }) public enableShortcut?: boolean | null; - @Input() - public formClass = 'form-inline search-form'; - - public showQueries = true; + public filterModel = EMPTY_FILTER_MODEL; public saveKey!: Observable; public saveQueryDialog = new DialogModel(); public saveQueryForm = new SaveQueryForm(); - public searchDialog = new DialogModel(false); - - public hasFilter = false; + public cleanedQuery: Query = sanitize(); + + public readonly options: FilterOptions; + + constructor( + public readonly contributorsState: ContributorsState, + private readonly localizer: LocalizerService, + ) { + this.options = { + cssClasses: { + ...BootstrapClasses, + buttonAdd: 'btn btn-success', + buttonAddOutline: 'btn btn-outline-success btn-sm', + buttonDefault: () => 'btn', + buttonLogical: active => `btn btn-sm btn-secondary btn-toggle ${active ? 'btn-primary' : ''}`, + dropdown: 'bg-white', + dropdownItem: active => `control-dropdown-item control-dropdown-item-selectable separated ${active ? 'active' : ''}`, + dropdownSearch: 'p-2', + }, + texts: { + addComparison: localizer.getOrKey('search.addFilter'), + addGroup: localizer.getOrKey('search.addGroup'), + addSorting: localizer.getOrKey('search.addSorting'), + and: 'AND', + not: 'NOT', + noResults: ' - ', + or: 'OR', + save: localizer.getOrKey('common.save'), + searchPlaceholder: localizer.getOrKey('common.search'), + searchShortcut: 'CTRL + I', + sortAsc: 'ascending', + sortDesc: 'descending', + sorting: localizer.getOrKey('search.sorting'), + }, + }; + } public ngOnChanges(changes: TypedSimpleChanges) { + if (changes.query) { + this.cleanedQuery = sanitize(this.query); + } + if (changes.query || changes.queries) { this.updateSaveKey(); } - if (changes.query) { - this.previousQuery = this.query; + if (changes.queryModel) { + this.updateFilterModel(); } - - this.hasFilter = hasFilter(this.query); } - public search(close = false) { - this.hasFilter = hasFilter(this.query); + private updateFilterModel() { + const model = this.queryModel; + if (!model) { + this.filterModel = EMPTY_FILTER_MODEL; + return; + } + + const filterModel: FilterModel = { fields: [], operators: [] }; + + const uniqueOperators = new Set(); + for (var operators of Object.values(model.operators)) { + for (const operator of operators) { + uniqueOperators.add(operator); + } + } + + for (const operator of uniqueOperators) { + const isEmpty = + operator === 'empty' || + operator === 'exists'; + + const label = this.localizer.get(`common.queryOperators.${operator}`) || operator; + + filterModel.operators.push({ value: operator, label, isEmpty }); + } - if (this.query && !equalsQuery(this.query, this.previousQuery)) { - this.queryChange.emit(this.query); + for (const field of model.schema.fields) { + let args; + let component: Type> | undefined = undefined; + + const { type, extra } = field.schema; + if (field.schema.type === 'Boolean') { + component = BooleanValue; + } else if (type === 'DateTime' && extra?.editor === 'Date') { + args = { editor: 'Date' }; + } else if (type === 'DateTime') { + args = { editor: 'DateTime' }; + } else if (type === 'Number') { + component = NumberValue; + } else if (type === 'String' && extra?.editor === 'Status') { + args = { editor: 'Status', statuses: model.statuses }; + } else if (type === 'String' && extra?.editor === 'User') { + args = { editor: 'User' }; + } else if (type === 'String' && !extra) { + component = StringValue; + } else if (type === 'StringArray' && extra?.schemaIds) { + args = { editor: 'Reference', schemaIds: extra.schemaIds }; + } else if (type === 'StringArray') { + component = StringValue; + } else { + continue; + } - this.previousQuery = this.query; + const filterField: FilterField = { + args, + component, + defaultValue: null, + description: field.description, + label: field.path, + operators: model.operators[field.schema.type] as any, + path: field.path, + }; + + filterModel.fields.push(filterField); } - if (close) { - this.searchDialog.hide(); + this.filterModel = filterModel; + } + + public bookmark(value: boolean) { + if (value) { + this.saveQueryDialog.show(); } } @@ -131,22 +225,6 @@ export class SearchFormComponent { this.saveQueryDialog.hide(); } - public changeQueryFullText(fullText: string) { - this.query = { ...this.query, fullText }; - - this.updateSaveKey(); - } - - public changeQuery(query: Query) { - this.query = query; - - this.updateSaveKey(); - } - - public changeView(value: boolean) { - this.showQueries = value; - } - private updateSaveKey() { if (this.queries && this.query) { this.saveKey = this.queries.getSaveKey(this.query); diff --git a/frontend/src/app/shared/components/table-header.component.ts b/frontend/src/app/shared/components/table-header.component.ts index ae296f8fb..3807dd5bc 100644 --- a/frontend/src/app/shared/components/table-header.component.ts +++ b/frontend/src/app/shared/components/table-header.component.ts @@ -7,8 +7,9 @@ import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { SortOrder } from 'ngx-inline-filter'; import { TranslatePipe } from '@app/framework'; -import { AppLanguageDto, Query, SortMode, Types } from '@app/shared/internal'; +import { AppLanguageDto, Query, Types } from '@app/shared/internal'; @Component({ selector: 'sqx-table-header', @@ -39,9 +40,9 @@ export class TableHeaderComponent { public sortable?: boolean | null; @Input() - public sortDefault: SortMode | undefined | null; + public sortDefault: SortOrder | undefined | null; - public order: SortMode | undefined | null; + public order: SortOrder | undefined | null; public ngOnChanges() { const { query, sortDefault, sortable, sortPath } = this; diff --git a/frontend/src/app/shared/index.ts b/frontend/src/app/shared/index.ts index c2771b215..d7f5465f3 100644 --- a/frontend/src/app/shared/index.ts +++ b/frontend/src/app/shared/index.ts @@ -59,13 +59,6 @@ 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'; diff --git a/frontend/src/app/shared/services/query.ts b/frontend/src/app/shared/services/query.ts index 87621680c..40fc5da51 100644 --- a/frontend/src/app/shared/services/query.ts +++ b/frontend/src/app/shared/services/query.ts @@ -5,6 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { ComplexQuery } from 'ngx-inline-filter'; import { QueryParams, RouteSynchronizer, Types } from '@app/framework'; import { StatusInfo } from './contents.service'; @@ -20,53 +21,6 @@ export type FilterSchemaType = 'String' | 'StringArray'; -export type FilterFieldUI = - 'Boolean' | - 'Date' | - 'DateTime' | - 'Reference' | - 'None' | - 'Number' | - 'Select' | - 'String' | - 'Status' | - 'Unsupported' | - 'User'; - -export function getFilterUI(comparison: FilterComparison, field: FilterableField): FilterFieldUI { - if (!field || !comparison) { - return 'None'; - } - - const { type, extra } = field.schema; - - if (comparison.op === 'empty' || comparison.op === 'exists') { - return 'None'; - } else if (type === 'Boolean') { - return 'Boolean'; - } else if (type === 'DateTime' && extra?.editor === 'Date') { - return 'Date'; - } else if (type === 'DateTime') { - return 'DateTime'; - } else if (type === 'Number') { - return 'Number'; - } else if (type === 'String' && extra?.editor === 'Status') { - return 'Status'; - } else if (type === 'String' && extra?.editor === 'User') { - return 'User'; - } else if (type === 'String' && extra?.options) { - return 'Select'; - } else if (type === 'String') { - return 'String'; - } else if (type === 'StringArray' && extra?.schemaIds) { - return 'Reference'; - } else if (type === 'StringArray') { - return 'String'; - } else { - return 'Unsupported'; - } -} - export interface FilterSchema { // The value type. readonly type: FilterSchemaType; @@ -100,80 +54,7 @@ export interface QueryModel { readonly operators: Readonly<{ [type: string]: ReadonlyArray }>; } -export type FilterNode = FilterComparison | FilterLogical | FilterNegation; -export type FilterLogical = FilterAnd | FilterOr; - -export type FilterComparison = Readonly<{ - // The full path to the property. - path: string; - - // The operator. - op: string; - - // The value. - value: any; -}>; - -export type FilterNegation = Readonly<{ - // The negated filter. - not: FilterComparison; -}>; - -export type FilterAnd = Readonly<{ - // The child filters if the logical filter is a conjunction (AND). - and: FilterNode[]; -}>; - -export type FilterOr = Readonly<{ - // The child filters if the logical filter is a disjunction (OR). - or: FilterNode[]; -}>; - -export function isNegation(input: FilterNode): input is FilterNegation { - return !!(input as any)['not']; -} - -export function isLogicalOr(input: FilterNode): input is FilterOr { - return !!(input as any)['or']; -} - -export function isLogicalAnd(input: FilterNode): input is FilterAnd { - return !!(input as any)['and']; -} - -export function isLogical(input: FilterNode): input is FilterLogical { - return isLogicalAnd(input) || isLogicalOr(input); -} - -export function isComparison(input: FilterNode): input is FilterComparison { - return !isNegation(input) && !isComparison(input); -} - -export interface QuerySorting { - // The full path to the property. - path: string; - - // The sort order. - order: SortMode; -} - -export const SORT_MODES: ReadonlyArray = [ - 'ascending', - 'descending', -]; - -export type SortMode = 'ascending' | 'descending'; - -export interface Query { - // The optional filter. - filter?: FilterLogical; - - // The full text search. - fullText?: string; - - // The sorting. - sort?: QuerySorting[]; - +export interface Query extends ComplexQuery { // The number of items to take. take?: number; @@ -181,13 +62,6 @@ export interface Query { skip?: number; } -const DEFAULT_QUERY = { - filter: { - and: [], - }, - sort: [], -}; - export class QueryFullTextSynchronizer implements RouteSynchronizer { public static readonly INSTANCE = new QueryFullTextSynchronizer(); @@ -242,7 +116,7 @@ export class QuerySynchronizer implements RouteSynchronizer { export function sanitize(query?: Query | null) { if (!query) { - return DEFAULT_QUERY; + return { filter: { and: [] }, sort: [] }; } if (!query.sort) { diff --git a/frontend/src/app/shell/pages/internal/internal-area.component.html b/frontend/src/app/shell/pages/internal/internal-area.component.html index f848d05aa..db5abf7a4 100644 --- a/frontend/src/app/shell/pages/internal/internal-area.component.html +++ b/frontend/src/app/shell/pages/internal/internal-area.component.html @@ -6,7 +6,11 @@ } diff --git a/frontend/src/app/shell/pages/internal/teams-area.component.html b/frontend/src/app/shell/pages/internal/teams-area.component.html index 0680b43f9..67e7bd4cd 100644 --- a/frontend/src/app/shell/pages/internal/teams-area.component.html +++ b/frontend/src/app/shell/pages/internal/teams-area.component.html @@ -1 +1 @@ - + diff --git a/frontend/src/app/theme/_bootstrap-vars.scss b/frontend/src/app/theme/_bootstrap-vars.scss index 6c3c27a3b..ebba4e4d1 100644 --- a/frontend/src/app/theme/_bootstrap-vars.scss +++ b/frontend/src/app/theme/_bootstrap-vars.scss @@ -57,9 +57,6 @@ $btn-border-width: 1px; $btn-white-space: nowrap; // Alerts -$alert-color-scale: -100%; -$alert-bg-scale: 0%; -$alert-border-scale: 0%; $alert-link-font-weight: normal; // Labels diff --git a/frontend/src/app/theme/_bootstrap.scss b/frontend/src/app/theme/_bootstrap.scss index 0072836b8..6ced88ba6 100644 --- a/frontend/src/app/theme/_bootstrap.scss +++ b/frontend/src/app/theme/_bootstrap.scss @@ -460,10 +460,6 @@ a { &.btn-warning { @include build-toggle-button($color-theme-warning, $color-badge-warning-background); } - - &.btn-secondary { - color: $color-text-decent; - } } .btn + .btn-toggle { @@ -722,10 +718,10 @@ $icon-size: 4.5rem; .form-control { &[readonly] { - color: $input-disabled-color; background-color: $input-disabled-bg; border-color: $input-disabled-border-color; - border-radius: inherit; + border-radius: $border-radius; + color: $input-disabled-color; opacity: 1; } } \ No newline at end of file diff --git a/frontend/src/app/theme/_common.scss b/frontend/src/app/theme/_common.scss index 6606bd627..fa4814586 100644 --- a/frontend/src/app/theme/_common.scss +++ b/frontend/src/app/theme/_common.scss @@ -119,6 +119,12 @@ hr { font-weight: bold; } +.dropdown-user { + .user-name { + margin-left: .5rem; + } +} + // // Help texts // diff --git a/frontend/src/app/theme/_vars.scss b/frontend/src/app/theme/_vars.scss index 583d7a367..e4b3b847c 100644 --- a/frontend/src/app/theme/_vars.scss +++ b/frontend/src/app/theme/_vars.scss @@ -89,6 +89,8 @@ $btn-disabled-color: inherit; $btn-disabled-bg: inherit; $btn-disabled-border-color: inherit; +$dropdown-bg: $color-white; + $modal-content-bg: $color-white; $history-dot-size: 10px; @@ -103,4 +105,10 @@ $asset-image: 12rem; $asset-footer: 7rem; $asset-background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAIAAAC1nk4lAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAACbSURBVGhD7c6hDQAxAMPA33+m7vYlJh7AoFKOBMbfyfyZRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4E3o9kA7YFFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgUUbD4FFGw+BRRsPgQejz7nPYYKl8IqSfgAAAABJRU5ErkJggg=='); -$gutter-y: 0; \ No newline at end of file +$gutter-y: 0; + +$nf-color-border: $color-border; +$nf-color-add-background: $color-theme-success; +$nf-color-primary: $color-theme-brand; +$nf-color-text: $color-text; +$nf-color-text-muted: $color-text-decent; \ No newline at end of file diff --git a/frontend/src/app/theme/theme.scss b/frontend/src/app/theme/theme.scss index 190c8a6be..94cf83d2c 100644 --- a/frontend/src/app/theme/theme.scss +++ b/frontend/src/app/theme/theme.scss @@ -12,6 +12,9 @@ // GraphiQL @import 'graphiql/graphiql.css'; +// Filter +@import 'ngx-inline-filter/styles/layout'; + // Custom files @import 'common'; @import 'panels2'; diff --git a/tools/e2e/tests/given-app/rules.spec.ts b/tools/e2e/tests/given-app/rules.spec.ts index 1dd457685..1f0284fce 100644 --- a/tools/e2e/tests/given-app/rules.spec.ts +++ b/tools/e2e/tests/given-app/rules.spec.ts @@ -14,12 +14,19 @@ test('has header', async ({ page }) => { }); test('create rule', async ({ rulesPage, rulePage }) => { - const ruleName = await createRandomRule(rulesPage, rulePage); + const ruleName = await createRandomRule(rulesPage, rulePage, true); const ruleCard = await rulesPage.getRuleCard(ruleName); await expect(ruleCard.root).toBeVisible(); }); +test('create disabled rule', async ({ rulesPage, rulePage }) => { + const ruleName = await createRandomRule(rulesPage, rulePage, true); + const ruleCard = await rulesPage.getRuleCard(ruleName); + + await expect(ruleCard.root.locator('sqx-toggle div').first()).toHaveAttribute('data-state', 'unchecked'); +}); + test('delete rule', async ({ rulesPage, rulePage }) => { const ruleName = await createRandomRule(rulesPage, rulePage); const ruleCard = await rulesPage.getRuleCard(ruleName); @@ -79,7 +86,7 @@ test('rename rule', async ({ rulePage, rulesPage }) => { await expect(newCard.root).toBeVisible(); }); -async function createRandomRule(rulesPage: RulesPage, rulePage: RulePage) { +async function createRandomRule(rulesPage: RulesPage, rulePage: RulePage, disabled = false) { const ruleName = `rule-${getRandomId()}`; await rulesPage.addRule(); @@ -93,6 +100,11 @@ async function createRandomRule(rulesPage: RulesPage, rulePage: RulePage) { await stepDialog.add(); await rulePage.enterName(ruleName); + + if (disabled) { + await rulePage.toggleEnabled(); + } + await rulePage.save(); await rulePage.back(); diff --git a/tools/e2e/tests/given-schema/contents.spec.ts b/tools/e2e/tests/given-schema/contents.spec.ts index a1b11375f..c20968d84 100644 --- a/tools/e2e/tests/given-schema/contents.spec.ts +++ b/tools/e2e/tests/given-schema/contents.spec.ts @@ -14,8 +14,8 @@ test.beforeEach(async ({ appName, schemaName, contentsPage }) => { await contentsPage.increasePageSize(); }); -test('has header', async ({ page }) => { - const header = page.getByRole('heading', { name: 'Contents' }); +test('has filter', async ({ page }) => { + const header = page.getByRole('button', { name: 'AND' }); await expect(header).toBeVisible(); }); diff --git a/tools/e2e/tests/pages/rule.ts b/tools/e2e/tests/pages/rule.ts index 32100c4c8..ab4b557f0 100644 --- a/tools/e2e/tests/pages/rule.ts +++ b/tools/e2e/tests/pages/rule.ts @@ -26,9 +26,13 @@ export class RulePage { return new StepDialog(this.page, this.page.getByTestId('dialog')); } + public async toggleEnabled() { + await this.page.locator('sqx-toggle div').first().click(); + } + public async save() { await this.page.getByRole('button', { name: 'Save' }).click(); - await this.page.getByText('Enabled').waitFor({ state: 'visible' }); + await this.page.getByRole('alert').waitFor({ state: 'visible' }); } public async back() { @@ -48,10 +52,12 @@ export class TriggerDialog { public async add() { await this.root.getByRole('button', { name: 'Add' }).click(); + await this.root.waitFor({ state: 'detached' }); } public async save() { await this.root.getByRole('button', { name: 'Save' }).click(); + await this.root.waitFor({ state: 'detached' }); } } @@ -68,9 +74,11 @@ export class StepDialog { public async add() { await this.root.getByRole('button', { name: 'Add' }).click(); + await this.root.waitFor({ state: 'detached' }); } public async save() { await this.root.getByRole('button', { name: 'Save' }).click(); + await this.root.waitFor({ state: 'detached' }); } } \ No newline at end of file