Browse Source

Improve queries

pull/1293/head
Sebastian Stehle 7 months ago
parent
commit
1b35cb3400
  1. 4
      backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs
  2. 7
      backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs
  3. 58
      backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs
  4. 68
      backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs
  5. 96
      backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs
  6. 2
      backend/src/Squidex.Infrastructure/Queries/QueryModel.cs
  7. 42
      backend/src/Squidex.Web/Pipeline/SchemaResolver.cs
  8. 24
      backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs
  9. 3
      frontend/package.json
  10. 4
      frontend/src/app/_theme.html
  11. 2
      frontend/src/app/features/api/pages/graphql/graphql-page.component.html
  12. 3
      frontend/src/app/features/apps/pages/app.component.html
  13. 8
      frontend/src/app/features/apps/pages/onboarding-dialog.component.html
  14. 3
      frontend/src/app/features/apps/pages/team.component.html
  15. 2
      frontend/src/app/features/assets/pages/asset-tag-dialog.component.html
  16. 6
      frontend/src/app/features/assets/pages/asset-tags.component.html
  17. 4
      frontend/src/app/features/content/pages/content/content-event.component.html
  18. 18
      frontend/src/app/features/content/pages/content/content-history-page.component.html
  19. 15
      frontend/src/app/features/content/pages/content/content-page.component.html
  20. 2
      frontend/src/app/features/content/pages/content/editor/content-editor.component.html
  21. 6
      frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html
  22. 2
      frontend/src/app/features/content/shared/forms/array-editor.component.html
  23. 2
      frontend/src/app/features/content/shared/forms/component.component.html
  24. 2
      frontend/src/app/features/content/shared/forms/field-languages.component.html
  25. 2
      frontend/src/app/features/content/shared/forms/user-info-editor.component.html
  26. 7
      frontend/src/app/features/content/shared/list/content.component.html
  27. 2
      frontend/src/app/features/content/shared/preview-button.component.html
  28. 2
      frontend/src/app/features/content/shared/references/reference-item.component.html
  29. 4
      frontend/src/app/features/content/shared/references/references-tags.component.ts
  30. 7
      frontend/src/app/features/dashboard/pages/dashboard-config.component.html
  31. 3
      frontend/src/app/features/dashboard/pages/dashboard-page.component.html
  32. 2
      frontend/src/app/features/rules/pages/events/rule-event.component.html
  33. 14
      frontend/src/app/features/rules/pages/rule/branch.component.html
  34. 8
      frontend/src/app/features/rules/pages/rule/rule-page.component.html
  35. 18
      frontend/src/app/features/rules/pages/rules/rule.component.html
  36. 2
      frontend/src/app/features/rules/pages/rules/rules-page.component.html
  37. 5
      frontend/src/app/features/rules/pages/simulator/rule-transition.component.html
  38. 2
      frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html
  39. 10
      frontend/src/app/features/rules/shared/state-attempt.component.html
  40. 4
      frontend/src/app/features/rules/shared/state-details.component.html
  41. 4
      frontend/src/app/features/rules/shared/state-step.component.html
  42. 26
      frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.html
  43. 10
      frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html
  44. 14
      frontend/src/app/features/schemas/pages/schema/fields/field.component.html
  45. 10
      frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html
  46. 7
      frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html
  47. 5
      frontend/src/app/features/schemas/pages/schema/schema-page.component.html
  48. 2
      frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html
  49. 4
      frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.html
  50. 8
      frontend/src/app/features/schemas/pages/schemas/schema-form.component.html
  51. 2
      frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html
  52. 4
      frontend/src/app/features/settings/pages/clients/client-connect-form.component.html
  53. 2
      frontend/src/app/features/settings/pages/clients/client.component.html
  54. 6
      frontend/src/app/features/settings/pages/more/more-page.component.html
  55. 10
      frontend/src/app/features/settings/pages/plans/plan.component.html
  56. 5
      frontend/src/app/features/settings/pages/workflows/workflow-step.component.html
  57. 6
      frontend/src/app/features/settings/pages/workflows/workflow.component.html
  58. 2
      frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html
  59. 10
      frontend/src/app/features/teams/pages/plans/plan.component.html
  60. 9
      frontend/src/app/framework/angular/layout.component.html
  61. 6
      frontend/src/app/framework/angular/menu-item.component.html
  62. 8
      frontend/src/app/framework/angular/menu.component.html
  63. 7
      frontend/src/app/framework/angular/modals/dialog-renderer.component.html
  64. 2
      frontend/src/app/framework/angular/modals/modal-dialog.component.html
  65. 13
      frontend/src/app/framework/angular/modals/tour-template.component.html
  66. 4
      frontend/src/app/framework/angular/pager.component.html
  67. 20
      frontend/src/app/shared/components/assets/asset-dialog.component.html
  68. 5
      frontend/src/app/shared/components/assets/asset-folder.component.html
  69. 2
      frontend/src/app/shared/components/assets/asset-path.component.html
  70. 10
      frontend/src/app/shared/components/assets/asset.component.html
  71. 2
      frontend/src/app/shared/components/cards/api-calls-card.component.html
  72. 2
      frontend/src/app/shared/components/contents/content-value.component.html
  73. 2
      frontend/src/app/shared/components/references/content-selector.component.html
  74. 4
      frontend/src/app/shared/components/search/query-list.component.html
  75. 2
      frontend/src/app/shared/components/table-header.component.html
  76. 5
      frontend/src/app/shared/components/tour-guide.component.html
  77. 1
      frontend/src/app/shared/model/custom.ts
  78. 1
      frontend/src/app/shared/model/generated.ts
  79. 12
      frontend/src/app/shell/pages/internal/profile-menu.component.html

4
backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentSqlQueryBuilder.cs

