diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs index ec33fed09..91825235d 100644 --- a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs @@ -10,12 +10,12 @@ using Squidex.Text; namespace Squidex.Domain.Apps.Entities.Contents; -public class ContentSqlQueryBuilder(SqlDialect dialect, string table, SqlParams? parameters = null) : SqlQueryBuilder(dialect, table, parameters) +public class ContentSqlQueryBuilder(SqlDialect dialect, string table, SqlParams? parameters = null) + : SqlQueryBuilder(dialect, table, parameters) { public override PropertyPath Visit(PropertyPath path) { var elements = path.ToList(); - elements[0] = elements[0].ToPascalCase(); return new PropertyPath(elements); diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs index 9f08cd907..072756f60 100644 --- a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs @@ -144,7 +144,7 @@ public class SqlDialect throw new NotSupportedException(); } - protected virtual string FormatValues(CompareOperator op, ClrValue value, SqlParams queryParameters) + protected virtual string FormatValues(CompareOperator op, ClrValue value, SqlParams queryParameters, bool withoutBraces = false) { if (!value.IsList && value.ValueType == ClrValueType.Null) { @@ -170,6 +170,11 @@ public class SqlDialect if (op == CompareOperator.In) { + if (withoutBraces) + { + return string.Join(", ", parameters); + } + return $"({string.Join(", ", parameters)})"; } diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs index 69589b94c..48d3500c7 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Text; using MySqlConnector; using Squidex.Infrastructure.Queries; @@ -85,7 +86,13 @@ public sealed class MySqlDialect : SqlDialect var sqlOrder = FormatOrder(order); var sqlPath = path.JsonPath(); - return $"IF(JSON_TYPE(JSON_EXTRACT({sqlPath})) IN ('INTEGER', 'DOUBLE', 'DECIMAL'), CAST(JSON_VALUE({sqlPath}) AS DOUBLE), NULL) {sqlOrder}, JSON_VALUE({sqlPath}) {sqlOrder}"; + return $""" + IF (JSON_TYPE(JSON_EXTRACT({sqlPath})) IN ('INTEGER', 'DOUBLE', 'DECIMAL'), + CAST(JSON_VALUE({sqlPath}) AS DOUBLE), + NULL + ) {sqlOrder}, + JSON_VALUE({sqlPath}) {sqlOrder} + """; } return base.OrderBy(path, order, isJson); @@ -100,15 +107,54 @@ public sealed class MySqlDialect : SqlDialect { if (isJson) { + var sqlPath = path.JsonPath(); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); var isBoolean = value.ValueType is ClrValueType.Boolean; - if (isBoolean) + + string ScalarCondition() { - var sqlPath = path.JsonPath(); - var sqlOp = FormatOperator(op, value); - var sqlRhs = FormatValues(op, value, queryParameters); + if (isBoolean) + { + return $"IF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + } - return $"IF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + return base.Where(path, op, value, queryParameters, isJson); + } + + if (value.IsList && op == CompareOperator.In) + { + return $""" + IF( + JSON_TYPE(JSON_EXTRACT({sqlPath})) = 'ARRAY', + JSON_OVERLAPS(JSON_EXTRACT({sqlPath}), JSON_ARRAY{sqlRhs}), + JSON_CONTAINS(JSON_ARRAY{sqlRhs}, JSON_EXTRACT({sqlPath})) + ) + """; } + + if (op == CompareOperator.Equals) + { + var valueExpr = value.ValueType switch + { + ClrValueType.Boolean => + $"IF({sqlRhs} = 1, 'true', 'false')", + ClrValueType.String => + $"JSON_QUOTE({sqlRhs})", + _ => + $"CAST({sqlRhs} AS JSON)", + }; + + return $""" + IF( + JSON_TYPE(JSON_EXTRACT({sqlPath})) = 'ARRAY', + JSON_CONTAINS(JSON_EXTRACT({sqlPath}), {valueExpr}), + {ScalarCondition()} + ) + """; + } + + return ScalarCondition(); } return base.Where(path, op, value, queryParameters, isJson); diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs index 306f74a49..88e5a960c 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Text; using Npgsql; using Squidex.Infrastructure.Queries; using Squidex.Providers.Postgres.App; @@ -71,27 +72,74 @@ public class PostgresDialect : SqlDialect { if (isJson) { - var issNumeric = value.ValueType is + var sqlPathCast = path.JsonPath(true); + var sqlPath = path.JsonPath(false); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + var isBoolean = value.ValueType is ClrValueType.Boolean; + + var isNumeric = value.ValueType is ClrValueType.Single or ClrValueType.Double or ClrValueType.Int32 or ClrValueType.Int64; - if (issNumeric) + + string ScalarCondition() { - var sqlOp = FormatOperator(op, value); - var sqlRhs = FormatValues(op, value, queryParameters); + if (isNumeric) + { + return $"(CASE WHEN jsonb_typeof({sqlPath}) = 'number' THEN ({sqlPathCast})::numeric {sqlOp} {sqlRhs} ELSE FALSE END)"; + } - return $"(CASE WHEN jsonb_typeof({path.JsonPath(false)}) = 'number' THEN ({path.JsonPath(true)})::numeric {sqlOp} {sqlRhs} ELSE FALSE END)"; + if (isBoolean) + { + return $"(CASE WHEN jsonb_typeof({sqlPath}) = 'boolean' THEN ({sqlPathCast})::boolean {sqlOp} {sqlRhs} ELSE FALSE END)"; + } + + return base.Where(path, op, value, queryParameters, true); } - var isBoolean = value.ValueType is ClrValueType.Boolean; - if (isBoolean) + string ToJsonbValue() + { + if (isNumeric) + { + return $"to_jsonb({sqlRhs}::numeric)"; + } + + if (isBoolean) + { + return $"to_jsonb({sqlRhs}::boolean)"; + } + + return $"to_jsonb({sqlRhs}::text)"; + } + + if (value.IsList && op == CompareOperator.In) { - var sqlOp = FormatOperator(op, value); - var sqlRhs = FormatValues(op, value, queryParameters); + return $""" + CASE WHEN jsonb_typeof({sqlPath}) = 'array' + THEN EXISTS ( + SELECT 1 + FROM jsonb_array_elements({sqlPath}) AS elem + WHERE jsonb_build_array{sqlRhs} @> elem + ) + ELSE {sqlPath} <@ jsonb_build_array{sqlRhs} + END + """; + } - return $"(CASE WHEN jsonb_typeof({path.JsonPath(false)}) = 'boolean' THEN ({path.JsonPath(true)})::boolean {sqlOp} {sqlRhs} ELSE FALSE END)"; + if (op == CompareOperator.Equals) + { + return $""" + CASE WHEN jsonb_typeof({sqlPath}) = 'array' + THEN {sqlPath} @> jsonb_build_array({ToJsonbValue()}) + ELSE {ScalarCondition()} + END + """; } + + return ScalarCondition(); } return base.Where(path, op, value, queryParameters, isJson); diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs index bc14727bb..e58cf0ea5 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Text; using Squidex.Infrastructure.Queries; using SqlException = Microsoft.Data.SqlClient.SqlException; @@ -87,45 +88,96 @@ public sealed class SqlServerDialect : SqlDialect { if (isJson) { - var issNumeric = value.ValueType is + var sqlPath = path.JsonPath(); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + var isNull = value.ValueType is ClrValueType.Null; + var isBoolean = value.ValueType is ClrValueType.Boolean; + var isNumeric = value.ValueType is ClrValueType.Single or ClrValueType.Double or ClrValueType.Int32 or ClrValueType.Int64; - if (issNumeric) + + string ScalarCondition() { - var sqlPath = path.JsonPath(); - var sqlOp = FormatOperator(op, value); - var sqlRhs = FormatValues(op, value, queryParameters); + if (isNull) + { + if (op == CompareOperator.Equals) + { + return $"JSON_QUERY({sqlPath}) IS NULL AND JSON_VALUE({sqlPath}) IS NULL"; + } + + if (op == CompareOperator.NotEquals) + { + return $"JSON_QUERY({sqlPath}) IS NOT NULL OR JSON_VALUE({sqlPath}) IS NOT NULL"; + } + } - return $"TRY_CAST(JSON_VALUE({sqlPath}) AS NUMERIC) {sqlOp} {sqlRhs}"; - } + if (isNumeric) + { + return $"TRY_CAST(JSON_VALUE({sqlPath}) AS NUMERIC) {sqlOp} {sqlRhs}"; + } - var isBoolean = value.ValueType is ClrValueType.Boolean; - if (isBoolean) - { - var sqlPath = path.JsonPath(); - var sqlOp = FormatOperator(op, value); - var sqlRhs = FormatValues(op, value, queryParameters); + if (isBoolean) + { + return $"IIF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + } - return $"IIF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + return base.Where(path, op, value, queryParameters, isJson); } - var isNull = value.ValueType is ClrValueType.Null; - if (isNull) + string ArrayCondition(string field, string op) { - var sqlPath = path.JsonPath(); - - if (op == CompareOperator.Equals) + if (isNumeric) { - return $"JSON_QUERY({sqlPath}) IS NULL AND JSON_VALUE({sqlPath}) IS NULL"; + return $"TRY_CAST({field} AS NUMERIC) {op} {sqlRhs}"; } - if (op == CompareOperator.NotEquals) + if (isBoolean) { - return $"JSON_QUERY({sqlPath}) IS NOT NULL OR JSON_VALUE({sqlPath}) IS NOT NULL"; + return $"IIF({field} = 'true', 1, 0) {op} {sqlRhs}"; } + + return $"{field} = {sqlRhs}"; + } + + if (value.IsList && op == CompareOperator.In) + { + return $""" + CASE WHEN LEFT(JSON_QUERY({sqlPath}), 1) = '[' + THEN ( + SELECT COUNT(*) + FROM OPENJSON({sqlPath}) AS field_arr + WHERE {ArrayCondition("field_arr.value", "IN")} + ) + ELSE ( + SELECT COUNT(*) + WHERE {ScalarCondition()} + ) + END > 0 + """; + } + + if (op == CompareOperator.Equals) + { + return $""" + CASE WHEN LEFT(JSON_QUERY({sqlPath}), 1) = '[' + THEN ( + SELECT COUNT(*) + FROM OPENJSON({sqlPath}) AS field_arr + WHERE {ArrayCondition("field_arr.value", "=")} + ) + ELSE ( + SELECT COUNT(*) + WHERE {ScalarCondition()} + ) + END > 0 + """; } + + return ScalarCondition(); } return base.Where(path, op, value, queryParameters, isJson); diff --git a/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs b/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs index 0daae1d6b..6d87646a4 100644 --- a/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs +++ b/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs @@ -11,7 +11,7 @@ public sealed class QueryModel { public static readonly IReadOnlyDictionary> DefaultOperators = new Dictionary> { - [FilterSchemaType.Any] = Enum.GetValues(typeof(CompareOperator)).OfType().ToList(), + [FilterSchemaType.Any] = Enum.GetValues().ToList(), [FilterSchemaType.Boolean] = [ CompareOperator.Equals, diff --git a/backend/src/Squidex.Web/Pipeline/SchemaResolver.cs b/backend/src/Squidex.Web/Pipeline/SchemaResolver.cs index 0d2f113f8..9a063843b 100644 --- a/backend/src/Squidex.Web/Pipeline/SchemaResolver.cs +++ b/backend/src/Squidex.Web/Pipeline/SchemaResolver.cs @@ -31,35 +31,29 @@ public sealed class SchemaResolver : IAsyncActionFilter public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var app = context.HttpContext.Features.Get(); - - if (app != null) + if (app != null && context.RouteData.Values.TryGetValue("schema", out var schemaValue)) { - if (context.RouteData.Values.TryGetValue("schema", out var schemaValue)) + var schemaIdOrName = schemaValue?.ToString(); + if (string.IsNullOrWhiteSpace(schemaIdOrName)) { - var schemaIdOrName = schemaValue?.ToString(); - - if (string.IsNullOrWhiteSpace(schemaIdOrName)) - { - context.Result = new NotFoundResult(); - return; - } - - var schema = await GetSchemaAsync(app.Id, schemaIdOrName, context.HttpContext.User); - - if (schema == null) - { - context.Result = new NotFoundResult(); - return; - } + context.Result = new NotFoundResult(); + return; + } - if (context.ActionDescriptor.EndpointMetadata.Any(x => x is SchemaMustBePublishedAttribute) && !schema.IsPublished) - { - context.Result = new NotFoundResult(); - return; - } + var schema = await GetSchemaAsync(app.Id, schemaIdOrName, context.HttpContext.User); + if (schema == null) + { + context.Result = new NotFoundResult(); + return; + } - context.HttpContext.Features.Set(schema); + if (context.ActionDescriptor.EndpointMetadata.Any(x => x is SchemaMustBePublishedAttribute) && !schema.IsPublished) + { + context.Result = new NotFoundResult(); + return; } + + context.HttpContext.Features.Set(schema); } await next(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs index 84bac8cb1..17a906c28 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs @@ -61,7 +61,7 @@ public abstract class EFQueryTests(ISqlFixture fixture) mixed = true; break; case 4: - mixed = new List { i }; + mixed = new List { i, true }; break; default: mixed = new Dictionary(); @@ -339,6 +339,28 @@ public abstract class EFQueryTests(ISqlFixture fixture) Assert.Equal([3, 5, 7], actual.Order().ToArray()); } + [Fact] + public async Task Should_filter_with_equal_in_json_array() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Json.array", 3), + }); + + Assert.Equal([3], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_many_in_json_array() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.In("Json.array", new List { 3, 5, 7 }), + }); + + Assert.Equal([3, 5, 7], actual.Order().ToArray()); + } + [Fact] public async Task Should_filter_with_many_in_mixed_json() { diff --git a/frontend/package.json b/frontend/package.json index dd3232693..ae5c1fb42 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "watch": "ng build --watch --configuration development", "storybook": "ng run squidex:storybook", "storybook-build": "ng run squidex:build-storybook", - "generate": "dotnet run --project generator/Generator/Generator.csproj" + "generate": "dotnet run --project generator/Generator/Generator.csproj", + "format": "prettier --write **/*.html" }, "private": true, "dependencies": { diff --git a/frontend/src/app/_theme.html b/frontend/src/app/_theme.html index d67902a6e..a32022ee4 100644 --- a/frontend/src/app/_theme.html +++ b/frontend/src/app/_theme.html @@ -1071,8 +1071,8 @@
Primary Secondary Success Danger - Warning Info - Light Dark + Warning Info Light + Dark
diff --git a/frontend/src/app/features/api/pages/graphql/graphql-page.component.html b/frontend/src/app/features/api/pages/graphql/graphql-page.component.html index f52da177f..01bde845c 100644 --- a/frontend/src/app/features/api/pages/graphql/graphql-page.component.html +++ b/frontend/src/app/features/api/pages/graphql/graphql-page.component.html @@ -3,7 +3,7 @@
@if (clientsReadable) { - + } diff --git a/frontend/src/app/features/apps/pages/app.component.html b/frontend/src/app/features/apps/pages/app.component.html index 46e674e76..26ca6844d 100644 --- a/frontend/src/app/features/apps/pages/app.component.html +++ b/frontend/src/app/features/apps/pages/app.component.html @@ -51,7 +51,8 @@ confirmRememberKey="leaveApp" confirmText="i18n:apps.leaveConfirmText" confirmTitle="i18n:apps.leaveConfirmTitle" - (sqxConfirmClick)="leave.emit(app)"> + (sqxConfirmClick)="leave.emit(app)" + type="button"> {{ "apps.leave" | sqxTranslate }} diff --git a/frontend/src/app/features/apps/pages/onboarding-dialog.component.html b/frontend/src/app/features/apps/pages/onboarding-dialog.component.html index c528e2da2..9bfbf2f66 100644 --- a/frontend/src/app/features/apps/pages/onboarding-dialog.component.html +++ b/frontend/src/app/features/apps/pages/onboarding-dialog.component.html @@ -3,7 +3,7 @@
- + @if (step === 0) {
@@ -15,7 +15,7 @@
- +
@@ -78,8 +78,8 @@
- - +
diff --git a/frontend/src/app/features/apps/pages/team.component.html b/frontend/src/app/features/apps/pages/team.component.html index 4396b9a2c..ede3ab6eb 100644 --- a/frontend/src/app/features/apps/pages/team.component.html +++ b/frontend/src/app/features/apps/pages/team.component.html @@ -24,7 +24,8 @@ confirmRememberKey="leaveApp" confirmText="i18n:teams.leaveConfirmText" confirmTitle="i18n:teams.leaveConfirmTitle" - (sqxConfirmClick)="leave.emit(team)"> + (sqxConfirmClick)="leave.emit(team)" + type="button"> {{ "teams.leave" | sqxTranslate }} diff --git a/frontend/src/app/features/assets/pages/asset-tag-dialog.component.html b/frontend/src/app/features/assets/pages/asset-tag-dialog.component.html index fd82f2f77..f844fc657 100644 --- a/frontend/src/app/features/assets/pages/asset-tag-dialog.component.html +++ b/frontend/src/app/features/assets/pages/asset-tag-dialog.component.html @@ -3,7 +3,7 @@ {{ "common.renameTag" | sqxTranslate }} - + diff --git a/frontend/src/app/features/assets/pages/asset-tags.component.html b/frontend/src/app/features/assets/pages/asset-tags.component.html index 90a0edd30..9eacad81e 100644 --- a/frontend/src/app/features/assets/pages/asset-tags.component.html +++ b/frontend/src/app/features/assets/pages/asset-tags.component.html @@ -1,11 +1,11 @@ diff --git a/frontend/src/app/features/content/pages/content/content-history-page.component.html b/frontend/src/app/features/content/pages/content/content-history-page.component.html index 6ba23ec6d..214fe1119 100644 --- a/frontend/src/app/features/content/pages/content/content-history-page.component.html +++ b/frontend/src/app/features/content/pages/content/content-history-page.component.html @@ -18,7 +18,7 @@ @if (content.canDraftCreate || content.canDraftDelete) {
@if (!content.newStatus) { - + } @else { @@ -50,7 +50,8 @@ confirmRememberKey="deleteDraft" confirmText="i18n:contents.deleteVersionConfirmText" confirmTitle="i18n:contents.deleteConfirmTitle" - (sqxConfirmClick)="deleteDraft()"> + (sqxConfirmClick)="deleteDraft()" + type="button"> {{ "contents.versionDelete" | sqxTranslate }} @@ -60,7 +61,8 @@ confirmRememberKey="deleteContent" confirmText="i18n:contents.deleteConfirmText" confirmTitle="i18n:contents.deleteConfirmTitle" - (sqxConfirmClick)="delete()"> + (sqxConfirmClick)="delete()" + type="button"> {{ "common.delete" | sqxTranslate }} @@ -88,7 +90,7 @@ @if (content.statusUpdates.length > 0) { @for (info of content.statusUpdates; track info) { - @@ -102,7 +104,8 @@ confirmRememberKey="cancelStatus" confirmText="i18n:contents.cancelStatusConfirmText" confirmTitle="i18n:contents.cancelStatusConfirmTitle" - (sqxConfirmClick)="cancelStatus()"> + (sqxConfirmClick)="cancelStatus()" + type="button"> {{ "contents.cancelStatus" | sqxTranslate }} @@ -112,7 +115,8 @@ confirmRememberKey="deleteContent" confirmText="i18n:contents.deleteConfirmText" confirmTitle="i18n:contents.deleteConfirmTitle" - (sqxConfirmClick)="delete()"> + (sqxConfirmClick)="delete()" + type="button"> {{ "common.delete" | sqxTranslate }} diff --git a/frontend/src/app/features/content/pages/content/content-page.component.html b/frontend/src/app/features/content/pages/content/content-page.component.html index a54a71196..17213ded1 100644 --- a/frontend/src/app/features/content/pages/content/content-page.component.html +++ b/frontend/src/app/features/content/pages/content/content-page.component.html @@ -4,7 +4,7 @@
@if (schema.type !== "Singleton") { - } @@ -75,7 +75,7 @@ @if (content.canClone) { - } @@ -86,7 +86,8 @@ confirmRememberKey="deleteContent" confirmText="i18n:contents.deleteConfirmText" confirmTitle="i18n:contents.deleteConfirmTitle" - (sqxConfirmClick)="delete()"> + (sqxConfirmClick)="delete()" + type="button"> {{ "common.delete" | sqxTranslate }} } @@ -133,8 +134,8 @@ scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="saveOnlyDropdown; closeAlways: true"> - - + +
} @@ -157,10 +158,10 @@ scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="savePublishDropdown; closeAlways: true"> - - diff --git a/frontend/src/app/features/content/pages/content/editor/content-editor.component.html b/frontend/src/app/features/content/pages/content/editor/content-editor.component.html index 6e0225049..411c967d3 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-editor.component.html +++ b/frontend/src/app/features/content/pages/content/editor/content-editor.component.html @@ -8,7 +8,7 @@ @if (contentVersion) {
- +
@if (isDeleted) { diff --git a/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html b/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html index ab8246fdd..e7083103d 100644 --- a/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html +++ b/frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html @@ -3,19 +3,19 @@ @if (mode | async; as currentMode) {
- {{ 'common.or' | sqxTranslate }} + {{ "common.or" | sqxTranslate }}
ApiKey: {{ rolesState.appName }}:{{ apiKey | async }}
diff --git a/frontend/src/app/features/content/shared/list/content.component.html b/frontend/src/app/features/content/shared/list/content.component.html index 67e39e851..5667b66ba 100644 --- a/frontend/src/app/features/content/shared/list/content.component.html +++ b/frontend/src/app/features/content/shared/list/content.component.html @@ -22,14 +22,14 @@ {{ "common.editInNewTab" | sqxTranslate }} @for (info of content.statusUpdates; track info) { - } @if (cloneable) { - + } @@ -39,7 +39,8 @@ confirmRememberKey="deleteContent" confirmText="i18n:contents.deleteConfirmText" confirmTitle="i18n:contents.deleteConfirmTitle" - (sqxConfirmClick)="delete.emit()"> + (sqxConfirmClick)="delete.emit()" + type="button"> {{ "common.delete" | sqxTranslate }} diff --git a/frontend/src/app/features/content/shared/preview-button.component.html b/frontend/src/app/features/content/shared/preview-button.component.html index a1d913c5c..c794891d5 100644 --- a/frontend/src/app/features/content/shared/preview-button.component.html +++ b/frontend/src/app/features/content/shared/preview-button.component.html @@ -9,7 +9,7 @@ @for (name of snapshot.previewNamesMore; track name) { - + }
diff --git a/frontend/src/app/features/content/shared/references/reference-item.component.html b/frontend/src/app/features/content/shared/references/reference-item.component.html index d2a660479..64be1d378 100644 --- a/frontend/src/app/features/content/shared/references/reference-item.component.html +++ b/frontend/src/app/features/content/shared/references/reference-item.component.html @@ -44,7 +44,7 @@
- @if (canRemove) { 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 e497bdfc9..8e12b2a61 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 @@ -144,7 +144,6 @@ export class ReferencesTagsComponent extends StatefulControlComponent x.id === content.id); - if (index >= 0) { this.contents[index] = content; } else { @@ -153,12 +152,10 @@ export class ReferencesTagsComponent extends StatefulControlComponent { this.isLoadingFailed = true; - this.resetConverterState(false); }, }); @@ -171,7 +168,6 @@ export class ReferencesTagsComponent extends StatefulControlComponent
- - + + diff --git a/frontend/src/app/features/dashboard/pages/dashboard-page.component.html b/frontend/src/app/features/dashboard/pages/dashboard-page.component.html index f0d6bfe67..4260e0804 100644 --- a/frontend/src/app/features/dashboard/pages/dashboard-page.component.html +++ b/frontend/src/app/features/dashboard/pages/dashboard-page.component.html @@ -24,7 +24,8 @@ } - @case ("api-calls") {animate.enter="fade-in" animate.leave="fade-out" + @case ("api-calls") { + animate.enter="fade-in" animate.leave="fade-out" } diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.html b/frontend/src/app/features/rules/pages/events/rule-event.component.html index 1bb2b89f6..3d92c41d5 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.html +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.html @@ -80,7 +80,7 @@
- +
diff --git a/frontend/src/app/features/rules/pages/rule/branch.component.html b/frontend/src/app/features/rules/pages/rule/branch.component.html index f4a7f19e8..ea690c6bd 100644 --- a/frontend/src/app/features/rules/pages/rule/branch.component.html +++ b/frontend/src/app/features/rules/pages/rule/branch.component.html @@ -14,7 +14,11 @@ @if (branchItems.length > 0) { @for (item of branchItems; track item.id; let last = $last; let i = $index) {
-
@@ -60,7 +64,11 @@ @if (last && item.step.step.stepType !== "If") {
-
@@ -68,7 +76,7 @@ } } @else {
-
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 c7e68057d..8ff719c07 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 @@ -2,7 +2,7 @@
-

{{ "common.rule" | sqxTranslate }}

@@ -18,7 +18,8 @@ confirmText="i18n:rules.triggerConfirmText" confirmTitle="i18n:rules.triggerConfirmTitle" [disabled]="!rule?.canTrigger" - (sqxConfirmClick)="trigger()"> + (sqxConfirmClick)="trigger()" + type="button"> } @@ -67,7 +68,8 @@ } diff --git a/frontend/src/app/features/rules/pages/rules/rule.component.html b/frontend/src/app/features/rules/pages/rules/rule.component.html index 399b5919c..df33ee5a3 100644 --- a/frontend/src/app/features/rules/pages/rules/rule.component.html +++ b/frontend/src/app/features/rules/pages/rules/rule.component.html @@ -22,15 +22,15 @@ @if (rule.canUpdate) { - + } @if (rule.canEnable) { - + } @if (rule.canDisable) { - + } @@ -42,7 +42,8 @@ confirmRememberKey="runRule" confirmText="i18n:rules.runRuleConfirmText" confirmTitle="i18n:rules.runRuleConfirmTitle" - (sqxConfirmClick)="run()"> + (sqxConfirmClick)="run()" + type="button"> {{ "rules.run" | sqxTranslate }} } @@ -53,7 +54,8 @@ confirmRememberKey="runRuleFromSnapshots" confirmText="i18n:rules.runRuleConfirmText" confirmTitle="i18n:rules.runRuleConfirmTitle" - (sqxConfirmClick)="runFromSnapshots()"> + (sqxConfirmClick)="runFromSnapshots()" + type="button"> {{ "rules.runFromSnapshots" | sqxTranslate }} } @@ -67,7 +69,8 @@ confirmRememberKey="deleteRule" confirmText="i18n:rules.deleteConfirmText" confirmTitle="i18n:rules.deleteConfirmTitle" - (sqxConfirmClick)="delete()"> + (sqxConfirmClick)="delete()" + type="button"> {{ "common.delete" | sqxTranslate }} } @@ -118,7 +121,8 @@ confirmText="i18n:rules.triggerConfirmText" confirmTitle="i18n:rules.triggerConfirmTitle" [disabled]="!rule.canTrigger" - (sqxConfirmClick)="trigger()"> + (sqxConfirmClick)="trigger()" + type="button"> } @else { 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 cfc521ddc..bdd5add33 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 @@ -15,7 +15,7 @@ @if (rulesState.runningRule | async; as runningRule) {
{{ "rules.runningRule" | sqxTranslate: { name: runningRule.name || "Unnamed Rule" } }} - +
} diff --git a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.html b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.html index 37bed2fee..f28c47d81 100644 --- a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.html +++ b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.html @@ -1,6 +1,5 @@ - @if (text) { - +
{{ text | sqxTranslate }} @@ -10,7 +9,7 @@ } @for (error of filteredErrors; track error; let last = $last) { - +
{{ error | sqxTranslate }} diff --git a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html index 950a6bccc..52d6a3887 100644 --- a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html +++ b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html @@ -71,7 +71,7 @@ text="i18n:rules.simulation.eventConditionEvaluated" /> @if (event.flowState) { - + } } } diff --git a/frontend/src/app/features/rules/shared/state-attempt.component.html b/frontend/src/app/features/rules/shared/state-attempt.component.html index be0676ddc..515e31ad6 100644 --- a/frontend/src/app/features/rules/shared/state-attempt.component.html +++ b/frontend/src/app/features/rules/shared/state-attempt.component.html @@ -2,19 +2,19 @@ @if (attempt) {
- {{ attempt.started.toISOString() || '-' }} + {{ attempt.started.toISOString() || "-" }}
- +
- {{ attempt.completed.toISOString() || '-' }} + {{ attempt.completed.toISOString() || "-" }}
- + @if (attempt.error) { } - + @if (output) { diff --git a/frontend/src/app/features/rules/shared/state-details.component.html b/frontend/src/app/features/rules/shared/state-details.component.html index 2a1d2d400..8ecb002d6 100644 --- a/frontend/src/app/features/rules/shared/state-details.component.html +++ b/frontend/src/app/features/rules/shared/state-details.component.html @@ -19,6 +19,6 @@ [isLast]="$last" [isNext]="state.nextStepId === item.id" [state]="state.steps[item.id]" - [stepInfo]="availableSteps[item.step.step.stepType]" - [stepDefinition]="state.definition.steps[item.id].step" /> + [stepDefinition]="state.definition.steps[item.id].step" + [stepInfo]="availableSteps[item.step.step.stepType]" /> } diff --git a/frontend/src/app/features/rules/shared/state-step.component.html b/frontend/src/app/features/rules/shared/state-step.component.html index f2f03e453..53c502f8d 100644 --- a/frontend/src/app/features/rules/shared/state-step.component.html +++ b/frontend/src/app/features/rules/shared/state-step.component.html @@ -21,7 +21,7 @@
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html b/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html index 8b33a126c..bc9c0bf7f 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html @@ -74,7 +74,7 @@ @if (!editForm) {
- - - + +
@@ -96,7 +96,7 @@ @if (editForm) {
- - +
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.html b/frontend/src/app/features/schemas/pages/schema/fields/field.component.html index d0e82e64a..a527a4a01 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.html @@ -56,19 +56,19 @@ @if (field.properties.isContentField) { @if (field.canEnable) { - + } @if (field.canDisable) { - + } @if (field.canHide) { - + } @if (field.canShow) { - + } } @@ -79,7 +79,8 @@ confirmRememberKey="lockField" confirmText="i18n:schemas.field.lockConfirmText" confirmTitle="i18n:schemas.field.lockConfirmTitle" - (sqxConfirmClick)="lockField()"> + (sqxConfirmClick)="lockField()" + type="button"> {{ "schemas.field.lock" | sqxTranslate }} } @@ -91,7 +92,8 @@ confirmRememberKey="deleteField" confirmText="i18n:schemas.field.deleteConfirmText" confirmTitle="i18n:schemas.field.deleteConfirmTitle" - (sqxConfirmClick)="deleteField()"> + (sqxConfirmClick)="deleteField()" + type="button"> {{ "common.delete" | sqxTranslate }}
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html index 8657f0e2f..1a50a4a64 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html @@ -1,25 +1,27 @@
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html b/frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html index 4c88742ac..486db462f 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html @@ -1,5 +1,10 @@
- + 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 f3590541f..c20049d61 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 @@ -76,7 +76,7 @@ @if (schemasState.canCreate) { - + } @@ -86,7 +86,8 @@ confirmRememberKey="deleteSchema" confirmText="i18n:schemas.deleteConfirmText" confirmTitle="i18n:schemas.deleteConfirmTitle" - (sqxConfirmClick)="deleteSchema()"> + (sqxConfirmClick)="deleteSchema()" + type="button"> {{ "common.delete" | sqxTranslate }} diff --git a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html index 6910a711f..349fd1d4c 100644 --- a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html +++ b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html @@ -3,7 +3,7 @@
@if (planInfo.isYearlySelected) { - + } @if (!planInfo.isYearlySelected) { @@ -57,7 +58,8 @@ [confirmText]="planInfo.plan.yearlyConfirmText!" confirmTitle="i18n:plans.changeConfirmTitle" [disabled]="(plansState.locked | async) !== 'None'" - (sqxConfirmClick)="changeYearly()"> + (sqxConfirmClick)="changeYearly()" + type="button"> {{ "plans.change" | sqxTranslate }} } diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html index 2efc7222a..2a78bb7e6 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.html @@ -6,7 +6,8 @@ [class.active]="step.name === workflow.dto.initial" [class.enabled]="step.name !== workflow.dto.initial && !step.isLocked" (click)="makeInitial()" - [disabled]="step.name === workflow.dto.initial || step.isLocked || disabled"> + [disabled]="step.name === workflow.dto.initial || step.isLocked || disabled" + type="button"> @if (!step.isLocked) { } @@ -69,7 +70,7 @@
- +
} diff --git a/frontend/src/app/features/settings/pages/workflows/workflow.component.html b/frontend/src/app/features/settings/pages/workflows/workflow.component.html index acc3683a0..0b55f2cef 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow.component.html @@ -37,13 +37,13 @@
diff --git a/frontend/src/app/features/teams/pages/plans/plan.component.html b/frontend/src/app/features/teams/pages/plans/plan.component.html index 7c95e8595..cc142be3e 100644 --- a/frontend/src/app/features/teams/pages/plans/plan.component.html +++ b/frontend/src/app/features/teams/pages/plans/plan.component.html @@ -22,7 +22,7 @@
@if (planInfo.isSelected) { - + } @if (!planInfo.isSelected) { @@ -33,7 +33,8 @@ [confirmText]="planInfo.plan.confirmText" confirmTitle="i18n:plans.changeConfirmTitle" [disabled]="(plansState.locked | async) !== 'None'" - (sqxConfirmClick)="changeMonthly()"> + (sqxConfirmClick)="changeMonthly()" + type="button"> {{ "plans.change" | sqxTranslate }} } @@ -47,7 +48,7 @@ @if (planInfo.isYearlySelected) { - + } @if (!planInfo.isYearlySelected) { @@ -57,7 +58,8 @@ [confirmText]="planInfo.plan.yearlyConfirmText!" confirmTitle="i18n:plans.changeConfirmTitle" [disabled]="(plansState.locked | async) !== 'None'" - (sqxConfirmClick)="changeYearly()"> + (sqxConfirmClick)="changeYearly()" + type="button"> {{ "plans.change" | sqxTranslate }} } diff --git a/frontend/src/app/framework/angular/layout.component.html b/frontend/src/app/framework/angular/layout.component.html index 6e0837cbc..16f171c0a 100644 --- a/frontend/src/app/framework/angular/layout.component.html +++ b/frontend/src/app/framework/angular/layout.component.html @@ -25,7 +25,7 @@ } @else { - + } } @@ -63,7 +63,12 @@ - @@ -21,7 +22,8 @@ [disabled]="disabled" (sqxConfirmClick)="action.emit()" [tabIndex]="tabIndex" - [title]="tooltip | sqxTranslate"> + [title]="tooltip | sqxTranslate" + type="button"> @if (icon) { } diff --git a/frontend/src/app/framework/angular/menu.component.html b/frontend/src/app/framework/angular/menu.component.html index 51375f2c2..7a224b210 100644 --- a/frontend/src/app/framework/angular/menu.component.html +++ b/frontend/src/app/framework/angular/menu.component.html @@ -23,7 +23,13 @@ } -
+
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 698512d9d..a73e8611b 100644 --- a/frontend/src/app/framework/angular/modals/dialog-renderer.component.html +++ b/frontend/src/app/framework/angular/modals/dialog-renderer.component.html @@ -22,7 +22,12 @@
@for (notification of snapshot.notifications; track notification) { -