@ -10,12 +10,12 @@ using Squidex.Text;
namespace Squidex.Domain.Apps.Entities.Contents; 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) public override PropertyPath Visit(PropertyPath path)
{ {
var elements = path.ToList(); var elements = path.ToList();
elements[0] = elements[0].ToPascalCase(); elements[0] = elements[0].ToPascalCase();
return new PropertyPath(elements); return new PropertyPath(elements);

7
backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs

@ -144,7 +144,7 @@ public class SqlDialect
throw new NotSupportedException(); 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) if (!value.IsList && value.ValueType == ClrValueType.Null)
{ {
@ -170,6 +170,11 @@ public class SqlDialect
if (op == CompareOperator.In) if (op == CompareOperator.In)
{ {
if (withoutBraces)
{
return string.Join(", ", parameters);
}
return $"({string.Join(", ", parameters)})"; return $"({string.Join(", ", parameters)})";
} }

58
backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text;
using MySqlConnector; using MySqlConnector;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
@ -85,7 +86,13 @@ public sealed class MySqlDialect : SqlDialect
var sqlOrder = FormatOrder(order); var sqlOrder = FormatOrder(order);
var sqlPath = path.JsonPath(); 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); return base.OrderBy(path, order, isJson);
@ -100,15 +107,54 @@ public sealed class MySqlDialect : SqlDialect
{ {
if (isJson) if (isJson)
{ {
var sqlPath = path.JsonPath();
var sqlOp = FormatOperator(op, value);
var sqlRhs = FormatValues(op, value, queryParameters);
var isBoolean = value.ValueType is ClrValueType.Boolean; var isBoolean = value.ValueType is ClrValueType.Boolean;
if (isBoolean)
string ScalarCondition()
{ {
var sqlPath = path.JsonPath(); if (isBoolean)
var sqlOp = FormatOperator(op, value); {
var sqlRhs = FormatValues(op, value, queryParameters); 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); return base.Where(path, op, value, queryParameters, isJson);

68
backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text;
using Npgsql; using Npgsql;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using Squidex.Providers.Postgres.App; using Squidex.Providers.Postgres.App;
@ -71,27 +72,74 @@ public class PostgresDialect : SqlDialect
{ {
if (isJson) 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.Single or
ClrValueType.Double or ClrValueType.Double or
ClrValueType.Int32 or ClrValueType.Int32 or
ClrValueType.Int64; ClrValueType.Int64;
if (issNumeric)
string ScalarCondition()
{ {
var sqlOp = FormatOperator(op, value); if (isNumeric)
var sqlRhs = FormatValues(op, value, queryParameters); {
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; string ToJsonbValue()
if (isBoolean) {
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); return $"""
var sqlRhs = FormatValues(op, value, queryParameters); 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); return base.Where(path, op, value, queryParameters, isJson);

96
backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
using SqlException = Microsoft.Data.SqlClient.SqlException; using SqlException = Microsoft.Data.SqlClient.SqlException;
@ -87,45 +88,96 @@ public sealed class SqlServerDialect : SqlDialect
{ {
if (isJson) 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.Single or
ClrValueType.Double or ClrValueType.Double or
ClrValueType.Int32 or ClrValueType.Int32 or
ClrValueType.Int64; ClrValueType.Int64;
if (issNumeric)
string ScalarCondition()
{ {
var sqlPath = path.JsonPath(); if (isNull)
var sqlOp = FormatOperator(op, value); {
var sqlRhs = FormatValues(op, value, queryParameters); 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)
if (isBoolean) {
{ return $"IIF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}";
var sqlPath = path.JsonPath(); }
var sqlOp = FormatOperator(op, value);
var sqlRhs = FormatValues(op, value, queryParameters);
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; string ArrayCondition(string field, string op)
if (isNull)
{ {
var sqlPath = path.JsonPath(); if (isNumeric)
if (op == CompareOperator.Equals)
{ {
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); return base.Where(path, op, value, queryParameters, isJson);

2
backend/src/Squidex.Infrastructure/Queries/QueryModel.cs

@ -11,7 +11,7 @@ public sealed class QueryModel
{ {
public static readonly IReadOnlyDictionary<FilterSchemaType, IReadOnlyList<CompareOperator>> DefaultOperators = new Dictionary<FilterSchemaType, IReadOnlyList<CompareOperator>> public static readonly IReadOnlyDictionary<FilterSchemaType, IReadOnlyList<CompareOperator>> DefaultOperators = new Dictionary<FilterSchemaType, IReadOnlyList<CompareOperator>>
{ {
[FilterSchemaType.Any] = Enum.GetValues(typeof(CompareOperator)).OfType<CompareOperator>().ToList(), [FilterSchemaType.Any] = Enum.GetValues<CompareOperator>().ToList(),
[FilterSchemaType.Boolean] = [FilterSchemaType.Boolean] =
[ [
CompareOperator.Equals, CompareOperator.Equals,

42
backend/src/Squidex.Web/Pipeline/SchemaResolver.cs

@ -31,35 +31,29 @@ public sealed class SchemaResolver : IAsyncActionFilter
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ {
var app = context.HttpContext.Features.Get<App>(); var app = context.HttpContext.Features.Get<App>();
if (app != null && context.RouteData.Values.TryGetValue("schema", out var schemaValue))
if (app != null)
{ {
if (context.RouteData.Values.TryGetValue("schema", out var schemaValue)) var schemaIdOrName = schemaValue?.ToString();
if (string.IsNullOrWhiteSpace(schemaIdOrName))
{ {
var schemaIdOrName = schemaValue?.ToString(); context.Result = new NotFoundResult();
return;
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;
}
if (context.ActionDescriptor.EndpointMetadata.Any(x => x is SchemaMustBePublishedAttribute) && !schema.IsPublished) var schema = await GetSchemaAsync(app.Id, schemaIdOrName, context.HttpContext.User);
{ if (schema == null)
context.Result = new NotFoundResult(); {
return; 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(); await next();

24
backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/EFQueryTests.cs

@ -61,7 +61,7 @@ public abstract class EFQueryTests<TContext>(ISqlFixture<TContext> fixture)
mixed = true; mixed = true;
break; break;
case 4: case 4:
mixed = new List<object> { i }; mixed = new List<object> { i, true };
break; break;
default: default:
mixed = new Dictionary<string, object>(); mixed = new Dictionary<string, object>();
@ -339,6 +339,28 @@ public abstract class EFQueryTests<TContext>(ISqlFixture<TContext> fixture)
Assert.Equal([3, 5, 7], actual.Order().ToArray()); 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<int> { 3, 5, 7 }),
});
Assert.Equal([3, 5, 7], actual.Order().ToArray());
}
[Fact] [Fact]
public async Task Should_filter_with_many_in_mixed_json() public async Task Should_filter_with_many_in_mixed_json()
{ {

3
frontend/package.json

@ -12,7 +12,8 @@
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"storybook": "ng run squidex:storybook", "storybook": "ng run squidex:storybook",
"storybook-build": "ng run squidex:build-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, "private": true,
"dependencies": { "dependencies": {

4
frontend/src/app/_theme.html

@ -1071,8 +1071,8 @@
<div class="bs-component" style="margin-bottom: 40px"> <div class="bs-component" style="margin-bottom: 40px">
<span class="badge badge-primary">Primary</span> <span class="badge badge-secondary">Secondary</span> <span class="badge badge-primary">Primary</span> <span class="badge badge-secondary">Secondary</span>
<span class="badge badge-success">Success</span> <span class="badge badge-danger">Danger</span> <span class="badge badge-success">Success</span> <span class="badge badge-danger">Danger</span>
<span class="badge badge-warning">Warning</span> <span class="badge badge-info">Info</span> <span class="badge badge-warning">Warning</span> <span class="badge badge-info">Info</span> <span class="badge badge-light">Light</span>
<span class="badge badge-light">Light</span> <span class="badge badge-dark">Dark</span> <span class="badge badge-dark">Dark</span>
</div> </div>
<div class="bs-component"> <div class="bs-component">

2
frontend/src/app/features/api/pages/graphql/graphql-page.component.html

@ -3,7 +3,7 @@
<div #graphiQLContainer inner sqxTourStep="graphQLExplorer"></div> <div #graphiQLContainer inner sqxTourStep="graphQLExplorer"></div>
@if (clientsReadable) { @if (clientsReadable) {
<button class="btn btn-simple btn-options" (click)="clientsDialog.show()"><i class="icon-clients"></i></button> <button class="btn btn-simple btn-options" (click)="clientsDialog.show()" type="button"><i class="icon-clients"></i></button>
} }
</sqx-layout> </sqx-layout>
<sqx-modal-dialog (dialogClose)="clientsDialog.hide()" *sqxModal="clientsDialog"> <sqx-modal-dialog (dialogClose)="clientsDialog.hide()" *sqxModal="clientsDialog">

3
frontend/src/app/features/apps/pages/app.component.html

@ -51,7 +51,8 @@
confirmRememberKey="leaveApp" confirmRememberKey="leaveApp"
confirmText="i18n:apps.leaveConfirmText" confirmText="i18n:apps.leaveConfirmText"
confirmTitle="i18n:apps.leaveConfirmTitle" confirmTitle="i18n:apps.leaveConfirmTitle"
(sqxConfirmClick)="leave.emit(app)"> (sqxConfirmClick)="leave.emit(app)"
type="button">
{{ "apps.leave" | sqxTranslate }} {{ "apps.leave" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

8
frontend/src/app/features/apps/pages/onboarding-dialog.component.html

@ -3,7 +3,7 @@
<div class="squid d-flex align-items-center justify-content-center"><img src="./images/squid.svg" /></div> <div class="squid d-flex align-items-center justify-content-center"><img src="./images/squid.svg" /></div>
</ng-container> </ng-container>
<ng-container content> <ng-container content>
<button class="header-right modal-close btn-inline-link" (click)="cancel()">{{ "tour.skip" | sqxTranslate }}</button> <button class="header-right modal-close btn-inline-link" (click)="cancel()" type="button">{{ "tour.skip" | sqxTranslate }}</button>
@if (step === 0) { @if (step === 0) {
<div class="onboarding-step"> <div class="onboarding-step">
<img class="header-left" animate.enter="fade-in" animate.leave="fade-out" src="./images/logo-white-small.png" /> <img class="header-left" animate.enter="fade-in" animate.leave="fade-out" src="./images/logo-white-small.png" />
@ -15,7 +15,7 @@
<div inline="false" [sqxMarkdown]="'tour.stepIntroText' | sqxTranslate"></div> <div inline="false" [sqxMarkdown]="'tour.stepIntroText' | sqxTranslate"></div>
<div class="mt-4"> <div class="mt-4">
<button class="btn btn-success" (click)="next()">{{ "tour.stepIntroNext" | sqxTranslate }}</button> <button class="btn btn-success" (click)="next()" type="button">{{ "tour.stepIntroNext" | sqxTranslate }}</button>
</div> </div>
</div> </div>
</div> </div>
@ -78,8 +78,8 @@
<div [sqxMarkdown]="'tour.stepTourText' | sqxTranslate"></div> <div [sqxMarkdown]="'tour.stepTourText' | sqxTranslate"></div>
<div class="mt-4"> <div class="mt-4">
<button class="btn btn-success" (click)="start()">{{ "tour.startYes" | sqxTranslate }}</button> <button class="btn btn-success" (click)="start()" type="button">{{ "tour.startYes" | sqxTranslate }}</button>
<button class="btn btn-outline-secondary ms-2" (click)="cancel()"> <button class="btn btn-outline-secondary ms-2" (click)="cancel()" type="button">
{{ "tour.startNo" | sqxTranslate }} {{ "tour.startNo" | sqxTranslate }}
</button> </button>
</div> </div>

3
frontend/src/app/features/apps/pages/team.component.html

@ -24,7 +24,8 @@
confirmRememberKey="leaveApp" confirmRememberKey="leaveApp"
confirmText="i18n:teams.leaveConfirmText" confirmText="i18n:teams.leaveConfirmText"
confirmTitle="i18n:teams.leaveConfirmTitle" confirmTitle="i18n:teams.leaveConfirmTitle"
(sqxConfirmClick)="leave.emit(team)"> (sqxConfirmClick)="leave.emit(team)"
type="button">
{{ "teams.leave" | sqxTranslate }} {{ "teams.leave" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

2
frontend/src/app/features/assets/pages/asset-tag-dialog.component.html

@ -3,7 +3,7 @@
<ng-container title> {{ "common.renameTag" | sqxTranslate }} </ng-container> <ng-container title> {{ "common.renameTag" | sqxTranslate }} </ng-container>
<ng-container content> <ng-container content>
<sqx-form-error [error]="editForm.error | async" /> <sqx-form-error [error]="editForm.error | async" />
<sqx-form-row for="tagName" label="common.name" vertical required> <sqx-form-row for="tagName" label="common.name" required vertical>
<input class="form-control" id="tagName" autocomplete="off" formControlName="tagName" sqxFocusOnInit /> <input class="form-control" id="tagName" autocomplete="off" formControlName="tagName" sqxFocusOnInit />
</sqx-form-row> </sqx-form-row>
</ng-container> </ng-container>

6
frontend/src/app/features/assets/pages/asset-tags.component.html

@ -1,11 +1,11 @@
<div class="nav nav-light flex-column"> <div class="nav nav-light flex-column">
<div class="nav-item"> <div class="nav-item">
<button class="nav-link" [class.active]="isEmpty()" (click)="tagsReset.emit()">{{ "common.tagsAll" | sqxTranslate }}</button> <button class="nav-link" [class.active]="isEmpty()" (click)="tagsReset.emit()" type="button">{{ "common.tagsAll" | sqxTranslate }}</button>
</div> </div>
@for (tag of tags; track tag.name) { @for (tag of tags; track tag.name) {
<div class="nav-item"> <div class="nav-item">
<button class="nav-link" [class.active]="isSelected(tag)" (click)="toggle.emit(tag.name)"> <button class="nav-link" [class.active]="isSelected(tag)" (click)="toggle.emit(tag.name)" type="button">
<div class="row g-0"> <div class="row g-0">
<div class="col"> <div class="col">
<span class="truncate">{{ tag.name }}</span> <span class="truncate">{{ tag.name }}</span>
@ -15,7 +15,7 @@
<div class="badge badge-secondary rounded-pill">{{ tag.count }}</div> <div class="badge badge-secondary rounded-pill">{{ tag.count }}</div>
@if (canRename) { @if (canRename) {
<button class="btn-sm btn-text-secondary btn-rename" (click)="renameTag(tag)" sqxStopClick> <button class="btn-sm btn-text-secondary btn-rename" (click)="renameTag(tag)" sqxStopClick type="button">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</button> </button>
} }

4
frontend/src/app/features/content/pages/content/content-event.component.html

@ -14,8 +14,8 @@
<div class="event-created">{{ event.created | sqxFromNow }}</div> <div class="event-created">{{ event.created | sqxFromNow }}</div>
@if (canLoadOrCompare) { @if (canLoadOrCompare) {
<button class="event-load btn-inline-link" (click)="dataLoad.emit()">{{ "contents.loadContent" | sqxTranslate }}</button> &middot; <button class="event-load btn-inline-link" (click)="dataLoad.emit()" type="button">{{ "contents.loadContent" | sqxTranslate }}</button> &middot;
<button class="event-load btn-inline-link" (click)="dataCompare.emit()">{{ "contents.versionCompare" | sqxTranslate }}</button> <button class="event-load btn-inline-link" (click)="dataCompare.emit()" type="button">{{ "contents.versionCompare" | sqxTranslate }}</button>
} }
</div> </div>
</div> </div>

18
frontend/src/app/features/content/pages/content/content-history-page.component.html

@ -18,7 +18,7 @@
@if (content.canDraftCreate || content.canDraftDelete) { @if (content.canDraftCreate || content.canDraftDelete) {
<div class="section mb-4"> <div class="section mb-4">
@if (!content.newStatus) { @if (!content.newStatus) {
<button class="btn btn-success btn-block" (click)="createDraft()">{{ "contents.draftNew" | sqxTranslate }}</button> <button class="btn btn-success btn-block" (click)="createDraft()" type="button">{{ "contents.draftNew" | sqxTranslate }}</button>
} @else { } @else {
<label>{{ "contents.draftStatus" | sqxTranslate }}</label> <label>{{ "contents.draftStatus" | sqxTranslate }}</label>
<button <button
@ -36,7 +36,7 @@
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdownNew; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdownNew; closeAlways: true">
@if (content.statusUpdates.length > 0) { @if (content.statusUpdates.length > 0) {
@for (info of content.statusUpdates; track info) { @for (info of content.statusUpdates; track info) {
<button class="dropdown-item" (click)="changeStatus(info.status)"> <button class="dropdown-item" (click)="changeStatus(info.status)" type="button">
{{ "common.statusChangeTo" | sqxTranslate }} {{ "common.statusChangeTo" | sqxTranslate }}
<i class="icon-circle icon-sm" [style.color]="info.color"></i> {{ info.status }} <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{ info.status }}
</button> </button>
@ -50,7 +50,8 @@
confirmRememberKey="deleteDraft" confirmRememberKey="deleteDraft"
confirmText="i18n:contents.deleteVersionConfirmText" confirmText="i18n:contents.deleteVersionConfirmText"
confirmTitle="i18n:contents.deleteConfirmTitle" confirmTitle="i18n:contents.deleteConfirmTitle"
(sqxConfirmClick)="deleteDraft()"> (sqxConfirmClick)="deleteDraft()"
type="button">
{{ "contents.versionDelete" | sqxTranslate }} {{ "contents.versionDelete" | sqxTranslate }}
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -60,7 +61,8 @@
confirmRememberKey="deleteContent" confirmRememberKey="deleteContent"
confirmText="i18n:contents.deleteConfirmText" confirmText="i18n:contents.deleteConfirmText"
confirmTitle="i18n:contents.deleteConfirmTitle" confirmTitle="i18n:contents.deleteConfirmTitle"
(sqxConfirmClick)="delete()"> (sqxConfirmClick)="delete()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>
@ -88,7 +90,7 @@
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true">
@if (content.statusUpdates.length > 0) { @if (content.statusUpdates.length > 0) {
@for (info of content.statusUpdates; track info) { @for (info of content.statusUpdates; track info) {
<button class="dropdown-item" (click)="changeStatus(info.status)"> <button class="dropdown-item" (click)="changeStatus(info.status)" type="button">
{{ "common.statusChangeTo" | sqxTranslate }} {{ "common.statusChangeTo" | sqxTranslate }}
<sqx-content-status layout="text" small="true" [status]="info.status" [statusColor]="info.color" /> <sqx-content-status layout="text" small="true" [status]="info.status" [statusColor]="info.color" />
</button> </button>
@ -102,7 +104,8 @@
confirmRememberKey="cancelStatus" confirmRememberKey="cancelStatus"
confirmText="i18n:contents.cancelStatusConfirmText" confirmText="i18n:contents.cancelStatusConfirmText"
confirmTitle="i18n:contents.cancelStatusConfirmTitle" confirmTitle="i18n:contents.cancelStatusConfirmTitle"
(sqxConfirmClick)="cancelStatus()"> (sqxConfirmClick)="cancelStatus()"
type="button">
{{ "contents.cancelStatus" | sqxTranslate }} {{ "contents.cancelStatus" | sqxTranslate }}
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -112,7 +115,8 @@
confirmRememberKey="deleteContent" confirmRememberKey="deleteContent"
confirmText="i18n:contents.deleteConfirmText" confirmText="i18n:contents.deleteConfirmText"
confirmTitle="i18n:contents.deleteConfirmTitle" confirmTitle="i18n:contents.deleteConfirmTitle"
(sqxConfirmClick)="delete()"> (sqxConfirmClick)="delete()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

15
frontend/src/app/features/content/pages/content/content-page.component.html

@ -4,7 +4,7 @@
<ng-container title> <ng-container title>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@if (schema.type !== "Singleton") { @if (schema.type !== "Singleton") {
<button class="btn btn-text-secondary" aria-labelledby="content-back" (click)="back()"> <button class="btn btn-text-secondary" aria-labelledby="content-back" (click)="back()" type="button">
<span id="content-back" hidden>{{ "common.back" | sqxTranslate }}</span> <i class="icon-angle-left"></i> <span id="content-back" hidden>{{ "common.back" | sqxTranslate }}</span> <i class="icon-angle-left"></i>
</button> </button>
} }
@ -75,7 +75,7 @@
</button> </button>
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true">
@if (content.canClone) { @if (content.canClone) {
<button class="dropdown-item" (click)="clone()"> <button class="dropdown-item" (click)="clone()" type="button">
{{ "common.clone" | sqxTranslate }} {{ "common.clone" | sqxTranslate }}
</button> </button>
} }
@ -86,7 +86,8 @@
confirmRememberKey="deleteContent" confirmRememberKey="deleteContent"
confirmText="i18n:contents.deleteConfirmText" confirmText="i18n:contents.deleteConfirmText"
confirmTitle="i18n:contents.deleteConfirmTitle" confirmTitle="i18n:contents.deleteConfirmTitle"
(sqxConfirmClick)="delete()"> (sqxConfirmClick)="delete()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
} }
@ -133,8 +134,8 @@
scrollY="true" scrollY="true"
[sqxAnchoredTo]="buttonSave" [sqxAnchoredTo]="buttonSave"
*sqxModal="saveOnlyDropdown; closeAlways: true"> *sqxModal="saveOnlyDropdown; closeAlways: true">
<button class="dropdown-item" (click)="saveAsDraft('Add')">{{ "common.saveAdd" | sqxTranslate }}</button> <button class="dropdown-item" (click)="saveAsDraft('Add')" type="button">{{ "common.saveAdd" | sqxTranslate }}</button>
<button class="dropdown-item" (click)="saveAsDraft('Close')">{{ "common.saveClose" | sqxTranslate }}</button> <button class="dropdown-item" (click)="saveAsDraft('Close')" type="button">{{ "common.saveClose" | sqxTranslate }}</button>
</sqx-dropdown-menu> </sqx-dropdown-menu>
</div> </div>
} }
@ -157,10 +158,10 @@
scrollY="true" scrollY="true"
[sqxAnchoredTo]="buttonSave" [sqxAnchoredTo]="buttonSave"
*sqxModal="savePublishDropdown; closeAlways: true"> *sqxModal="savePublishDropdown; closeAlways: true">
<button class="dropdown-item" (click)="saveAndPublish('Add')"> <button class="dropdown-item" (click)="saveAndPublish('Add')" type="button">
{{ "contents.saveAndPublishAdd" | sqxTranslate }} {{ "contents.saveAndPublishAdd" | sqxTranslate }}
</button> </button>
<button class="dropdown-item" (click)="saveAndPublish('Close')"> <button class="dropdown-item" (click)="saveAndPublish('Close')" type="button">
{{ "contents.saveAndPublishClose" | sqxTranslate }} {{ "contents.saveAndPublishClose" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

2
frontend/src/app/features/content/pages/content/editor/content-editor.component.html

@ -8,7 +8,7 @@
@if (contentVersion) { @if (contentVersion) {
<div class="alert alert-danger"> <div class="alert alert-danger">
<div class="float-end"> <div class="float-end">
<button (click)="loadLatest.emit()">{{ "contents.viewLatest" | sqxTranslate }}</button> <button (click)="loadLatest.emit()" type="button">{{ "contents.viewLatest" | sqxTranslate }}</button>
</div> </div>
@if (isDeleted) { @if (isDeleted) {

6
frontend/src/app/features/content/pages/content/inspecting/content-inspection.component.html

@ -3,19 +3,19 @@
@if (mode | async; as currentMode) { @if (mode | async; as currentMode) {
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="currentMode === 'Content'" (click)="setMode('Content')"> <button class="nav-link" [class.active]="currentMode === 'Content'" (click)="setMode('Content')" type="button">
{{ "contents.inspectContent" | sqxTranslate }} {{ "contents.inspectContent" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="currentMode === 'Data'" (click)="setMode('Data')"> <button class="nav-link" [class.active]="currentMode === 'Data'" (click)="setMode('Data')" type="button">
{{ "contents.inspectData" | sqxTranslate }} {{ "contents.inspectData" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="currentMode === 'FlatData'" (click)="setMode('FlatData')"> <button class="nav-link" [class.active]="currentMode === 'FlatData'" (click)="setMode('FlatData')" type="button">
{{ "contents.inspectFlatData" | sqxTranslate }} {{ "contents.inspectFlatData" | sqxTranslate }}
</button> </button>
</li> </li>

2
frontend/src/app/features/content/shared/forms/array-editor.component.html

@ -90,7 +90,7 @@
</button> </button>
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonSelect" *sqxModal="schemasDropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonSelect" *sqxModal="schemasDropdown; closeAlways: true">
@for (schema of schemasList; track schema) { @for (schema of schemasList; track schema) {
<button class="dropdown-item" (click)="addComponent(schema)">{{ schema.displayName }}</button> <button class="dropdown-item" (click)="addComponent(schema)" type="button">{{ schema.displayName }}</button>
} }
</sqx-dropdown-menu> </sqx-dropdown-menu>
} }

2
frontend/src/app/features/content/shared/forms/component.component.html

@ -28,7 +28,7 @@
</button> </button>
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonSelect" *sqxModal="schemasDropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonSelect" *sqxModal="schemasDropdown; closeAlways: true">
@for (schema of schemasList; track schema) { @for (schema of schemasList; track schema) {
<button class="dropdown-item" (click)="setSchema(schema)">{{ schema.displayName }}</button> <button class="dropdown-item" (click)="setSchema(schema)" type="button">{{ schema.displayName }}</button>
} }
</sqx-dropdown-menu> </sqx-dropdown-menu>
} }

2
frontend/src/app/features/content/shared/forms/field-languages.component.html

@ -9,7 +9,7 @@
} }
</button> </button>
} }
@if (formModel.field.properties.isComplexUI || !showAllControls) { @if (formModel.field.properties.isComplexUI || !showAllControls) {
<sqx-language-selector <sqx-language-selector
[exists]="formModel.translationStatus | async" [exists]="formModel.translationStatus | async"

2
frontend/src/app/features/content/shared/forms/user-info-editor.component.html

@ -22,7 +22,7 @@
<div ngPreserveWhitespaces>Authorization: ApiKey {{ rolesState.appName }}:{{ apiKey | async }}</div> <div ngPreserveWhitespaces>Authorization: ApiKey {{ rolesState.appName }}:{{ apiKey | async }}</div>
</sqx-code> </sqx-code>
<sqx-form-hint> <sqx-form-hint>
{{ 'common.or' | sqxTranslate }} {{ "common.or" | sqxTranslate }}
</sqx-form-hint> </sqx-form-hint>
<sqx-code> <sqx-code>
<div ngPreserveWhitespaces>ApiKey: {{ rolesState.appName }}:{{ apiKey | async }}</div> <div ngPreserveWhitespaces>ApiKey: {{ rolesState.appName }}:{{ apiKey | async }}</div>

7
frontend/src/app/features/content/shared/list/content.component.html

@ -22,14 +22,14 @@
<sqx-dropdown-menu position="bottom-start" scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu position="bottom-start" scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true">
<a class="dropdown-item" [routerLink]="link" sqxExternalLink target="_blank"> {{ "common.editInNewTab" | sqxTranslate }} </a> <a class="dropdown-item" [routerLink]="link" sqxExternalLink target="_blank"> {{ "common.editInNewTab" | sqxTranslate }} </a>
@for (info of content.statusUpdates; track info) { @for (info of content.statusUpdates; track info) {
<button class="dropdown-item" (click)="statusChange.emit(info.status)"> <button class="dropdown-item" (click)="statusChange.emit(info.status)" type="button">
{{ "common.statusChangeTo" | sqxTranslate }} {{ "common.statusChangeTo" | sqxTranslate }}
<sqx-content-status layout="text" small="true" [status]="info.status" [statusColor]="info.color" /> <sqx-content-status layout="text" small="true" [status]="info.status" [statusColor]="info.color" />
</button> </button>
} }
@if (cloneable) { @if (cloneable) {
<button class="dropdown-item" (click)="clone.emit(); dropdown.hide()">{{ "common.clone" | sqxTranslate }}</button> <button class="dropdown-item" (click)="clone.emit(); dropdown.hide()" type="button">{{ "common.clone" | sqxTranslate }}</button>
} }
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -39,7 +39,8 @@
confirmRememberKey="deleteContent" confirmRememberKey="deleteContent"
confirmText="i18n:contents.deleteConfirmText" confirmText="i18n:contents.deleteConfirmText"
confirmTitle="i18n:contents.deleteConfirmTitle" confirmTitle="i18n:contents.deleteConfirmTitle"
(sqxConfirmClick)="delete.emit()"> (sqxConfirmClick)="delete.emit()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

2
frontend/src/app/features/content/shared/preview-button.component.html

@ -9,7 +9,7 @@
<button class="btn btn-secondary dropdown-toggle" (click)="dropdown.toggle()" type="button"></button> <button class="btn btn-secondary dropdown-toggle" (click)="dropdown.toggle()" type="button"></button>
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonGroup" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonGroup" *sqxModal="dropdown; closeAlways: true">
@for (name of snapshot.previewNamesMore; track name) { @for (name of snapshot.previewNamesMore; track name) {
<button class="dropdown-item" (click)="follow(name)">{{ name }}</button> <button class="dropdown-item" (click)="follow(name)" type="button">{{ name }}</button>
} }
</sqx-dropdown-menu> </sqx-dropdown-menu>
</div> </div>

2
frontend/src/app/features/content/shared/references/reference-item.component.html

@ -44,7 +44,7 @@
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
<div class="reference-menu"> <div class="reference-menu">
<button class="btn btn-text-secondary" (click)="clone.emit()" title="{{ 'common.clone' | sqxTranslate }}"> <button class="btn btn-text-secondary" (click)="clone.emit()" title="{{ 'common.clone' | sqxTranslate }}" type="button">
<i class="icon-copy"></i> <i class="icon-copy"></i>
</button> </button>
@if (canRemove) { @if (canRemove) {

4
frontend/src/app/features/content/shared/references/references-tags.component.ts

@ -144,7 +144,6 @@ export class ReferencesTagsComponent extends StatefulControlComponent<State, Rea
for (const content of items) { for (const content of items) {
const index = this.contents.findIndex(x => x.id === content.id); const index = this.contents.findIndex(x => x.id === content.id);
if (index >= 0) { if (index >= 0) {
this.contents[index] = content; this.contents[index] = content;
} else { } else {
@ -153,12 +152,10 @@ export class ReferencesTagsComponent extends StatefulControlComponent<State, Rea
} }
this.isLoadingFailed = false; this.isLoadingFailed = false;
this.resetConverterState(true); this.resetConverterState(true);
}, },
error: () => { error: () => {
this.isLoadingFailed = true; this.isLoadingFailed = true;
this.resetConverterState(false); this.resetConverterState(false);
}, },
}); });
@ -171,7 +168,6 @@ export class ReferencesTagsComponent extends StatefulControlComponent<State, Rea
if (rebuild) { if (rebuild) {
const converter = new ReferencesTagsConverter(this.language, this.contents, this.localizer); const converter = new ReferencesTagsConverter(this.language, this.contents, this.localizer);
this.next({ converter }); this.next({ converter });
} }

7
frontend/src/app/features/dashboard/pages/dashboard-config.component.html

@ -24,8 +24,8 @@
} }
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button class="dropdown-item" (click)="startExpertMode()"> {{ "common.expertMode" | sqxTranslate }} </button> <button class="dropdown-item" (click)="startExpertMode()" type="button">{{ "common.expertMode" | sqxTranslate }}</button>
<button class="dropdown-item" (click)="saveConfig()"> {{ "common.save" | sqxTranslate }} </button> <button class="dropdown-item" (click)="saveConfig()" type="button">{{ "common.save" | sqxTranslate }}</button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button <button
class="dropdown-item dropdown-item-delete" class="dropdown-item dropdown-item-delete"
@ -33,7 +33,8 @@
confirmRememberKey="resetConfig" confirmRememberKey="resetConfig"
confirmText="i18n:dashboard.resetConfigConfirmText" confirmText="i18n:dashboard.resetConfigConfirmText"
confirmTitle="i18n:dashboard.resetConfigConfirmTitle" confirmTitle="i18n:dashboard.resetConfigConfirmTitle"
(sqxConfirmClick)="resetConfig()"> (sqxConfirmClick)="resetConfig()"
type="button">
{{ "common.reset" | sqxTranslate }} {{ "common.reset" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

3
frontend/src/app/features/dashboard/pages/dashboard-page.component.html

@ -24,7 +24,8 @@
<sqx-github-card /> <sqx-github-card />
} }
@case ("api-calls") {animate.enter="fade-in" animate.leave="fade-out" @case ("api-calls") {
animate.enter="fade-in" animate.leave="fade-out"
<sqx-api-calls-card [app]="app" [usage]="callsUsage" /> <sqx-api-calls-card [app]="app" [usage]="callsUsage" />
} }

2
frontend/src/app/features/rules/pages/events/rule-event.component.html

@ -80,7 +80,7 @@
</div> </div>
<div class="event-section"> <div class="event-section">
<sqx-state-details [availableSteps]="availableSteps" [state]="event.flowState" [branchItems]="branchItems" /> <sqx-state-details [availableSteps]="availableSteps" [branchItems]="branchItems" [state]="event.flowState" />
</div> </div>
</td> </td>
</tr> </tr>

14
frontend/src/app/features/rules/pages/rule/branch.component.html

@ -14,7 +14,11 @@
@if (branchItems.length > 0) { @if (branchItems.length > 0) {
@for (item of branchItems; track item.id; let last = $last; let i = $index) { @for (item of branchItems; track item.id; let last = $last; let i = $index) {
<div class="centered"> <div class="centered">
<button class="btn btn-outline-secondary btn-circle-sm" attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}" (click)="add(i - 1)"> <button
class="btn btn-outline-secondary btn-circle-sm"
attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}"
(click)="add(i - 1)"
type="button">
<i class="icon-plus"></i> <i class="icon-plus"></i>
</button> </button>
</div> </div>
@ -60,7 +64,11 @@
@if (last && item.step.step.stepType !== "If") { @if (last && item.step.step.stepType !== "If") {
<div class="centered"> <div class="centered">
<button class="btn btn-outline-secondary btn-circle-sm" attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}" (click)="add(i)"> <button
class="btn btn-outline-secondary btn-circle-sm"
attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}"
(click)="add(i)"
type="button">
<i class="icon-plus"></i> <i class="icon-plus"></i>
</button> </button>
</div> </div>
@ -68,7 +76,7 @@
} }
} @else { } @else {
<div class="centered"> <div class="centered">
<button class="btn btn-outline-secondary btn-circle" attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}" (click)="add()"> <button class="btn btn-outline-secondary btn-circle" attr.aria-label="{{ 'rules.addStep' | sqxTranslate }}" (click)="add()" type="button">
<i class="icon-plus"></i> <i class="icon-plus"></i>
</button> </button>
</div> </div>

8
frontend/src/app/features/rules/pages/rule/rule-page.component.html

@ -2,7 +2,7 @@
<sqx-layout layout="main" overflow="true" padding="true" white="true"> <sqx-layout layout="main" overflow="true" padding="true" white="true">
<ng-container title> <ng-container title>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<button class="btn btn-text-secondary" attr.aria-label="{{ 'common.back' | sqxTranslate }}" (click)="back()"> <button class="btn btn-text-secondary" attr.aria-label="{{ 'common.back' | sqxTranslate }}" (click)="back()" type="button">
<i class="icon-angle-left"></i> <i class="icon-angle-left"></i>
</button> </button>
<h3 class="title">{{ "common.rule" | sqxTranslate }}</h3> <h3 class="title">{{ "common.rule" | sqxTranslate }}</h3>
@ -18,7 +18,8 @@
confirmText="i18n:rules.triggerConfirmText" confirmText="i18n:rules.triggerConfirmText"
confirmTitle="i18n:rules.triggerConfirmTitle" confirmTitle="i18n:rules.triggerConfirmTitle"
[disabled]="!rule?.canTrigger" [disabled]="!rule?.canTrigger"
(sqxConfirmClick)="trigger()"> (sqxConfirmClick)="trigger()"
type="button">
<i class="icon-play-line"></i> <i class="icon-play-line"></i>
</button> </button>
} }
@ -67,7 +68,8 @@
<button <button
class="btn btn-outline-secondary btn-circle" class="btn btn-outline-secondary btn-circle"
attr.aria-label="{{ 'rules.addTrigger' | sqxTranslate }}" attr.aria-label="{{ 'rules.addTrigger' | sqxTranslate }}"
(click)="triggerDialog.show()"> (click)="triggerDialog.show()"
type="button">
<i class="icon-plus"></i> <i class="icon-plus"></i>
</button> </button>
} }

18
frontend/src/app/features/rules/pages/rules/rule.component.html

@ -22,15 +22,15 @@
</button> </button>
<sqx-dropdown-menu position="bottom-end" scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu position="bottom-end" scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true">
@if (rule.canUpdate) { @if (rule.canUpdate) {
<button class="dropdown-item" [routerLink]="rule.id">{{ "common.edit" | sqxTranslate }}</button> <button class="dropdown-item" [routerLink]="rule.id" type="button">{{ "common.edit" | sqxTranslate }}</button>
} }
@if (rule.canEnable) { @if (rule.canEnable) {
<button class="dropdown-item" (click)="enable()">{{ "common.enable" | sqxTranslate }}</button> <button class="dropdown-item" (click)="enable()" type="button">{{ "common.enable" | sqxTranslate }}</button>
} }
@if (rule.canDisable) { @if (rule.canDisable) {
<button class="dropdown-item" (click)="disable()">{{ "common.disable" | sqxTranslate }}</button> <button class="dropdown-item" (click)="disable()" type="button">{{ "common.disable" | sqxTranslate }}</button>
} }
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -42,7 +42,8 @@
confirmRememberKey="runRule" confirmRememberKey="runRule"
confirmText="i18n:rules.runRuleConfirmText" confirmText="i18n:rules.runRuleConfirmText"
confirmTitle="i18n:rules.runRuleConfirmTitle" confirmTitle="i18n:rules.runRuleConfirmTitle"
(sqxConfirmClick)="run()"> (sqxConfirmClick)="run()"
type="button">
{{ "rules.run" | sqxTranslate }} {{ "rules.run" | sqxTranslate }}
</button> </button>
} }
@ -53,7 +54,8 @@
confirmRememberKey="runRuleFromSnapshots" confirmRememberKey="runRuleFromSnapshots"
confirmText="i18n:rules.runRuleConfirmText" confirmText="i18n:rules.runRuleConfirmText"
confirmTitle="i18n:rules.runRuleConfirmTitle" confirmTitle="i18n:rules.runRuleConfirmTitle"
(sqxConfirmClick)="runFromSnapshots()"> (sqxConfirmClick)="runFromSnapshots()"
type="button">
{{ "rules.runFromSnapshots" | sqxTranslate }} {{ "rules.runFromSnapshots" | sqxTranslate }}
</button> </button>
} }
@ -67,7 +69,8 @@
confirmRememberKey="deleteRule" confirmRememberKey="deleteRule"
confirmText="i18n:rules.deleteConfirmText" confirmText="i18n:rules.deleteConfirmText"
confirmTitle="i18n:rules.deleteConfirmTitle" confirmTitle="i18n:rules.deleteConfirmTitle"
(sqxConfirmClick)="delete()"> (sqxConfirmClick)="delete()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
} }
@ -118,7 +121,8 @@
confirmText="i18n:rules.triggerConfirmText" confirmText="i18n:rules.triggerConfirmText"
confirmTitle="i18n:rules.triggerConfirmTitle" confirmTitle="i18n:rules.triggerConfirmTitle"
[disabled]="!rule.canTrigger" [disabled]="!rule.canTrigger"
(sqxConfirmClick)="trigger()"> (sqxConfirmClick)="trigger()"
type="button">
<i class="icon-play-line"></i> <i class="icon-play-line"></i>
</button> </button>
} @else { } @else {

2
frontend/src/app/features/rules/pages/rules/rules-page.component.html

@ -15,7 +15,7 @@
@if (rulesState.runningRule | async; as runningRule) { @if (rulesState.runningRule | async; as runningRule) {
<div class="alert alert-danger d-flex justify-content-between align-items-center p-2 ps-4 mb-4"> <div class="alert alert-danger d-flex justify-content-between align-items-center p-2 ps-4 mb-4">
{{ "rules.runningRule" | sqxTranslate: { name: runningRule.name || "Unnamed Rule" } }} {{ "rules.runningRule" | sqxTranslate: { name: runningRule.name || "Unnamed Rule" } }}
<button class="btn" (click)="cancelRun()">{{ "common.cancel" | sqxTranslate }}</button> <button class="btn" (click)="cancelRun()" type="button">{{ "common.cancel" | sqxTranslate }}</button>
</div> </div>
} }

5
frontend/src/app/features/rules/pages/simulator/rule-transition.component.html

@ -1,6 +1,5 @@
@if (text) { @if (text) {
<sqx-history-step small="true" [isLast]="isLast && filteredErrors.length === 0"> <sqx-history-step [isLast]="isLast && filteredErrors.length === 0" small="true">
<ng-container summary> <ng-container summary>
<div class="col text-sm truncate"> <div class="col text-sm truncate">
{{ text | sqxTranslate }} {{ text | sqxTranslate }}
@ -10,7 +9,7 @@
} }
@for (error of filteredErrors; track error; let last = $last) { @for (error of filteredErrors; track error; let last = $last) {
<sqx-history-step small="true" [isLast]="isLast && last"> <sqx-history-step [isLast]="isLast && last" small="true">
<ng-container summary> <ng-container summary>
<div class="col text-sm text-danger truncate"> <div class="col text-sm text-danger truncate">
{{ error | sqxTranslate }} {{ error | sqxTranslate }}

2
frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html

@ -71,7 +71,7 @@
text="i18n:rules.simulation.eventConditionEvaluated" /> text="i18n:rules.simulation.eventConditionEvaluated" />
@if (event.flowState) { @if (event.flowState) {
<sqx-state-details [availableSteps]="availableSteps" showEvent="false" [state]="event.flowState" [branchItems]="branchItems" /> <sqx-state-details [availableSteps]="availableSteps" [branchItems]="branchItems" showEvent="false" [state]="event.flowState" />
} }
} }
} }

10
frontend/src/app/features/rules/shared/state-attempt.component.html

@ -2,19 +2,19 @@
@if (attempt) { @if (attempt) {
<div class="row g-0"> <div class="row g-0">
<div class="col"> <div class="col">
<label>{{ "common.started" | sqxTranslate }}:&nbsp;</label> <span class="text-sm">{{ attempt.started.toISOString() || '-' }}</span> <label>{{ "common.started" | sqxTranslate }}:&nbsp;</label> <span class="text-sm">{{ attempt.started.toISOString() || "-" }}</span>
</div> </div>
<div class="col"> <div class="col">
<label>{{ "common.completed" | sqxTranslate }}:&nbsp;</label> <span class="text-sm">{{ attempt.completed.toISOString() || '-' }}</span> <label>{{ "common.completed" | sqxTranslate }}:&nbsp;</label> <span class="text-sm">{{ attempt.completed.toISOString() || "-" }}</span>
</div> </div>
</div> </div>
@if (attempt.error) { @if (attempt.error) {
<label class="mt-3">{{ "common.error" | sqxTranslate }}</label> <label class="mt-3">{{ "common.error" | sqxTranslate }}</label>
<sqx-code-editor disabled="true" height="auto" mode="ace/mode/text" [ngModel]="attempt.error" wordWrap="true" /> <sqx-code-editor disabled="true" height="auto" mode="ace/mode/text" [ngModel]="attempt.error" wordWrap="true" />
} }
@if (output) { @if (output) {
<label class="mt-3">{{ "common.output" | sqxTranslate }}</label> <label class="mt-3">{{ "common.output" | sqxTranslate }}</label>
<sqx-code-editor disabled="true" height="auto" mode="ace/mode/text" [ngModel]="output" wordWrap="true" /> <sqx-code-editor disabled="true" height="auto" mode="ace/mode/text" [ngModel]="output" wordWrap="true" />

4
frontend/src/app/features/rules/shared/state-details.component.html

@ -19,6 +19,6 @@
[isLast]="$last" [isLast]="$last"
[isNext]="state.nextStepId === item.id" [isNext]="state.nextStepId === item.id"
[state]="state.steps[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]" />
} }

4
frontend/src/app/features/rules/shared/state-step.component.html

@ -21,7 +21,7 @@
<ul class="nav nav-tabs2 mt-2"> <ul class="nav nav-tabs2 mt-2">
@if (stepInfo.properties.length > 0) { @if (stepInfo.properties.length > 0) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="attemptIndex === -1" (click)="selectAttempt(-1)"> <button class="nav-link" [class.active]="attemptIndex === -1" (click)="selectAttempt(-1)" type="button">
{{ "common.properties" | sqxTranslate }} {{ "common.properties" | sqxTranslate }}
</button> </button>
</li> </li>
@ -29,7 +29,7 @@
@if (state) { @if (state) {
@for (attempt of state.attempts; let i = $index; track i) { @for (attempt of state.attempts; let i = $index; track i) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="attemptIndex === i" (click)="selectAttempt(i)"> <button class="nav-link" [class.active]="attemptIndex === i" (click)="selectAttempt(i)" type="button">
@if ($first) { @if ($first) {
{{ "rules.ruleEvents.attempt" | sqxTranslate }}&nbsp; {{ "rules.ruleEvents.attempt" | sqxTranslate }}&nbsp;
} }

26
frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.html

@ -6,37 +6,43 @@
<sqx-form-row for="name" label="common.name" vertical> <sqx-form-row for="name" label="common.name" vertical>
<input class="form-control" id="name" [ngModel]="schema.name" [ngModelOptions]="{ standalone: true }" readonly /> <input class="form-control" id="name" [ngModel]="schema.name" [ngModelOptions]="{ standalone: true }" readonly />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="label" label="common.label" vertical hint="schemas.schemaLabelHint"> <sqx-form-row for="label" hint="schemas.schemaLabelHint" label="common.label" vertical>
<input class="form-control" id="label" formControlName="label" /> <input class="form-control" id="label" formControlName="label" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="hints" label="common.hints" vertical hint="schemas.schemaHintsHint"> <sqx-form-row for="hints" hint="schemas.schemaHintsHint" label="common.hints" vertical>
<textarea class="form-control" id="hints" formControlName="hints" rows="4"></textarea> <textarea class="form-control" id="hints" formControlName="hints" rows="4"></textarea>
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="contentsSidebarUrl" label="schemas.contentsSidebarUrl" vertical hint="schemas.contentsSidebarUrlHint"> <sqx-form-row for="contentsSidebarUrl" hint="schemas.contentsSidebarUrlHint" label="schemas.contentsSidebarUrl" vertical>
<input class="form-control" id="contentsSidebarUrl" formControlName="contentsSidebarUrl" type="url" /> <input class="form-control" id="contentsSidebarUrl" formControlName="contentsSidebarUrl" type="url" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="contentSidebarUrl" label="schemas.contentSidebarUrl" vertical hint="schemas.contentSidebarUrlHint"> <sqx-form-row for="contentSidebarUrl" hint="schemas.contentSidebarUrlHint" label="schemas.contentSidebarUrl" vertical>
<input class="form-control" id="contentSidebarUrl" formControlName="contentSidebarUrl" type="url" /> <input class="form-control" id="contentSidebarUrl" formControlName="contentSidebarUrl" type="url" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="contentEditorUrl" label="schemas.contentEditorUrl" vertical hint="schemas.contentEditorUrlHint"> <sqx-form-row for="contentEditorUrl" hint="schemas.contentEditorUrlHint" label="schemas.contentEditorUrl" vertical>
<input class="form-control" id="contentEditorUrl" formControlName="contentEditorUrl" type="url" /> <input class="form-control" id="contentEditorUrl" formControlName="contentEditorUrl" type="url" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="contentsListUrl" label="schemas.contentsListUrl" vertical hint="schemas.contentsListUrlHint"> <sqx-form-row for="contentsListUrl" hint="schemas.contentsListUrlHint" label="schemas.contentsListUrl" vertical>
<input class="form-control" id="contentsListUrl" formControlName="contentsListUrl" type="url" /> <input class="form-control" id="contentsListUrl" formControlName="contentsListUrl" type="url" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="tags" label="common.tags" vertical hint="schemas.schemaTagsHint"> <sqx-form-row for="tags" hint="schemas.schemaTagsHint" label="common.tags" vertical>
<sqx-tag-editor id="tags" formControlName="tags" /> <sqx-tag-editor id="tags" formControlName="tags" />
</sqx-form-row> </sqx-form-row>
<sqx-form-row for="validateOnPublish" label="schemas.validateOnPublish" check vertical hint="schemas.validateOnPublishHint" alert="schemas.validateOnPublishHint"> <sqx-form-row
<input class="form-check-input" id="validateOnPublish" formControlName="validateOnPublish" type="checkbox" /> alert="schemas.validateOnPublishHint"
check
for="validateOnPublish"
hint="schemas.validateOnPublishHint"
label="schemas.validateOnPublish"
vertical>
<input class="form-check-input" id="validateOnPublish" formControlName="validateOnPublish" type="checkbox" />
</sqx-form-row> </sqx-form-row>
</div> </div>

10
frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html

@ -74,7 +74,7 @@
@if (!editForm) { @if (!editForm) {
<div> <div>
<div class="btn-group ms-2" attr.aria-label="{{ 'schemas.addField' | sqxTranslate }}" role="group"> <div class="btn-group ms-2" attr.aria-label="{{ 'schemas.addField' | sqxTranslate }}" role="group">
<button class="btn btn-success" (click)="addField('Close')" shortcut="CTRL + SHIFT + S"> <button class="btn btn-success" (click)="addField('Close')" shortcut="CTRL + SHIFT + S" type="button">
{{ "schemas.addFieldAndClose" | sqxTranslate }} {{ "schemas.addFieldAndClose" | sqxTranslate }}
</button> </button>
<button <button
@ -86,8 +86,8 @@
<i class="icon-angle-down"></i> <i class="icon-angle-down"></i>
</button> </button>
<sqx-dropdown-menu position="top-end" scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="addFieldModal; closeAlways: true"> <sqx-dropdown-menu position="top-end" scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="addFieldModal; closeAlways: true">
<button class="dropdown-item" (click)="addField('Add')">{{ "schemas.addFieldAndCreate" | sqxTranslate }}</button> <button class="dropdown-item" (click)="addField('Add')" type="button">{{ "schemas.addFieldAndCreate" | sqxTranslate }}</button>
<button class="dropdown-item" (click)="addField('Edit')">{{ "schemas.addFieldAndEdit" | sqxTranslate }}</button> <button class="dropdown-item" (click)="addField('Edit')" type="button">{{ "schemas.addFieldAndEdit" | sqxTranslate }}</button>
</sqx-dropdown-menu> </sqx-dropdown-menu>
</div> </div>
</div> </div>
@ -96,7 +96,7 @@
@if (editForm) { @if (editForm) {
<div> <div>
<div class="btn-group ms-2" attr.aria-label="{{ 'schemas.saveField' | sqxTranslate }}" role="group"> <div class="btn-group ms-2" attr.aria-label="{{ 'schemas.saveField' | sqxTranslate }}" role="group">
<button class="btn btn-primary" (click)="save('Close')" shortcut="CTRL + SHIFT + S"> <button class="btn btn-primary" (click)="save('Close')" shortcut="CTRL + SHIFT + S" type="button">
{{ "schemas.saveFieldAndClose" | sqxTranslate }} {{ "schemas.saveFieldAndClose" | sqxTranslate }}
</button> </button>
<button <button
@ -108,7 +108,7 @@
<i class="icon-angle-down"></i> <i class="icon-angle-down"></i>
</button> </button>
<sqx-dropdown-menu position="bottom-end" scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="addFieldModal; closeAlways: true"> <sqx-dropdown-menu position="bottom-end" scrollY="true" [sqxAnchoredTo]="buttonSave" *sqxModal="addFieldModal; closeAlways: true">
<button class="dropdown-item" (click)="save('Add')">{{ "schemas.saveFieldAndNew" | sqxTranslate }}</button> <button class="dropdown-item" (click)="save('Add')" type="button">{{ "schemas.saveFieldAndNew" | sqxTranslate }}</button>
</sqx-dropdown-menu> </sqx-dropdown-menu>
</div> </div>
</div> </div>

14
frontend/src/app/features/schemas/pages/schema/fields/field.component.html

@ -56,19 +56,19 @@
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="dropdown; closeAlways: true">
@if (field.properties.isContentField) { @if (field.properties.isContentField) {
@if (field.canEnable) { @if (field.canEnable) {
<button class="dropdown-item" (click)="enableField()">{{ "schemas.field.enable" | sqxTranslate }}</button> <button class="dropdown-item" (click)="enableField()" type="button">{{ "schemas.field.enable" | sqxTranslate }}</button>
} }
@if (field.canDisable) { @if (field.canDisable) {
<button class="dropdown-item" (click)="disableField()">{{ "schemas.field.disable" | sqxTranslate }}</button> <button class="dropdown-item" (click)="disableField()" type="button">{{ "schemas.field.disable" | sqxTranslate }}</button>
} }
@if (field.canHide) { @if (field.canHide) {
<button class="dropdown-item" (click)="hideField()">{{ "schemas.field.hide" | sqxTranslate }}</button> <button class="dropdown-item" (click)="hideField()" type="button">{{ "schemas.field.hide" | sqxTranslate }}</button>
} }
@if (field.canShow) { @if (field.canShow) {
<button class="dropdown-item" (click)="showField()">{{ "schemas.field.show" | sqxTranslate }}</button> <button class="dropdown-item" (click)="showField()" type="button">{{ "schemas.field.show" | sqxTranslate }}</button>
} }
} }
@ -79,7 +79,8 @@
confirmRememberKey="lockField" confirmRememberKey="lockField"
confirmText="i18n:schemas.field.lockConfirmText" confirmText="i18n:schemas.field.lockConfirmText"
confirmTitle="i18n:schemas.field.lockConfirmTitle" confirmTitle="i18n:schemas.field.lockConfirmTitle"
(sqxConfirmClick)="lockField()"> (sqxConfirmClick)="lockField()"
type="button">
{{ "schemas.field.lock" | sqxTranslate }} {{ "schemas.field.lock" | sqxTranslate }}
</button> </button>
} }
@ -91,7 +92,8 @@
confirmRememberKey="deleteField" confirmRememberKey="deleteField"
confirmText="i18n:schemas.field.deleteConfirmText" confirmText="i18n:schemas.field.deleteConfirmText"
confirmTitle="i18n:schemas.field.deleteConfirmTitle" confirmTitle="i18n:schemas.field.deleteConfirmTitle"
(sqxConfirmClick)="deleteField()"> (sqxConfirmClick)="deleteField()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</ng-container> </ng-container>

10
frontend/src/app/features/schemas/pages/schema/fields/forms/field-form.component.html

@ -1,25 +1,27 @@
<div class="table-items-row-details-tabs clearfix"> <div class="table-items-row-details-tabs clearfix">
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)"> <button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)" type="button">
{{ "schemas.field.tabCommon" | sqxTranslate }} {{ "schemas.field.tabCommon" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item" [class.hidden]="!field.properties.isContentField"> <li class="nav-item" [class.hidden]="!field.properties.isContentField">
<button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)"> <button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)" type="button">
{{ "schemas.field.tabValidation" | sqxTranslate }} {{ "schemas.field.tabValidation" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item" [class.hidden]="!field.properties.isContentField"> <li class="nav-item" [class.hidden]="!field.properties.isContentField">
<button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)"> <button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)" type="button">
{{ "schemas.field.tabEditing" | sqxTranslate }} {{ "schemas.field.tabEditing" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item" [class.hidden]="field.properties.fieldType !== 'Json'"> <li class="nav-item" [class.hidden]="field.properties.fieldType !== 'Json'">
<button class="nav-link" [class.active]="selectedTab === 3" (click)="selectTab(3)">{{ "schemas.field.tabMore" | sqxTranslate }}</button> <button class="nav-link" [class.active]="selectedTab === 3" (click)="selectTab(3)" type="button">
{{ "schemas.field.tabMore" | sqxTranslate }}
</button>
</li> </li>
</ul> </ul>

7
frontend/src/app/features/schemas/pages/schema/fields/types/rich-text-ui.component.html

@ -1,5 +1,10 @@
<div [formGroup]="fieldForm"> <div [formGroup]="fieldForm">
<sqx-form-row #classNamesRow for="classNames" hint="schemas.fieldTypes.string.classNamesHint" label="schemas.fieldTypes.string.classNames" [prefix]="field.fieldId"> <sqx-form-row
#classNamesRow
for="classNames"
hint="schemas.fieldTypes.string.classNamesHint"
label="schemas.fieldTypes.string.classNames"
[prefix]="field.fieldId">
<sqx-tag-editor [id]="classNamesRow.fieldName" formControlName="classNames" /> <sqx-tag-editor [id]="classNamesRow.fieldName" formControlName="classNames" />
</sqx-form-row> </sqx-form-row>

5
frontend/src/app/features/schemas/pages/schema/schema-page.component.html

@ -76,7 +76,7 @@
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="editOptionsDropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="editOptionsDropdown; closeAlways: true">
@if (schemasState.canCreate) { @if (schemasState.canCreate) {
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button class="dropdown-item" (click)="cloneSchema()">{{ "common.clone" | sqxTranslate }}</button> <button class="dropdown-item" (click)="cloneSchema()" type="button">{{ "common.clone" | sqxTranslate }}</button>
} }
<ng-container> <ng-container>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@ -86,7 +86,8 @@
confirmRememberKey="deleteSchema" confirmRememberKey="deleteSchema"
confirmText="i18n:schemas.deleteConfirmText" confirmText="i18n:schemas.deleteConfirmText"
confirmTitle="i18n:schemas.deleteConfirmTitle" confirmTitle="i18n:schemas.deleteConfirmTitle"
(sqxConfirmClick)="deleteSchema()"> (sqxConfirmClick)="deleteSchema()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</ng-container> </ng-container>

2
frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.html

@ -3,7 +3,7 @@
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
@for (script of editForm.form.controls | sqxKeys; track script) { @for (script of editForm.form.controls | sqxKeys; track script) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="schemaScript === script" (click)="selectField(script)"> <button class="nav-link" [class.active]="schemaScript === script" (click)="selectField(script)" type="button">
{{ script | sqxScriptName }} {{ script | sqxScriptName }}
</button> </button>
</li> </li>

4
frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.html

@ -2,13 +2,13 @@
<div class="inner-header"> <div class="inner-header">
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)"> <button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)" type="button">
{{ "schemas.listFields" | sqxTranslate }} <small>({{ fieldsInLists.length }})</small> {{ "schemas.listFields" | sqxTranslate }} <small>({{ fieldsInLists.length }})</small>
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)"> <button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)" type="button">
{{ "schemas.referenceFields" | sqxTranslate }} <small>({{ fieldsInReferences.length }})</small> {{ "schemas.referenceFields" | sqxTranslate }} <small>({{ fieldsInReferences.length }})</small>
</button> </button>
</li> </li>

8
frontend/src/app/features/schemas/pages/schemas/schema-form.component.html

@ -12,20 +12,20 @@
<div class="col"> <div class="col">
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)"> <button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)" type="button">
{{ "schemas.createCustom" | sqxTranslate }} {{ "schemas.createCustom" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)"> <button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)" type="button">
{{ "schemas.createFromJson" | sqxTranslate }} {{ "schemas.createFromJson" | sqxTranslate }}
</button> </button>
</li> </li>
@if (hasChatBot) { @if (hasChatBot) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)"> <button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)" type="button">
{{ "schemas.createAI" | sqxTranslate }} {{ "schemas.createAI" | sqxTranslate }}
<span class="badge rounded-pill badge-primary">New</span> <span class="badge rounded-pill badge-primary">New</span>
</button> </button>
@ -143,7 +143,7 @@
</div> </div>
</div> </div>
</sqx-form-row> </sqx-form-row>
<sqx-form-alert> {{ "schemas.promptExample" | sqxTranslate }} </sqx-form-alert> <sqx-form-alert> {{ "schemas.promptExample" | sqxTranslate }} </sqx-form-alert>
<sqx-code-editor disabled="true" [height]="1000" [ngModel]="generateLog" [ngModelOptions]="{ standalone: true }" /> <sqx-code-editor disabled="true" [height]="1000" [ngModel]="generateLog" [ngModelOptions]="{ standalone: true }" />

2
frontend/src/app/features/settings/pages/asset-scripts/asset-scripts-page.component.html

@ -11,7 +11,7 @@
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
@for (script of editForm.form.controls | sqxKeys; track script) { @for (script of editForm.form.controls | sqxKeys; track script) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="assetScript === script" (click)="selectField(script)"> <button class="nav-link" [class.active]="assetScript === script" (click)="selectField(script)" type="button">
{{ script | sqxScriptName }} {{ script | sqxScriptName }}
</button> </button>
</li> </li>

4
frontend/src/app/features/settings/pages/clients/client-connect-form.component.html

@ -11,7 +11,7 @@
</li> </li>
} @else { } @else {
<li class="breadcrumb-item done"> <li class="breadcrumb-item done">
<button class="btn-inline-link" (click)="go('Start')"> <button class="btn-inline-link" (click)="go('Start')" type="button">
<i class="icon-checkmark"></i> {{ "clients.connectWizard.step1Title" | sqxTranslate }} <i class="icon-checkmark"></i> {{ "clients.connectWizard.step1Title" | sqxTranslate }}
</button> </button>
</li> </li>
@ -157,7 +157,7 @@
} }
</ng-container> </ng-container>
<ng-container footer> <ng-container footer>
<button class="btn btn-text-secondary" (click)="go('Start')" [disabled]="step === 'Start'"> <button class="btn btn-text-secondary" (click)="go('Start')" [disabled]="step === 'Start'" type="button">
{{ "common.back" | sqxTranslate }} {{ "common.back" | sqxTranslate }}
</button> </button>
</ng-container> </ng-container>

2
frontend/src/app/features/settings/pages/clients/client.component.html

@ -93,7 +93,7 @@
@if (client.canUpdate) { @if (client.canUpdate) {
<div class="col-auto"> <div class="col-auto">
<button class="btn btn-outline-secondary" (click)="updateApiCallsLimit(apiCallsLimit)"> <button class="btn btn-outline-secondary" (click)="updateApiCallsLimit(apiCallsLimit)" type="button">
{{ "common.save" | sqxTranslate }} {{ "common.save" | sqxTranslate }}
</button> </button>
</div> </div>

6
frontend/src/app/features/settings/pages/more/more-page.component.html

@ -48,7 +48,11 @@
<div> <div>
<sqx-avatar [identifier]="app.name" [image]="app.image" size="150" /> <sqx-avatar [identifier]="app.name" [image]="app.image" size="150" />
@if (isEditableImage && app.image) { @if (isEditableImage && app.image) {
<button class="btn btn-danger btn-sm app-image-remove" (click)="removeImage()" title="i18n:apps.removeImage"> <button
class="btn btn-danger btn-sm app-image-remove"
(click)="removeImage()"
title="i18n:apps.removeImage"
type="button">
<i class="icon-bin2"></i> <i class="icon-bin2"></i>
</button> </button>
} }

10
frontend/src/app/features/settings/pages/plans/plan.component.html

@ -22,7 +22,7 @@
</div> </div>
@if (planInfo.isSelected) { @if (planInfo.isSelected) {
<button class="btn btn-block btn-text-success plan-selected">&#10003; {{ "plans.selected" | sqxTranslate }}</button> <button class="btn btn-block btn-text-success plan-selected" type="button">&#10003; {{ "plans.selected" | sqxTranslate }}</button>
} }
@if (!planInfo.isSelected) { @if (!planInfo.isSelected) {
@ -33,7 +33,8 @@
[confirmText]="planInfo.plan.confirmText" [confirmText]="planInfo.plan.confirmText"
confirmTitle="i18n:plans.changeConfirmTitle" confirmTitle="i18n:plans.changeConfirmTitle"
[disabled]="(plansState.locked | async) !== 'None'" [disabled]="(plansState.locked | async) !== 'None'"
(sqxConfirmClick)="changeMonthly()"> (sqxConfirmClick)="changeMonthly()"
type="button">
{{ "plans.change" | sqxTranslate }} {{ "plans.change" | sqxTranslate }}
</button> </button>
} }
@ -47,7 +48,7 @@
</div> </div>
@if (planInfo.isYearlySelected) { @if (planInfo.isYearlySelected) {
<button class="btn btn-block btn-text-success plan-selected">&#10003; {{ "plans.selected" | sqxTranslate }}</button> <button class="btn btn-block btn-text-success plan-selected" type="button">&#10003; {{ "plans.selected" | sqxTranslate }}</button>
} }
@if (!planInfo.isYearlySelected) { @if (!planInfo.isYearlySelected) {
@ -57,7 +58,8 @@
[confirmText]="planInfo.plan.yearlyConfirmText!" [confirmText]="planInfo.plan.yearlyConfirmText!"
confirmTitle="i18n:plans.changeConfirmTitle" confirmTitle="i18n:plans.changeConfirmTitle"
[disabled]="(plansState.locked | async) !== 'None'" [disabled]="(plansState.locked | async) !== 'None'"
(sqxConfirmClick)="changeYearly()"> (sqxConfirmClick)="changeYearly()"
type="button">
{{ "plans.change" | sqxTranslate }} {{ "plans.change" | sqxTranslate }}
</button> </button>
} }

5
frontend/src/app/features/settings/pages/workflows/workflow-step.component.html

@ -6,7 +6,8 @@
[class.active]="step.name === workflow.dto.initial" [class.active]="step.name === workflow.dto.initial"
[class.enabled]="step.name !== workflow.dto.initial && !step.isLocked" [class.enabled]="step.name !== workflow.dto.initial && !step.isLocked"
(click)="makeInitial()" (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) { @if (!step.isLocked) {
<i class="icon-arrow-right text-decent"></i> <i class="icon-arrow-right text-decent"></i>
} }
@ -69,7 +70,7 @@
</div> </div>
<div class="col ps-2"> <div class="col ps-2">
<button class="btn btn-outline-secondary" (click)="addTransition(openStep.name)"><i class="icon-plus"></i></button> <button class="btn btn-outline-secondary" (click)="addTransition(openStep.name)" type="button"><i class="icon-plus"></i></button>
</div> </div>
</div> </div>
} }

6
frontend/src/app/features/settings/pages/workflows/workflow.component.html

@ -37,13 +37,13 @@
<div class="table-items-row-details-tabs clearfix"> <div class="table-items-row-details-tabs clearfix">
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)"> <button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)" type="button">
{{ "workflows.tabEdit" | sqxTranslate }} {{ "workflows.tabEdit" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)"> <button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)" type="button">
{{ "workflows.tabVisualize" | sqxTranslate }} {{ "workflows.tabVisualize" | sqxTranslate }}
</button> </button>
</li> </li>
@ -94,7 +94,7 @@
} }
@if (workflowView.dto.canUpdate) { @if (workflowView.dto.canUpdate) {
<button class="btn btn-success" (click)="addStep()">{{ "workflows.addStep" | sqxTranslate }}</button> <button class="btn btn-success" (click)="addStep()" type="button">{{ "workflows.addStep" | sqxTranslate }}</button>
} }
} @else { } @else {
<sqx-workflow-diagram [workflow]="workflowView" /> <sqx-workflow-diagram [workflow]="workflowView" />

2
frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html

@ -30,7 +30,7 @@
<div class="import-hint"> <div class="import-hint">
<sqx-form-hint> <sqx-form-hint>
{{ "contributors.importHint" | sqxTranslate }} {{ "contributors.importHint" | sqxTranslate }}
<button class="btn-inline-link" (click)="importDialog.show()">{{ "contributors.importButton" | sqxTranslate }}</button> <button class="btn-inline-link" (click)="importDialog.show()" type="button">{{ "contributors.importButton" | sqxTranslate }}</button>
</sqx-form-hint> </sqx-form-hint>
</div> </div>
</div> </div>

10
frontend/src/app/features/teams/pages/plans/plan.component.html

@ -22,7 +22,7 @@
</div> </div>
@if (planInfo.isSelected) { @if (planInfo.isSelected) {
<button class="btn btn-block btn-text-success plan-selected">&#10003; {{ "plans.selected" | sqxTranslate }}</button> <button class="btn btn-block btn-text-success plan-selected" type="button">&#10003; {{ "plans.selected" | sqxTranslate }}</button>
} }
@if (!planInfo.isSelected) { @if (!planInfo.isSelected) {
@ -33,7 +33,8 @@
[confirmText]="planInfo.plan.confirmText" [confirmText]="planInfo.plan.confirmText"
confirmTitle="i18n:plans.changeConfirmTitle" confirmTitle="i18n:plans.changeConfirmTitle"
[disabled]="(plansState.locked | async) !== 'None'" [disabled]="(plansState.locked | async) !== 'None'"
(sqxConfirmClick)="changeMonthly()"> (sqxConfirmClick)="changeMonthly()"
type="button">
{{ "plans.change" | sqxTranslate }} {{ "plans.change" | sqxTranslate }}
</button> </button>
} }
@ -47,7 +48,7 @@
</div> </div>
@if (planInfo.isYearlySelected) { @if (planInfo.isYearlySelected) {
<button class="btn btn-block btn-text-success plan-selected">&#10003; {{ "plans.selected" | sqxTranslate }}</button> <button class="btn btn-block btn-text-success plan-selected" type="button">&#10003; {{ "plans.selected" | sqxTranslate }}</button>
} }
@if (!planInfo.isYearlySelected) { @if (!planInfo.isYearlySelected) {
@ -57,7 +58,8 @@
[confirmText]="planInfo.plan.yearlyConfirmText!" [confirmText]="planInfo.plan.yearlyConfirmText!"
confirmTitle="i18n:plans.changeConfirmTitle" confirmTitle="i18n:plans.changeConfirmTitle"
[disabled]="(plansState.locked | async) !== 'None'" [disabled]="(plansState.locked | async) !== 'None'"
(sqxConfirmClick)="changeYearly()"> (sqxConfirmClick)="changeYearly()"
type="button">
{{ "plans.change" | sqxTranslate }} {{ "plans.change" | sqxTranslate }}
</button> </button>
} }

9
frontend/src/app/framework/angular/layout.component.html

@ -25,7 +25,7 @@
<i class="icon-close"></i> <i class="icon-close"></i>
</a> </a>
} @else { } @else {
<button class="btn panel2-collapse"><i class="icon-close"></i></button> <button class="btn panel2-collapse" type="button"><i class="icon-close"></i></button>
} }
</div> </div>
} }
@ -63,7 +63,12 @@
<ng-container *ngTemplateOutlet="menuTemplate"></ng-container> <ng-container *ngTemplateOutlet="menuTemplate"></ng-container>
</div> </div>
</div> </div>
<button class="btn panel2-collapse" attr.aria-label="{{ 'common.togglePanel' | sqxTranslate }}" (click)="toggle()" sqxStopClick> <button
class="btn panel2-collapse"
attr.aria-label="{{ 'common.togglePanel' | sqxTranslate }}"
(click)="toggle()"
sqxStopClick
type="button">
@if (displayMode === "Collapsed" && expandedWidth > 0) { @if (displayMode === "Collapsed" && expandedWidth > 0) {
<i class="icon-angle-right"></i> <i class="icon-angle-right"></i>
} @else if (displayMode === "Collapsed") { } @else if (displayMode === "Collapsed") {

6
frontend/src/app/framework/angular/menu-item.component.html

@ -7,7 +7,8 @@
[confirmText]="confirmText | sqxTranslate" [confirmText]="confirmText | sqxTranslate"
[confirmTitle]="confirmTitle | sqxTranslate" [confirmTitle]="confirmTitle | sqxTranslate"
(sqxConfirmClick)="action.emit()" (sqxConfirmClick)="action.emit()"
[title]="tooltip | sqxTranslate"> [title]="tooltip | sqxTranslate"
type="button">
{{ menuLabel || label | sqxTranslate }} {{ menuLabel || label | sqxTranslate }}
</button> </button>
</ng-template> </ng-template>
@ -21,7 +22,8 @@
[disabled]="disabled" [disabled]="disabled"
(sqxConfirmClick)="action.emit()" (sqxConfirmClick)="action.emit()"
[tabIndex]="tabIndex" [tabIndex]="tabIndex"
[title]="tooltip | sqxTranslate"> [title]="tooltip | sqxTranslate"
type="button">
@if (icon) { @if (icon) {
<i class="icon-{{ icon }}"></i> <i class="icon-{{ icon }}"></i>
} }

8
frontend/src/app/framework/angular/menu.component.html

@ -23,7 +23,13 @@
</sqx-dropdown-menu> </sqx-dropdown-menu>
} }
<div class="d-flex flex-nowrap" #menu [class.gap-1]="small" [class.gap-2]="!small" [class.overflown-right]="!!overflowMenuItems && isRightAligned" [class.overflown-left]="!!overflowMenuItems && !isRightAligned"> <div
class="d-flex flex-nowrap"
#menu
[class.gap-1]="small"
[class.gap-2]="!small"
[class.overflown-left]="!!overflowMenuItems && !isRightAligned"
[class.overflown-right]="!!overflowMenuItems && isRightAligned">
<ng-content /> <ng-content />
</div> </div>
</div> </div>

7
frontend/src/app/framework/angular/modals/dialog-renderer.component.html

@ -22,7 +22,12 @@
<div class="notification-container notification-container-bottom-right"> <div class="notification-container notification-container-bottom-right">
@for (notification of snapshot.notifications; track notification) { @for (notification of snapshot.notifications; track notification) {
<div class="alert alert-light alert-dismissible border shadow-sm timed" animate.enter="fade-in" animate.leave="fade-out" (click)="close(notification)" role="alert"> <div
class="alert alert-light alert-dismissible border shadow-sm timed"
animate.enter="fade-in"
animate.leave="fade-out"
(click)="close(notification)"
role="alert">
<button class="btn-sm btn-close" data-dismiss="alert" (dialogClose)="close(notification)" type="button"></button> <button class="btn-sm btn-close" data-dismiss="alert" (dialogClose)="close(notification)" type="button"></button>
<div class="timer timer-{{ notification.messageType }}"></div> <div class="timer timer-{{ notification.messageType }}"></div>
<div [sqxMarkdown]="notification.message | sqxTranslate"></div> <div [sqxMarkdown]="notification.message | sqxTranslate"></div>

2
frontend/src/app/framework/angular/modals/modal-dialog.component.html

@ -1,4 +1,4 @@
<div class="modal" animate.enter="fade-in" animate.leave="fade-out" data-testid="dialog" cdkTrapFocus> <div class="modal" animate.enter="fade-in" animate.leave="fade-out" cdkTrapFocus data-testid="dialog">
<div class="modal-dialog modal-{{ size }}" [class.modal-fh]="fullHeight"> <div class="modal-dialog modal-{{ size }}" [class.modal-fh]="fullHeight">
<div class="modal-content" [sqxTourStep]="tourId"> <div class="modal-content" [sqxTourStep]="tourId">
@if (showHeader) { @if (showHeader) {

13
frontend/src/app/framework/angular/modals/tour-template.component.html

@ -1,6 +1,7 @@
<div <div
class="tour-step" class="tour-step"
animate.enter="fade-in" animate.leave="fade-out" animate.enter="fade-in"
animate.leave="fade-out"
[class.d-none]="!isVisible" [class.d-none]="!isVisible"
offset="4" offset="4"
[position]="currentStep.position || 'bottom-start'" [position]="currentStep.position || 'bottom-start'"
@ -31,13 +32,13 @@
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex pe-4"> <div class="d-flex pe-4">
@if (hasPrev) { @if (hasPrev) {
<button class="btn btn-outline-secondary btn-sm me-2" (click)="tourService.prev()"> <button class="btn btn-outline-secondary btn-sm me-2" (click)="tourService.prev()" type="button">
{{ "common.prev" | sqxTranslate }} {{ "common.prev" | sqxTranslate }}
</button> </button>
} }
@if (currentStep.hideAll) { @if (currentStep.hideAll) {
<button class="btn btn-outline-danger btn-sm" (click)="currentStep.hideAll()"> <button class="btn btn-outline-danger btn-sm" (click)="currentStep.hideAll()" type="button">
{{ "tour.tooltipStop" | sqxTranslate }} {{ "tour.tooltipStop" | sqxTranslate }}
</button> </button>
} }
@ -45,19 +46,19 @@
<div class="d-flex ps-4"> <div class="d-flex ps-4">
@if (hasNext) { @if (hasNext) {
<button class="btn btn-outline-secondary btn-sm" (click)="tourService.next()"> <button class="btn btn-outline-secondary btn-sm" (click)="tourService.next()" type="button">
{{ "common.next" | sqxTranslate }} {{ "common.next" | sqxTranslate }}
</button> </button>
} }
@if (hasFinish) { @if (hasFinish) {
<button class="btn btn-outline-success btn-sm" (click)="tourService.end()"> <button class="btn btn-outline-success btn-sm" (click)="tourService.end()" type="button">
{{ "common.finish" | sqxTranslate }} {{ "common.finish" | sqxTranslate }}
</button> </button>
} }
@if (currentStep.hideThis) { @if (currentStep.hideThis) {
<button class="btn btn-outline-success btn-sm" (click)="currentStep.hideThis()"> <button class="btn btn-outline-success btn-sm" (click)="currentStep.hideThis()" type="button">
{{ "tour.tooltipConfirm" | sqxTranslate }} {{ "tour.tooltipConfirm" | sqxTranslate }}
</button> </button>
} }

4
frontend/src/app/framework/angular/pager.component.html

@ -14,11 +14,11 @@
<span class="page-info d-flex align-items-center justify-content-end"> <span class="page-info d-flex align-items-center justify-content-end">
<span class="btn deactivated">&nbsp;</span> &nbsp; <span class="btn deactivated">&nbsp;</span> &nbsp;
@if (paging.count > 0 && paging.total > 0) { @if (paging.count > 0 && paging.total > 0) {
<button class="btn deactivated">{{ "common.pagerInfo" | sqxTranslate: translationInfo }}</button> <button class="btn deactivated" type="button">{{ "common.pagerInfo" | sqxTranslate: translationInfo }}</button>
} }
@if (paging.count > 0 && paging.total <= 0) { @if (paging.count > 0 && paging.total <= 0) {
<button class="btn" (click)="loadTotal.emit()" title="{{ 'common.pagerReload' | sqxTranslate }}"> <button class="btn" (click)="loadTotal.emit()" title="{{ 'common.pagerReload' | sqxTranslate }}" type="button">
{{ "common.pagerInfoNoTotal" | sqxTranslate: translationInfo }} {{ "common.pagerInfoNoTotal" | sqxTranslate: translationInfo }}
</button> </button>
} }

20
frontend/src/app/shared/components/assets/asset-dialog.component.html

@ -6,14 +6,14 @@
<div class="col"> <div class="col">
<ul class="nav nav-tabs2"> <ul class="nav nav-tabs2">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)"> <button class="nav-link" [class.active]="selectedTab === 0" (click)="selectTab(0)" type="button">
{{ "assets.tabMetadata" | sqxTranslate }} {{ "assets.tabMetadata" | sqxTranslate }}
</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
@if (isImage) { @if (isImage) {
<button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)"> <button class="nav-link" [class.active]="selectedTab === 1" (click)="selectTab(1)" type="button">
{{ "assets.tabImage" | sqxTranslate }} {{ "assets.tabImage" | sqxTranslate }}
</button> </button>
} }
@ -21,7 +21,7 @@
<li class="nav-item"> <li class="nav-item">
@if (isImage) { @if (isImage) {
<button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)"> <button class="nav-link" [class.active]="selectedTab === 2" (click)="selectTab(2)" type="button">
{{ "assets.tabFocusPoint" | sqxTranslate }} {{ "assets.tabFocusPoint" | sqxTranslate }}
</button> </button>
} }
@ -29,7 +29,7 @@
<li class="nav-item"> <li class="nav-item">
@if (isDocumentLikely) { @if (isDocumentLikely) {
<button class="nav-link" [class.active]="selectedTab === 3" (click)="selectTab(3)"> <button class="nav-link" [class.active]="selectedTab === 3" (click)="selectTab(3)" type="button">
{{ "assets.tabTextEditor" | sqxTranslate }} {{ "assets.tabTextEditor" | sqxTranslate }}
</button> </button>
} }
@ -37,14 +37,14 @@
<li class="nav-item"> <li class="nav-item">
@if (isVideo || (asset | sqxPreviewable)) { @if (isVideo || (asset | sqxPreviewable)) {
<button class="nav-link" [class.active]="selectedTab === 4" (click)="selectTab(4)"> <button class="nav-link" [class.active]="selectedTab === 4" (click)="selectTab(4)" type="button">
{{ "assets.tabPreview" | sqxTranslate }} {{ "assets.tabPreview" | sqxTranslate }}
</button> </button>
} }
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="selectedTab === 5" (click)="selectTab(5)"> <button class="nav-link" [class.active]="selectedTab === 5" (click)="selectTab(5)" type="button">
{{ "assets.tabHistory" | sqxTranslate }} {{ "assets.tabHistory" | sqxTranslate }}
</button> </button>
</li> </li>
@ -60,7 +60,13 @@
} }
@case (1) { @case (1) {
<button class="btn btn-primary" [class.invisible]="!isUploadable" (click)="cropImage()" [disabled]="progress > 0" type="button"> <button
class="btn btn-primary"
[class.invisible]="!isUploadable"
(click)="cropImage()"
[disabled]="progress > 0"
type="button"
type="button">
{{ "common.upload" | sqxTranslate }} {{ "common.upload" | sqxTranslate }}
</button> </button>
} }

5
frontend/src/app/shared/components/assets/asset-folder.component.html

@ -17,7 +17,7 @@
</button> </button>
<sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="editDropdown; closeAlways: true"> <sqx-dropdown-menu scrollY="true" [sqxAnchoredTo]="buttonOptions" *sqxModal="editDropdown; closeAlways: true">
@if (canUpdate) { @if (canUpdate) {
<button class="dropdown-item" (click)="editDialog.show()">{{ "common.rename" | sqxTranslate }}</button> <button class="dropdown-item" (click)="editDialog.show()" type="button">{{ "common.rename" | sqxTranslate }}</button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
} }
<button <button
@ -26,7 +26,8 @@
confirmRememberKey="deleteAssetFolder" confirmRememberKey="deleteAssetFolder"
confirmText="i18n:assets.deleteFolderConfirmText" confirmText="i18n:assets.deleteFolderConfirmText"
confirmTitle="i18n:assets.deleteFolderConfirmTitle" confirmTitle="i18n:assets.deleteFolderConfirmTitle"
(sqxConfirmClick)="emitDelete()"> (sqxConfirmClick)="emitDelete()"
type="button">
{{ "common.delete" | sqxTranslate }} {{ "common.delete" | sqxTranslate }}
</button> </button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

2
frontend/src/app/shared/components/assets/asset-path.component.html

@ -3,7 +3,7 @@
@if (i > 0) { @if (i > 0) {
<i class="icon-angle-right"></i> <i class="icon-angle-right"></i>
} }
<button class="btn" [class.first]="i === 0" [class.force]="i < path.length - 1 || all" (click)="navigate.emit(item)"> <button class="btn" [class.first]="i === 0" [class.force]="i < path.length - 1 || all" (click)="navigate.emit(item)" type="button">
{{ item.folderName | sqxTranslate }} {{ item.folderName | sqxTranslate }}
</button> </button>
} @empty { } @empty {

10
frontend/src/app/shared/components/assets/asset.component.html

@ -25,7 +25,7 @@
<div class="overlay-menu"> <div class="overlay-menu">
@if (!isDisabled) { @if (!isDisabled) {
<button class="file-edit ms-2" attr.aria-label="{{ 'common.edit' | sqxTranslate }}" (click)="emitEdit()"> <button class="file-edit ms-2" attr.aria-label="{{ 'common.edit' | sqxTranslate }}" (click)="emitEdit()" type="button">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</button> </button>
} }
@ -38,7 +38,7 @@
<i class="icon-download"></i> <i class="icon-download"></i>
</a> </a>
@if (folderIcon) { @if (folderIcon) {
<button class="file-folder ms-2" (click)="selectFolder.emit(asset.parentId)"><i class="icon-folder"></i></button> <button class="file-folder ms-2" (click)="selectFolder.emit(asset.parentId)" type="button"><i class="icon-folder"></i></button>
} }
@if (!isDisabled && !removeMode && asset.canDelete) { @if (!isDisabled && !removeMode && asset.canDelete) {
@ -48,7 +48,8 @@
confirmRememberKey="deleteAsset" confirmRememberKey="deleteAsset"
confirmText="i18n:assets.deleteConfirmText" confirmText="i18n:assets.deleteConfirmText"
confirmTitle="i18n:assets.deleteConfirmTitle" confirmTitle="i18n:assets.deleteConfirmTitle"
(sqxConfirmClick)="delete.emit()"> (sqxConfirmClick)="delete.emit()"
type="button">
<i class="icon-delete"></i> <i class="icon-delete"></i>
</button> </button>
} }
@ -60,7 +61,8 @@
confirmRememberKey="removeAsset" confirmRememberKey="removeAsset"
confirmText="i18n:assets.removeConfirmText" confirmText="i18n:assets.removeConfirmText"
confirmTitle="i18n:assets.removeConfirmTitle" confirmTitle="i18n:assets.removeConfirmTitle"
(sqxConfirmClick)="remove.emit()"> (sqxConfirmClick)="remove.emit()"
type="button">
<i class="icon-close"></i> <i class="icon-close"></i>
</button> </button>
} }

2
frontend/src/app/shared/components/cards/api-calls-card.component.html

@ -3,7 +3,7 @@
{{ "dashboard.apiCallsCard" | sqxTranslate }} {{ "dashboard.apiCallsCard" | sqxTranslate }}
@if (app) { @if (app) {
<div class="float-end"> <div class="float-end">
<button class="btn-inline-link" (click)="downloadLog(app)"> <button class="btn-inline-link" (click)="downloadLog(app)" type="button">
<small>{{ "dashboard.downloadLog" | sqxTranslate }}</small> <small>{{ "dashboard.downloadLog" | sqxTranslate }}</small>
</button> </button>
</div> </div>

2
frontend/src/app/shared/components/contents/content-value.component.html

@ -4,7 +4,7 @@
</div> </div>
@if (isString && fields) { @if (isString && fields) {
<button class="btn btn-icon" (click)="toggle()" sqxStopClick><i class="icon-wrap_text"></i></button> <button class="btn btn-icon" (click)="toggle()" sqxStopClick type="button"><i class="icon-wrap_text"></i></button>
} }
} @else { } @else {
<div <div

2
frontend/src/app/shared/components/references/content-selector.component.html

@ -114,7 +114,7 @@
</button> </button>
} }
<button class="btn btn-success" (click)="emitSelect()" [disabled]="selectionCount === 0 || selectionCount > maxItems" type="submit"> <button class="btn btn-success" (click)="emitSelect()" [disabled]="selectionCount === 0 || selectionCount > maxItems" type="button" type="submit">
{{ "contents.referencesLink" | sqxTranslate: { count: selectionCount } }} {{ "contents.referencesLink" | sqxTranslate: { count: selectionCount } }}
</button> </button>
</div> </div>

4
frontend/src/app/shared/components/search/query-list.component.html

@ -2,7 +2,7 @@
<ul class="nav nav-light flex-column"> <ul class="nav nav-light flex-column">
@for (saved of queries; track saved.name) { @for (saved of queries; track saved.name) {
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" [class.active]="isSelectedQuery(saved)" (click)="search.emit(saved.query)"> <button class="nav-link" [class.active]="isSelectedQuery(saved)" (click)="search.emit(saved.query)" type="button">
<div class="row g-0"> <div class="row g-0">
<div class="col truncate"> <div class="col truncate">
<i class="icon-circle" [class.hidden]="!saved.color" [style.color]="saved.color"></i> <i class="icon-circle" [class.hidden]="!saved.color" [style.color]="saved.color"></i>
@ -11,7 +11,7 @@
<div class="col-auto"> <div class="col-auto">
@if (canRemove) { @if (canRemove) {
<button class="btn-sm btn-text-danger btn-remove" (click)="remove.emit(saved)" sqxStopClick> <button class="btn-sm btn-text-danger btn-remove" (click)="remove.emit(saved)" sqxStopClick type="button">
<i class="icon-close"></i> <i class="icon-close"></i>
</button> </button>
} }

2
frontend/src/app/shared/components/table-header.component.html

@ -1,5 +1,5 @@
@if (sortable) { @if (sortable) {
<button class="btn-inline-link truncate" (click)="sort()"> <button class="btn-inline-link truncate" (click)="sort()" type="button">
<span class="truncate"> <span class="truncate">
@if (order === "ascending") { @if (order === "ascending") {
<i class="icon-caret-up"></i> <i class="icon-caret-up"></i>

5
frontend/src/app/shared/components/tour-guide.component.html

@ -7,7 +7,7 @@
<div class="col">{{ "tour.guide-title" | sqxTranslate }}</div> <div class="col">{{ "tour.guide-title" | sqxTranslate }}</div>
<div class="col-auto"> <div class="col-auto">
<button class="btn btn-simple btn-sm" (click)="toggle()"><i class="icon-angle-down"></i></button> <button class="btn btn-simple btn-sm" (click)="toggle()" type="button"><i class="icon-angle-down"></i></button>
</div> </div>
</div> </div>
</div> </div>
@ -36,7 +36,8 @@
class="btn btn-outline-secondary btn-block mt-2" class="btn btn-outline-secondary btn-block mt-2"
(click)="complete()" (click)="complete()"
[sqxScrollActive]="(tourState.pendingTasks | async) === 0" [sqxScrollActive]="(tourState.pendingTasks | async) === 0"
[sqxScrollContainer]="body"> [sqxScrollContainer]="body"
type="button">
{{ "tour.complete" | sqxTranslate }} {{ "tour.complete" | sqxTranslate }}
</button> </button>
</div> </div>

1
frontend/src/app/shared/model/custom.ts

@ -586,6 +586,7 @@ export class SchemaDto extends generated.SchemaDto {
public export(): any { public export(): any {
const fieldKeys = [ const fieldKeys = [
'fieldId', 'fieldId',
'cachedValues',
'parentId', 'parentId',
'parentFieldId', 'parentFieldId',
'_links', '_links',

1
frontend/src/app/shared/model/generated.ts

@ -2383,6 +2383,7 @@ export class SchemaDto extends ResourceDto implements ISchemaDto {
public export(): any { public export(): any {
const fieldKeys = [ const fieldKeys = [
'fieldId', 'fieldId',
'cachedValues',
'parentId', 'parentId',
'parentFieldId', 'parentFieldId',
'_links', '_links',

12
frontend/src/app/shell/pages/internal/profile-menu.component.html

@ -13,14 +13,18 @@
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@if (uiState.canUseAdminResource | async) { @if (uiState.canUseAdminResource | async) {
<button class="dropdown-item" routerLink="/app/administration">{{ "common.administration" | sqxTranslate }}</button> <button class="dropdown-item" routerLink="/app/administration" type="button">{{ "common.administration" | sqxTranslate }}</button>
} }
<a class="dropdown-item" href="{{ snapshot.profileUrl }}" sqxExternalLink="noicon"> {{ "profile.title" | sqxTranslate }} </a> <a class="dropdown-item" href="{{ snapshot.profileUrl }}" sqxExternalLink="noicon"> {{ "profile.title" | sqxTranslate }} </a>
<div class="dropdown-submenu"> <div class="dropdown-submenu">
<button class="dropdown-item dropdown-toggle" (click)="toggleSubmenu()" sqxStopClick>{{ "common.language" | sqxTranslate }}</button> <button class="dropdown-item dropdown-toggle" (click)="toggleSubmenu()" sqxStopClick type="button">{{ "common.language" | sqxTranslate }}</button>
<div class="dropdown-menu" [class.open]="snapshot.showSubmenu"> <div class="dropdown-menu" [class.open]="snapshot.showSubmenu">
@for (availableLanguage of languages; track availableLanguage) { @for (availableLanguage of languages; track availableLanguage) {
<button class="dropdown-item" [class.active]="availableLanguage.iso2Code === language" (click)="changeLanguage(availableLanguage.iso2Code)"> <button
class="dropdown-item"
[class.active]="availableLanguage.iso2Code === language"
(click)="changeLanguage(availableLanguage.iso2Code)"
type="button">
{{ availableLanguage.localName }} {{ availableLanguage.localName }}
</button> </button>
} }
@ -28,5 +32,5 @@
</div> </div>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button class="dropdown-item" (click)="logout()" sqxExternalLink>{{ "common.logout" | sqxTranslate }}</button> <button class="dropdown-item" (click)="logout()" sqxExternalLink type="button">{{ "common.logout" | sqxTranslate }}</button>
</sqx-dropdown-menu> </sqx-dropdown-menu>

Loading…
Cancel
Save