Browse Source

Resolve comments (#1297)

* Refactorings

* T

* Move to labels

* Comments.

* Fix invalidation.

* Test

* Tests

* Fix e2e
pull/1299/head
Sebastian Stehle 2 months ago
committed by GitHub
parent
commit
5bac79f731
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      backend/i18n/frontend_de.json
  2. 21
      backend/i18n/frontend_en.json
  3. 21
      backend/i18n/frontend_fr.json
  4. 21
      backend/i18n/frontend_it.json
  5. 21
      backend/i18n/frontend_nl.json
  6. 21
      backend/i18n/frontend_pt.json
  7. 21
      backend/i18n/frontend_zh.json
  8. 21
      backend/i18n/source/frontend_en.json
  9. 4
      backend/src/Squidex/wwwroot/editor/squidex-editor.js
  10. 48
      frontend/.storybook/main.js
  11. 94
      frontend/.storybook/main.ts
  12. 21
      frontend/.storybook/preview.js
  13. 28
      frontend/.storybook/preview.ts
  14. 1
      frontend/.storybook/tsconfig.json
  15. 10
      frontend/angular.json
  16. 3714
      frontend/package-lock.json
  17. 8
      frontend/package.json
  18. 163
      frontend/refactor.js
  19. 8
      frontend/src/app/_theme.html
  20. 21
      frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.html
  21. 3
      frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts
  22. 2
      frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html
  23. 19
      frontend/src/app/features/administration/pages/users/user.component.html
  24. 3
      frontend/src/app/features/administration/pages/users/user.component.ts
  25. 4
      frontend/src/app/features/api/api-area.component.html
  26. 8
      frontend/src/app/features/api/pages/graphql/graphql-page.component.html
  27. 2
      frontend/src/app/features/apps/pages/apps-page.component.html
  28. 2
      frontend/src/app/features/apps/pages/news-dialog.component.html
  29. 2
      frontend/src/app/features/apps/pages/onboarding-dialog.component.html
  30. 2
      frontend/src/app/features/assets/pages/asset-tag-dialog.component.html
  31. 7
      frontend/src/app/features/assets/pages/asset-tags.component.html
  32. 9
      frontend/src/app/features/assets/pages/assets-page.component.html
  33. 18
      frontend/src/app/features/content/pages/calendar/calendar-page.component.html
  34. 28
      frontend/src/app/features/content/pages/comments/comments-page.component.html
  35. 9
      frontend/src/app/features/content/pages/comments/comments-page.component.scss
  36. 12
      frontend/src/app/features/content/pages/comments/comments-page.component.ts
  37. 2
      frontend/src/app/features/content/pages/content/content-history-page.component.html
  38. 35
      frontend/src/app/features/content/pages/content/content-page.component.html
  39. 12
      frontend/src/app/features/content/pages/content/content-page.component.ts
  40. 8
      frontend/src/app/features/content/pages/contents/contents-page.component.html
  41. 2
      frontend/src/app/features/content/pages/schemas/schemas-page.component.html
  42. 18
      frontend/src/app/features/content/shared/forms/array-editor.component.html
  43. 46
      frontend/src/app/features/content/shared/forms/array-item.component.html
  44. 2
      frontend/src/app/features/content/shared/forms/assets-editor.component.html
  45. 2
      frontend/src/app/features/content/shared/forms/component.component.html
  46. 4
      frontend/src/app/features/content/shared/forms/content-field.component.html
  47. 20
      frontend/src/app/features/content/shared/forms/content-field.component.ts
  48. 1
      frontend/src/app/features/content/shared/forms/field-copy-button.component.html
  49. 17
      frontend/src/app/features/content/shared/forms/field-editor.component.html
  50. 40
      frontend/src/app/features/content/shared/forms/field-editor.component.ts
  51. 12
      frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html
  52. 13
      frontend/src/app/features/content/shared/list/content.component.html
  53. 14
      frontend/src/app/features/content/shared/references/reference-item.component.html
  54. 2
      frontend/src/app/features/dashboard/pages/dashboard-config.component.html
  55. 5
      frontend/src/app/features/rules/pages/rule/rule-page.component.html
  56. 3
      frontend/src/app/features/rules/pages/rules/rule.component.html
  57. 2
      frontend/src/app/features/rules/shared/actions/branches-input.component.html
  58. 3
      frontend/src/app/features/rules/shared/rule-element.component.html
  59. 2
      frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.html
  60. 2
      frontend/src/app/features/rules/shared/triggers/comment-trigger.component.html
  61. 3
      frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.html
  62. 20
      frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.html
  63. 2
      frontend/src/app/features/rules/shared/triggers/cron-job-trigger.component.html
  64. 2
      frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.html
  65. 2
      frontend/src/app/features/schemas/pages/schema/common/schema-edit-form.component.html
  66. 4
      frontend/src/app/features/schemas/pages/schema/export/schema-export-form.component.html
  67. 2
      frontend/src/app/features/schemas/pages/schema/fields/field-wizard.component.html
  68. 6
      frontend/src/app/features/schemas/pages/schema/fields/field.component.html
  69. 4
      frontend/src/app/features/schemas/pages/schema/indexes/index-form.component.html
  70. 2
      frontend/src/app/features/schemas/pages/schema/indexes/index.component.html
  71. 13
      frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.html
  72. 7
      frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.html
  73. 12
      frontend/src/app/features/schemas/pages/schema/schema-page.component.html
  74. 4
      frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.html
  75. 6
      frontend/src/app/features/schemas/pages/schemas/schema-form.component.html
  76. 2
      frontend/src/app/features/settings/pages/clients/client-add-form.component.html
  77. 16
      frontend/src/app/features/settings/pages/clients/client-connect-form.component.html
  78. 3
      frontend/src/app/features/settings/pages/clients/client.component.html
  79. 3
      frontend/src/app/features/settings/pages/contributors/contributor.component.html
  80. 3
      frontend/src/app/features/settings/pages/contributors/contributor.component.ts
  81. 2
      frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.html
  82. 4
      frontend/src/app/features/settings/pages/jobs/job.component.html
  83. 2
      frontend/src/app/features/settings/pages/languages/language-add-form.component.html
  84. 8
      frontend/src/app/features/settings/pages/languages/language.component.html
  85. 9
      frontend/src/app/features/settings/pages/more/more-page.component.html
  86. 4
      frontend/src/app/features/settings/pages/plans/plan.component.html
  87. 2
      frontend/src/app/features/settings/pages/plans/plans-page.component.html
  88. 2
      frontend/src/app/features/settings/pages/roles/role-add-form.component.html
  89. 23
      frontend/src/app/features/settings/pages/roles/role.component.html
  90. 2
      frontend/src/app/features/settings/pages/roles/roles-page.component.html
  91. 10
      frontend/src/app/features/settings/pages/settings/settings-page.component.html
  92. 2
      frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.html
  93. 19
      frontend/src/app/features/settings/pages/workflows/workflow-step.component.html
  94. 4
      frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html
  95. 3
      frontend/src/app/features/settings/pages/workflows/workflow.component.html
  96. 14
      frontend/src/app/features/settings/settings-menu.component.html
  97. 10
      frontend/src/app/features/teams/pages/auth/auth-page.component.html
  98. 3
      frontend/src/app/features/teams/pages/contributors/contributor.component.html
  99. 3
      frontend/src/app/features/teams/pages/contributors/contributor.component.ts
  100. 2
      frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.html

21
backend/i18n/frontend_de.json

@ -3,6 +3,7 @@
"api.generalApi": "Allgemeine API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token des aktuellen Benutzers",
"api.pageTitle": "API",
"api.selectClient": "Client auswählen",
@ -97,8 +98,10 @@
"assets.fileTooBig": "Asset ist zu groß.",
"assets.folderName": "Ordnername",
"assets.folderNameHint": "Der Ordnername wird als Anzeigename verwendet und ist möglicherweise nicht eindeutig.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Ausgewählte Assets verknüpfen ({count})",
"assets.listPageTitle": "Assets",
"assets.listView": "List View",
"assets.loadFailed": "Fehler beim Laden der Assets. Bitte neu laden.",
"assets.loadFoldersFailed": "Fehler beim Laden der Asset-Ordner. Bitte neu laden.",
"assets.loadTagsFailed": "Fehler beim Laden der Tags. Bitte neu laden.",
@ -107,6 +110,7 @@
"assets.move": "Verschieben",
"assets.moved": "Asset wurde verschoben.",
"assets.moveFailed": "Fehler beim Verschieben des Assets. Bitte neu laden.",
"assets.openFolder": "Open Folder",
"assets.protected": "Geschützt",
"assets.protectedHint": "Assets sind standardmäßig öffentlich. Jeder mit dem Link kann die Datei herunterladen. Wenn Sie ein Asset schützen, können nur authentifizierte Benutzer (normalerweise ein Client) das Asset herunterladen.",
"assets.refreshTooltip": "Assets aktualisieren",
@ -188,11 +192,17 @@
"clients.reloaded": "Clients neu geladen.",
"clients.revokeFailed": "Fehler beim Widerrufen des Clients. Bitte neu laden.",
"clients.tokenFailed": "Fehler beim Erstellen des Tokens. Bitte versuchen Sie es erneut.",
"comments.all": "Resolved",
"comments.create": "Einen Kommentar erstellen",
"comments.deleteConfirmText": "Möchten Sie den Kommentar wirklich löschen?",
"comments.deleteConfirmTitle": "Kommentar löschen",
"comments.follow": "Folgen",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Kommentare",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Aktionen",
"common.add": "Hinzufügen",
"common.administration": "Verwaltung",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Dieses Element nach ganz oben verschieben",
"contents.arrayMoveUp": "Dieses Element nach oben verschieben",
"contents.arrayNoFields": "Fügen Sie zuerst ein verschachteltes Feld hinzu, um Elemente hinzuzufügen.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Vorhandenes Asset auswählen",
"contents.assetsUpload": "Neues Asset hochladen",
"contents.autotranslate": "Automatisch aus der Hauptsprache übersetzen",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Fehler beim Löschen oder Aktualisieren des Inhalts. Bitte neu laden.",
"contents.calendar": "Geplante Inhalte",
"contents.cancelStatus": "Geplanten Status abbrechen",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Sie betrachten die gelöschte Version **{version}**.",
"contents.viewLatest": "Neueste anzeigen",
"contents.viewReset": "Standardansicht zurücksetzen",
"contents.wrapText": "Wrap Text",
"contributors.add": "Mitwirkenden hinzufügen",
"contributors.add.title": "Mitwirkenden hinzufügen oder einladen",
"contributors.addFailed": "Fehler beim Hinzufügen von Mitwirkenden. Bitte neu laden.",
@ -690,6 +704,7 @@
"roles.add.description": "Fügen Sie eine neue Rolle hinzu, wenn Sie benutzerdefinierte Berechtigungen und Richtlinien benötigen.",
"roles.add.title": "Eine benutzerdefinierte Rolle hinzufügen",
"roles.addFailed": "Fehler beim Hinzufügen der Rolle. Bitte neu laden.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Benutzerdefinierte Rollen",
"roles.default.owner": "Kann alles tun, einschließlich des Löschens der App.",
"roles.default.reader": "Kann nur Assets und Inhalte lesen.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Fehler beim Widerrufen der Rolle. Bitte neu laden.",
"roles.roleNamePlaceholder": "Rollennamen eingeben",
"roles.updateFailed": "Fehler beim Aktualisieren der Rolle. Bitte neu laden.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Schritt hinzufügen",
"rules.addTrigger": "Auslöser hinzufügen",
"rules.advancedFormattingHint": "Sie können erweiterte Formatierungen verwenden",
@ -791,6 +807,7 @@
"rules.runningRule": "Regel '{name}' wird gerade ausgeführt.",
"rules.runRuleConfirmText": "Möchten Sie die Regel wirklich für alle Ereignisse ausführen?",
"rules.runRuleConfirmTitle": "Regel ausführen",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Definieren Sie, für welche Schemas und Änderungen Sie den Inhaltsauslöser ausführen möchten.",
"rules.simulate": "Simulieren",
"rules.simulateTooltip": "Simulieren Sie diese Regeln mit den letzten 100 Ereignissen.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignorieren, wenn dieser Schritt fehlschlägt, und mit dem nächsten Schritt fortfahren (nach allen Wiederholungen), andernfalls wird der Flow abgeschlossen.",
"rules.stepNameHint": "Der Name der Regel. Wird in Protokollen und Debug-Ansichten angezeigt.",
"rules.stop": "Die Regel wird bald gestoppt.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Auslöser hinzufügen",
"rules.trigger.edit": "Auslöser bearbeiten",
"rules.triggerAll": "Bei allen Inhaltsereignissen auslösen",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Einzelner Inhalt",
"schemas.modeSingleDescription": "Am besten für einzelne Instanzen wie die Startseite, Datenschutzrichtlinien, Einstellungen...",
"schemas.nameWarning": "Der Name wird verwendet, um Ihr Schema in jeder HTTP-Anfrage zu identifizieren. Er muss innerhalb einer App eindeutig sein und kann nach dem Festlegen nicht mehr geändert werden.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "Keine Vorschau-URLs konfiguriert.",
"schemas.previewUrls.help": "Sehen Sie sich die integrierte Hilfeseite an, um mehr über Vorschau-URLs zu erfahren.",
"schemas.previewUrls.namePlaceholder": "Web oder Mobil",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schemas neu geladen.",
"schemas.reorderFieldsFailed": "Fehler beim Neuordnen der Felder. Bitte neu laden.",
"schemas.rules.action": "Aktion",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Bedingung in Javascript",
"schemas.rules.empty": "Keine Feldregeln konfiguriert.",
"schemas.rules.title": "Feldregeln",
@ -1289,6 +1309,7 @@
"workflows.add.description": "Der Workflow-Name wird nur in der Benutzeroberfläche verwendet und nicht den Redakteuren angezeigt.",
"workflows.add.title": "Einen neuen Workflow hinzufügen",
"workflows.addStep": "Schritt hinzufügen",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Fehler beim Erstellen des Workflows. Bitte neu laden.",
"workflows.deleteConfirmText": "Möchten Sie den Workflow wirklich entfernen?",
"workflows.deleteConfirmTitle": "Workflow löschen",

21
backend/i18n/frontend_en.json

@ -3,6 +3,7 @@
"api.generalApi": "General API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "Asset is too big.",
"assets.folderName": "Folder Name",
"assets.folderNameHint": "The folder name is used as a display name and might not be unique.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Link selected assets ({count})",
"assets.listPageTitle": "Assets",
"assets.listView": "List View",
"assets.loadFailed": "Failed to load assets. Please reload.",
"assets.loadFoldersFailed": "Failed to load asset folders. Please reload.",
"assets.loadTagsFailed": "Failed to load tags. Please reload.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "Failed to move asset. Please reload.",
"assets.openFolder": "Open Folder",
"assets.protected": "Protected",
"assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.",
"assets.refreshTooltip": "Refresh Assets",
@ -188,11 +192,17 @@
"clients.reloaded": "Clients reloaded.",
"clients.revokeFailed": "Failed to revoke client. Please reload.",
"clients.tokenFailed": "Failed to create token. Please retry.",
"comments.all": "Resolved",
"comments.create": "Create a comment",
"comments.deleteConfirmText": "Do you really want to delete the comment?",
"comments.deleteConfirmTitle": "Delete comment",
"comments.follow": "Follow",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Comments",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Move this item to top",
"contents.arrayMoveUp": "Move this item up",
"contents.arrayNoFields": "Add a nested field first to add items.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Upload new Asset",
"contents.autotranslate": "Autotranslate from master language",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Failed to delete or update content. Please reload.",
"contents.calendar": "Scheduled Contents",
"contents.cancelStatus": "Cancel scheduled status",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "View latest",
"contents.viewReset": "Reset Default View",
"contents.wrapText": "Wrap Text",
"contributors.add": "Add Contributor",
"contributors.add.title": "Add or invite contributor",
"contributors.addFailed": "Failed to add contributors. Please reload.",
@ -690,6 +704,7 @@
"roles.add.description": "Add a new role if you need custom permission and policies.",
"roles.add.title": "Add a custom Role",
"roles.addFailed": "Failed to add role. Please reload.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Custom roles",
"roles.default.owner": "Can do everything, including deleting the app.",
"roles.default.reader": "Can only read assets and contents.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Failed to revoke role. Please reload.",
"roles.roleNamePlaceholder": "Enter role name",
"roles.updateFailed": "Failed to update role. Please reload.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "You can use advanced formatting",
@ -791,6 +807,7 @@
"rules.runningRule": "Rule '{name}' is currently running.",
"rules.runRuleConfirmText": "Do you really want to run the rule for all events?",
"rules.runRuleConfirmTitle": "Run rule",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "Simulate",
"rules.simulateTooltip": "Simulate this rules using the last 100 events.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "Rule will stop soon.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Trigger on all content events",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Single content",
"schemas.modeSingleDescription": "Best for single instances like the home page, privacy policies, settings...",
"schemas.nameWarning": "The name is used to identify your schema in every HTTP request. It must be unique within an app and cannot be changed once set.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "No preview urls configured.",
"schemas.previewUrls.help": "Checkout the integrated help page to learn more about preview URL's.",
"schemas.previewUrls.namePlaceholder": "Web or Mobile",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schemas reloaded.",
"schemas.reorderFieldsFailed": "Failed to reorder fields. Please reload.",
"schemas.rules.action": "Action",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Condition in Javascript",
"schemas.rules.empty": "No field rules configured.",
"schemas.rules.title": "Field Rules",
@ -1289,6 +1309,7 @@
"workflows.add.description": "The workflow name is only used in the UI and not shown to editors.",
"workflows.add.title": "Add a new workflow",
"workflows.addStep": "Add Step",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Failed to create workflow. Please reload.",
"workflows.deleteConfirmText": "Do you really want to remove the workflow?",
"workflows.deleteConfirmTitle": "Delete workflow",

21
backend/i18n/frontend_fr.json

@ -3,6 +3,7 @@
"api.generalApi": "API générale",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "L'actif est trop grand.",
"assets.folderName": "Nom de dossier",
"assets.folderNameHint": "Le nom du dossier est utilisé comme nom d'affichage et peut ne pas être unique.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Lier les éléments sélectionnés ({count})",
"assets.listPageTitle": "Actifs",
"assets.listView": "List View",
"assets.loadFailed": "Échec du chargement des éléments. Veuillez recharger.",
"assets.loadFoldersFailed": "Échec du chargement des dossiers d'actifs. Veuillez recharger.",
"assets.loadTagsFailed": "Échec du chargement des balises. Veuillez recharger.",
@ -107,6 +110,7 @@
"assets.move": "Déplacer",
"assets.moved": "L'actif a été déplacé.",
"assets.moveFailed": "Échec du déplacement de l'élément. Veuillez recharger.",
"assets.openFolder": "Open Folder",
"assets.protected": "Protégé",
"assets.protectedHint": "Les actifs sont publics par défaut. Toute personne disposant du lien peut télécharger le fichier. Si vous protégez une ressource, seuls les utilisateurs authentifiés (généralement un client) peuvent télécharger la ressource.",
"assets.refreshTooltip": "Actualiser les ressources",
@ -188,11 +192,17 @@
"clients.reloaded": "Clients rechargés.",
"clients.revokeFailed": "Échec de la révocation du client. Veuillez recharger.",
"clients.tokenFailed": "Échec de la création du jeton. Veuillez réessayer.",
"comments.all": "Resolved",
"comments.create": "Créer un commentaire",
"comments.deleteConfirmText": "Voulez-vous vraiment supprimer le commentaire\u00A0?",
"comments.deleteConfirmTitle": "Supprimer le commentaire",
"comments.follow": "Suivre",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "commentaires",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Déplacer cet élément vers le haut",
"contents.arrayMoveUp": "Déplacez cet élément vers le haut",
"contents.arrayNoFields": "Ajoutez d'abord un champ imbriqué pour ajouter des éléments.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Déposez des fichiers ou cliquez sur",
"contents.autotranslate": "Traduire automatiquement à partir de la langue principale",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Échec de la suppression ou de la mise à jour du contenu. Veuillez recharger.",
"contents.calendar": "Contenu programmé",
"contents.cancelStatus": "Annuler le statut planifié",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Affichage de la version supprimée **{version}**.",
"contents.viewLatest": "Afficher les dernières",
"contents.viewReset": "Réinitialiser la vue par défaut",
"contents.wrapText": "Wrap Text",
"contributors.add": "Ajouter un contributeur",
"contributors.add.title": "Ajouter ou inviter un contributeur",
"contributors.addFailed": "Impossible d'ajouter des contributeurs. Veuillez recharger.",
@ -690,6 +704,7 @@
"roles.add.description": "Ajoutez un nouveau rôle si vous avez besoin d'autorisations et de politiques personnalisées.",
"roles.add.title": "Ajouter un rôle personnalisé",
"roles.addFailed": "Échec de l'ajout du rôle. Veuillez recharger.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Rôles personnalisés",
"roles.default.owner": "Peut tout faire, y compris supprimer l'application.",
"roles.default.reader": "Peut uniquement lire les actifs et le contenu.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Échec de la révocation du rôle. Veuillez recharger.",
"roles.roleNamePlaceholder": "Entrez le nom du rôle",
"roles.updateFailed": "Échec de la mise à jour du rôle. Veuillez recharger.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "Vous pouvez utiliser le formatage avancé",
@ -791,6 +807,7 @@
"rules.runningRule": "La règle '{name}' est en cours d'exécution.",
"rules.runRuleConfirmText": "Voulez-vous vraiment exécuter la règle pour tous les événements\u00A0?",
"rules.runRuleConfirmTitle": "Exécuter la règle",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Définissez sur quels schémas et modifications vous souhaitez exécuter le déclencheur de contenu.",
"rules.simulate": "Simuler",
"rules.simulateTooltip": "Simulez ces règles en utilisant les 100 derniers événements.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "La règle s'arrêtera bientôt.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Déclencher sur tous les événements de contenu",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Contenu unique",
"schemas.modeSingleDescription": "Idéal pour les instances uniques comme la page d'accueil, les politiques de confidentialité, les paramètres...",
"schemas.nameWarning": "Ces valeurs ne peuvent pas être modifiées ultérieurement.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "Aucune URL d'aperçu configurée.",
"schemas.previewUrls.help": "Consultez la page d'aide intégrée pour en savoir plus sur les URL d'aperçu.",
"schemas.previewUrls.namePlaceholder": "Web ou mobile",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schémas rechargés.",
"schemas.reorderFieldsFailed": "Impossible de réorganiser les champs. Veuillez recharger.",
"schemas.rules.action": "Action",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "État en Javascript",
"schemas.rules.empty": "Aucune règle de champ configurée.",
"schemas.rules.title": "Règles de champ",
@ -1289,6 +1309,7 @@
"workflows.add.description": "Le nom du flux de travail n'est utilisé que dans l'interface utilisateur et n'est pas affiché pour les éditeurs.",
"workflows.add.title": "Ajouter un nouveau flux de travail",
"workflows.addStep": "Ajouter une étape",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Échec de la création du flux de travail. Veuillez recharger.",
"workflows.deleteConfirmText": "Voulez-vous vraiment supprimer le flux de travail\u00A0?",
"workflows.deleteConfirmTitle": "Supprimer le flux de travail",

21
backend/i18n/frontend_it.json

@ -3,6 +3,7 @@
"api.generalApi": "API generali",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "La risorsa è troppo grande.",
"assets.folderName": "Nome della cartella",
"assets.folderNameHint": "Il nome della cartella viene usato solo per la visualizzazione e può non essere univoco.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Collega le risorse selezionate ({count})",
"assets.listPageTitle": "Risorse",
"assets.listView": "List View",
"assets.loadFailed": "Non è stato possibile caricare le risorse. Per favore ricarica.",
"assets.loadFoldersFailed": "Non è stato possibile caricare le cartelle delle risorse. Per favore ricarica.",
"assets.loadTagsFailed": "Failed to load tags. Please reload.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.",
"assets.openFolder": "Open Folder",
"assets.protected": "Protetto",
"assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.",
"assets.refreshTooltip": "Aggiorna le risorse",
@ -188,11 +192,17 @@
"clients.reloaded": "Client ricaricati.",
"clients.revokeFailed": "Non è stato possibile rimuovere il client. Per favore ricarica.",
"clients.tokenFailed": "Non è stato possibile creare il token. Per favore riprova.",
"comments.all": "Resolved",
"comments.create": "Creare un commento",
"comments.deleteConfirmText": "Sei sicuro di voler cancellare il commento?",
"comments.deleteConfirmTitle": "Cancella il comment",
"comments.follow": "Segui",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Commenti",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Azioni",
"common.add": "Add",
"common.administration": "Amministrazione",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Sposta in cima questo elemento",
"contents.arrayMoveUp": "Sposta su questo elemento",
"contents.arrayNoFields": "Aggiungi un primo campo annidato agli elementi.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Trascina i file o clicca",
"contents.autotranslate": "Traduci in automatico dalla lingua principale",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Non è stato possibile eliminare il contenuto. Per favore ricarica.",
"contents.calendar": "Scheduled Contents",
"contents.cancelStatus": "Cancel scheduled status",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "Visualizza l'ultima",
"contents.viewReset": "Imposta la visualizzazione predefinita",
"contents.wrapText": "Wrap Text",
"contributors.add": "Aggiungi un collaboratore",
"contributors.add.title": "Add or invite contributor",
"contributors.addFailed": "Non è stato possibile aggiungere collaboratori. Per favore ricarica.",
@ -690,6 +704,7 @@
"roles.add.description": "Add a new role if you need custom permission and policies.",
"roles.add.title": "Add a custom Role",
"roles.addFailed": "Non è stato possibile aggiungere il ruolo. Per favore ricarica.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Custom roles",
"roles.default.owner": "Hai come amministratore tutte le funzionalità, compreso cancellare le app.",
"roles.default.reader": "Hai un'utenza in sola lettura sia per i contenuti che per le risorse.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Non è stato possibile rimuovere il ruolo. Per favore ricarica.",
"roles.roleNamePlaceholder": "Inserisci il nome del ruolo",
"roles.updateFailed": "Non è stato possibile aggiornare il ruolo. Per favore ricarica.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "You can use advanced formatting",
@ -791,6 +807,7 @@
"rules.runningRule": "La regola '{name}' è attualmente in esecuzione.",
"rules.runRuleConfirmText": "Sei sicuro di voler eseguire la regola per tutti gli eventi?",
"rules.runRuleConfirmTitle": "Esegui la regola",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "Simulate",
"rules.simulateTooltip": "Simulate this rules using the last 100 events.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "La regola si fermerà al più presto.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Trigger on all content events",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Singolo contenuto",
"schemas.modeSingleDescription": "Ideale per contenuti singoli come la pagina principale, privacy, impostazioni...",
"schemas.nameWarning": "Questi valori non possono essere cambiati in un secondo momento.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "Nessuna url per l'anteprima è stato configurato.",
"schemas.previewUrls.help": "Riguardo all'URL dell'anteprima controlla la pagina della guida integrata per saperne di più.",
"schemas.previewUrls.namePlaceholder": "Web o Mobile",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schemai ricaricati.",
"schemas.reorderFieldsFailed": "Non è stato possibile riordinare i campi. Per favore ricarica.",
"schemas.rules.action": "Azione",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Condizioni dentro il Javascript",
"schemas.rules.empty": "Non è stata configurata nessuna regola per il campo.",
"schemas.rules.title": "Regole per il campo",
@ -1289,6 +1309,7 @@
"workflows.add.description": "The workflow name is only used in the UI and not shown to editors.",
"workflows.add.title": "Add a new workflow",
"workflows.addStep": "Aggiungi uno Step",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Non è stato possibile creare un workflow. Per favore ricarica.",
"workflows.deleteConfirmText": "Sei sicuro di voler eliminare il workflow?",
"workflows.deleteConfirmTitle": "Cancella il workflow",

21
backend/i18n/frontend_nl.json

@ -3,6 +3,7 @@
"api.generalApi": "Algemene API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "Asset is te groot.",
"assets.folderName": "Mapnaam",
"assets.folderNameHint": "De mapnaam wordt gebruikt als weergavenaam en mag niet uniek zijn.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Link geselecteerde items ({count})",
"assets.listPageTitle": "Bestanden",
"assets.listView": "List View",
"assets.loadFailed": "Laden van bestanden is mislukt. Laad opnieuw.",
"assets.loadFoldersFailed": "Laden van mappen is mislukt. Laad opnieuw.",
"assets.loadTagsFailed": "Laden van tags is mislukt. Laad opnieuw.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "Verplaatsen van item is mislukt. Laad opnieuw.",
"assets.openFolder": "Open Folder",
"assets.protected": "Beschermd",
"assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.",
"assets.refreshTooltip": "Bestanden vernieuwen",
@ -188,11 +192,17 @@
"clients.reloaded": "Clients herladen.",
"clients.revokeFailed": "Kan client niet intrekken. Laad opnieuw.",
"clients.tokenFailed": "Maken van token is mislukt. Probeer het opnieuw.",
"comments.all": "Resolved",
"comments.create": "Maak een opmerking",
"comments.deleteConfirmText": "Wil je de opmerking echt verwijderen?",
"comments.deleteConfirmTitle": "Verwijder opmerking",
"comments.follow": "Volgen",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Reacties",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Acties",
"common.add": "Add",
"common.administration": "Administratie",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Verplaats dit item naar boven",
"contents.arrayMoveUp": "Verplaats dit item omhoog",
"contents.arrayNoFields": "Voeg eerst een genest veld toe om items toe te voegen.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Zet bestanden neer of klik",
"contents.autotranslate": "Automatisch vertalen vanuit de hoofdtaal",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.",
"contents.calendar": "Ingeplande inhoud",
"contents.cancelStatus": "Annuleer ingeplande status",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "Bekijk laatste",
"contents.viewReset": "Standaardweergave herstellen",
"contents.wrapText": "Wrap Text",
"contributors.add": "Bijdrager toevoegen",
"contributors.add.title": "Nodig bijdrager uit of voeg toe",
"contributors.addFailed": "Toevoegen van bijdragers is mislukt. Laad opnieuw.",
@ -690,6 +704,7 @@
"roles.add.description": "Voeg een nieuwe rol toe als je aangepaste rechten en beleid nodig hebt.",
"roles.add.title": "Voeg een aangepaste rol toe",
"roles.addFailed": "Toevoegen van rol is mislukt. Laad opnieuw.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Aangepaste rollen",
"roles.default.owner": "Kan alles doen, inclusief het verwijderen van de app.",
"roles.default.reader": "Kan alleen items en inhoud lezen.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.",
"roles.roleNamePlaceholder": "Voer de rolnaam in",
"roles.updateFailed": "Update rol mislukt. Laad opnieuw.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "You can use advanced formatting",
@ -791,6 +807,7 @@
"rules.runningRule": "Regel '{name}' is momenteel actief.",
"rules.runRuleConfirmText": "Wil je de regel echt voor alle evenementen uitvoeren?",
"rules.runRuleConfirmTitle": "Regel uitvoeren",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "Simuleren",
"rules.simulateTooltip": "Simuleer deze regels met behulp van de laatste 100 gebeurtenissen.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "Regel stopt binnenkort.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Trigger on all content events",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Enkele inhoud",
"schemas.modeSingleDescription": "Beste voor afzonderlijke instanties zoals de startpagina, privacybeleid, instellingen ...",
"schemas.nameWarning": "Deze waarden kunnen later niet worden gewijzigd.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "Geen voorbeeld-URL's geconfigureerd.",
"schemas.previewUrls.help": "Bekijk de geïntegreerde helppagina voor meer informatie over voorbeeld-URL's.",
"schemas.previewUrls.namePlaceholder": "Web of mobiel",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schema's herladen.",
"schemas.reorderFieldsFailed": "De volgorde van velden is mislukt. Laad opnieuw.",
"schemas.rules.action": "Actie",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Conditie in Javascript",
"schemas.rules.empty": "Geen veldregels geconfigureerd.",
"schemas.rules.title": "Veldregels",
@ -1289,6 +1309,7 @@
"workflows.add.description": "De naam van de workflow wordt alleen gebruikt in de gebruikersinterface en niet getoond aan editors.",
"workflows.add.title": "Een nieuwe workflow toevoegen",
"workflows.addStep": "Stap toevoegen",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Maken van workflow mislukt. Laad opnieuw.",
"workflows.deleteConfirmText": "Wil je de workflow echt verwijderen?",
"workflows.deleteConfirmTitle": "Workflow verwijderen",

21
backend/i18n/frontend_pt.json

@ -3,6 +3,7 @@
"api.generalApi": "API Geral",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "O ficheiro é muito grande.",
"assets.folderName": "Nome da pasta",
"assets.folderNameHint": "O nome da pasta é usado como nome de visor e pode não ser único.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Link ficheiros selecionados ({count})",
"assets.listPageTitle": "Ficheiros",
"assets.listView": "List View",
"assets.loadFailed": "Falhou em carregar os items. Por favor, recarregue.",
"assets.loadFoldersFailed": "Falhou em carregar pastas de ficheiros. Por favor, recarregue.",
"assets.loadTagsFailed": "Falhou em carregar etiquetas. Por favor, recarregue.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "Falha ao mover ficheiro. Por favor, recarregue.",
"assets.openFolder": "Open Folder",
"assets.protected": "Protegido",
"assets.protectedHint": "Os ativos são públicos por defeito. Todos com o link podem descarregar o ficheiro. Se fizer um ficheiro protegido, apenas utilizadores autenticados (normalmente um cliente) podem descarregar o ficheiro.",
"assets.refreshTooltip": "Atualizar ficheiros",
@ -188,11 +192,17 @@
"clients.reloaded": "Clientes recarregados.",
"clients.revokeFailed": "Falhou em revogar o cliente. Por favor, recarregue.",
"clients.tokenFailed": "Falhou em criar um símbolo. Por favor, reda o redando.",
"comments.all": "Resolved",
"comments.create": "Criar um comentário",
"comments.deleteConfirmText": "Quer mesmo apagar o comentário?",
"comments.deleteConfirmTitle": "Apagar comentário",
"comments.follow": "Seguir",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Comentários",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Ações",
"common.add": "Add",
"common.administration": "Administração",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Mova este item para cima",
"contents.arrayMoveUp": "Mova este item para cima",
"contents.arrayNoFields": "Adicione primeiro um campo aninhado para adicionar itens.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Deixe cair ficheiros ou clique",
"contents.autotranslate": "Transtração automática da linguagem mestra",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Falhou em eliminar ou atualizar o conteúdo. Por favor, recarregue.",
"contents.calendar": "Conteúdo Agendado",
"contents.cancelStatus": "Cancelar estado agendado",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "Ver as últimas",
"contents.viewReset": "Ver por defeito de reposição",
"contents.wrapText": "Wrap Text",
"contributors.add": "Adicionar Colaborador",
"contributors.add.title": "Adicionar ou convidar colaborador",
"contributors.addFailed": "Não conseguiu adicionar contribuintes. Por favor, recarregue.",
@ -690,6 +704,7 @@
"roles.add.description": "Adicione um novo grupo se precisar de permissão e políticas personalizadas.",
"roles.add.title": "Adicione um grupo personalizado",
"roles.addFailed": "Falha ao adicionar grupo. Por favor recarregue.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Grupos personalizados",
"roles.default.owner": "Pode fazer tudo, incluindo apagar a app.",
"roles.default.reader": "Só pode ler bens e conteúdos.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Falhou em revogar o grupo. Por favor, recarregue.",
"roles.roleNamePlaceholder": "Insira o nome da grupo",
"roles.updateFailed": "Falhou na atualização do grupo. Por favor, recarregue.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "Você pode usar formatação avançada",
@ -791,6 +807,7 @@
"rules.runningRule": "A regra '{name}' está atualmente em execução.",
"rules.runRuleConfirmText": "Quer mesmo gerir a regra para todos os eventos?",
"rules.runRuleConfirmTitle": "Regra de execução",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "Simular",
"rules.simulateTooltip": "Simular estas regras usando os últimos 100 eventos.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "A regra vai parar em breve.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Gatilho em todos os eventos de conteúdo",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Conteúdo único",
"schemas.modeSingleDescription": "O melhor para casos individuais como a página inicial, políticas de privacidade, configurações...",
"schemas.nameWarning": "Estes valores não podem ser alterados mais tarde.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "Sem urls de pré-visualização configurados.",
"schemas.previewUrls.help": "Check-out a página de ajuda integrada para saber mais sobre URL's de pré-visualização.",
"schemas.previewUrls.namePlaceholder": "Web ou Mobile",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Os esquemas recarregaram.",
"schemas.reorderFieldsFailed": "Falhou em reordenar campos. Por favor, recarregue.",
"schemas.rules.action": "Ação",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Condição em Javascript",
"schemas.rules.empty": "Nenhuma regra de campo configurada.",
"schemas.rules.title": "Regras de Campo",
@ -1289,6 +1309,7 @@
"workflows.add.description": "O nome do fluxo de trabalho é usado apenas na UI e não é mostrado aos editores.",
"workflows.add.title": "Adicione um novo fluxo de trabalho",
"workflows.addStep": "Adicionar Passo",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Falhou em criar fluxo de trabalho. Por favor, recarregue.",
"workflows.deleteConfirmText": "Quer mesmo remover o fluxo de trabalho?",
"workflows.deleteConfirmTitle": "Eliminar fluxo de trabalho",

21
backend/i18n/frontend_zh.json

@ -3,6 +3,7 @@
"api.generalApi": "通用 API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "资源太大。",
"assets.folderName": "文件夹名称",
"assets.folderNameHint": "文件夹名称用作显示名称,不能唯一。",
"assets.gridView": "Grid View",
"assets.linkSelected": "链接选定的资源 ({count})",
"assets.listPageTitle": "资源",
"assets.listView": "List View",
"assets.loadFailed": "资源加载失败,请重新加载。",
"assets.loadFoldersFailed": "加载资源文件夹失败。请重新加载。",
"assets.loadTagsFailed": "Failed to load tags. Please reload.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "资源移动失败。请重新加载。",
"assets.openFolder": "Open Folder",
"assets.protected": "受保护",
"assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.",
"assets.refreshTooltip": "刷新资源",
@ -188,11 +192,17 @@
"clients.reloaded": "客户端重新加载。",
"clients.revokeFailed": "撤销客户端失败。请重新加载。",
"clients.tokenFailed": "创建令牌失败。请重试。",
"comments.all": "Resolved",
"comments.create": "创建评论",
"comments.deleteConfirmText": "你真的要删除评论吗?",
"comments.deleteConfirmTitle": "删除评论",
"comments.follow": "关注",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "评论",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "动作",
"common.add": "Add",
"common.administration": "管理",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "将此项移至顶部",
"contents.arrayMoveUp": "将此项向上移动",
"contents.arrayNoFields": "先添加一个嵌套字段来添加项目。",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "删除文件或点击",
"contents.autotranslate": "从母语自动翻译",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "删除或更新内容失败。请重新加载。",
"contents.calendar": "Scheduled Contents",
"contents.cancelStatus": "Cancel scheduled status",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "查看最新",
"contents.viewReset": "重置默认视图",
"contents.wrapText": "Wrap Text",
"contributors.add": "添加贡献者",
"contributors.add.title": "Add or invite contributor",
"contributors.addFailed": "添加贡献者失败。请重新加载。",
@ -690,6 +704,7 @@
"roles.add.description": "Add a new role if you need custom permission and policies.",
"roles.add.title": "Add a custom Role",
"roles.addFailed": "添加角色失败,请重新加载。",
"roles.addPermission": "Add permission",
"roles.customRoles": "Custom roles",
"roles.default.owner": "可以做任何事情,包括删除应用程序。",
"roles.default.reader": "只能读取资源和内容。",
@ -715,6 +730,7 @@
"roles.revokeFailed": "撤销角色失败,请重新加载。",
"roles.roleNamePlaceholder": "输入角色名称",
"roles.updateFailed": "更新角色失败。请重新加载。",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "You can use advanced formatting",
@ -791,6 +807,7 @@
"rules.runningRule": "规则 '{name}' 当前正在运行。",
"rules.runRuleConfirmText": "你真的想为所有事件运行规则吗?",
"rules.runRuleConfirmTitle": "运行规则",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "模拟",
"rules.simulateTooltip": "使用最近 100 个事件模拟此规则。",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "规则很快就会停止。",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Trigger on all content events",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "单一内容",
"schemas.modeSingleDescription": "最适合单个实例,如主页、隐私政策、设置...",
"schemas.nameWarning": "这些值以后不能更改。",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "未配置预览网址。",
"schemas.previewUrls.help": "查看集成帮助页面以了解有关预览 URL 的更多信息。",
"schemas.previewUrls.namePlaceholder": "网络或移动",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schemas reloaded.",
"schemas.reorderFieldsFailed": "重新排序字段失败。请重新加载。",
"schemas.rules.action": "动作",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Javascript 中的条件",
"schemas.rules.empty": "没有配置字段规则。",
"schemas.rules.title": "字段规则",
@ -1289,6 +1309,7 @@
"workflows.add.description": "The workflow name is only used in the UI and not shown to editors.",
"workflows.add.title": "Add a new workflow",
"workflows.addStep": "添加步骤",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "创建工作流失败。请重新加载。",
"workflows.deleteConfirmText": "你真的要删除工作流吗?",
"workflows.deleteConfirmTitle": "删除工作流",

21
backend/i18n/source/frontend_en.json

@ -3,6 +3,7 @@
"api.generalApi": "General API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.graphqlShowClients": "Show Clients",
"api.noClient": "Token of current user",
"api.pageTitle": "API",
"api.selectClient": "Select Client",
@ -97,8 +98,10 @@
"assets.fileTooBig": "Asset is too big.",
"assets.folderName": "Folder Name",
"assets.folderNameHint": "The folder name is used as a display name and might not be unique.",
"assets.gridView": "Grid View",
"assets.linkSelected": "Link selected assets ({count})",
"assets.listPageTitle": "Assets",
"assets.listView": "List View",
"assets.loadFailed": "Failed to load assets. Please reload.",
"assets.loadFoldersFailed": "Failed to load asset folders. Please reload.",
"assets.loadTagsFailed": "Failed to load tags. Please reload.",
@ -107,6 +110,7 @@
"assets.move": "Move",
"assets.moved": "Asset has been moved.",
"assets.moveFailed": "Failed to move asset. Please reload.",
"assets.openFolder": "Open Folder",
"assets.protected": "Protected",
"assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.",
"assets.refreshTooltip": "Refresh Assets",
@ -188,11 +192,17 @@
"clients.reloaded": "Clients reloaded.",
"clients.revokeFailed": "Failed to revoke client. Please reload.",
"clients.tokenFailed": "Failed to create token. Please retry.",
"comments.all": "Resolved",
"comments.create": "Create a comment",
"comments.deleteConfirmText": "Do you really want to delete the comment?",
"comments.deleteConfirmTitle": "Delete comment",
"comments.follow": "Follow",
"comments.reference": "Reference",
"comments.reply": "Reply to comment",
"comments.resolve": "Resolve comment",
"comments.title": "Comments",
"comments.unresolve": "Unresolve comment",
"comments.unresolved": "Open Comments",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
@ -431,9 +441,12 @@
"contents.arrayMoveTop": "Move this item to top",
"contents.arrayMoveUp": "Move this item up",
"contents.arrayNoFields": "Add a nested field first to add items.",
"contents.assetsGridView": "Grid View",
"contents.assetsListView": "List View",
"contents.assetsSelect": "Select existing Asset",
"contents.assetsUpload": "Upload new Asset",
"contents.autotranslate": "Autotranslate from master language",
"contents.autotranslateMenu": "Autotranslate",
"contents.bulkFailed": "Failed to delete or update content. Please reload.",
"contents.calendar": "Scheduled Contents",
"contents.cancelStatus": "Cancel scheduled status",
@ -534,6 +547,7 @@
"contents.versionViewingDeleted": "Viewing deleted version **{version}**.",
"contents.viewLatest": "View latest",
"contents.viewReset": "Reset Default View",
"contents.wrapText": "Wrap Text",
"contributors.add": "Add Contributor",
"contributors.add.title": "Add or invite contributor",
"contributors.addFailed": "Failed to add contributors. Please reload.",
@ -690,6 +704,7 @@
"roles.add.description": "Add a new role if you need custom permission and policies.",
"roles.add.title": "Add a custom Role",
"roles.addFailed": "Failed to add role. Please reload.",
"roles.addPermission": "Add permission",
"roles.customRoles": "Custom roles",
"roles.default.owner": "Can do everything, including deleting the app.",
"roles.default.reader": "Can only read assets and contents.",
@ -715,6 +730,7 @@
"roles.revokeFailed": "Failed to revoke role. Please reload.",
"roles.roleNamePlaceholder": "Enter role name",
"roles.updateFailed": "Failed to update role. Please reload.",
"rules.addReferencedSchema": "Add Referenced Schema",
"rules.addStep": "Add Step",
"rules.addTrigger": "Add trigger",
"rules.advancedFormattingHint": "You can use advanced formatting",
@ -791,6 +807,7 @@
"rules.runningRule": "Rule '{name}' is currently running.",
"rules.runRuleConfirmText": "Do you really want to run the rule for all events?",
"rules.runRuleConfirmTitle": "Run rule",
"rules.schemas.addSchema": "Add Schema",
"rules.schemas.hint": "Define on which schemas and changes you want to run the content trigger.",
"rules.simulate": "Simulate",
"rules.simulateTooltip": "Simulate this rules using the last 100 events.",
@ -815,6 +832,7 @@
"rules.stepIgnoreErrorHint": "Ignore when this step fails and continue with the next step (after all retries), otherwise completes the flow.",
"rules.stepNameHint": "The name of the rule. Shown in logs and debug views.",
"rules.stop": "Rule will stop soon.",
"rules.trigger": "Trigger",
"rules.trigger.add": "Add Trigger",
"rules.trigger.edit": "Edit Trigger",
"rules.triggerAll": "Trigger on all content events",
@ -1021,6 +1039,7 @@
"schemas.modeSingle": "Single content",
"schemas.modeSingleDescription": "Best for single instances like the home page, privacy policies, settings...",
"schemas.nameWarning": "The name is used to identify your schema in every HTTP request. It must be unique within an app and cannot be changed once set.",
"schemas.previewUrls.add": "Add Preview URL",
"schemas.previewUrls.empty": "No preview urls configured.",
"schemas.previewUrls.help": "Checkout the integrated help page to learn more about preview URL's.",
"schemas.previewUrls.namePlaceholder": "Web or Mobile",
@ -1035,6 +1054,7 @@
"schemas.reloaded": "Schemas reloaded.",
"schemas.reorderFieldsFailed": "Failed to reorder fields. Please reload.",
"schemas.rules.action": "Action",
"schemas.rules.add": "Add Rule",
"schemas.rules.condition": "Condition in Javascript",
"schemas.rules.empty": "No field rules configured.",
"schemas.rules.title": "Field Rules",
@ -1289,6 +1309,7 @@
"workflows.add.description": "The workflow name is only used in the UI and not shown to editors.",
"workflows.add.title": "Add a new workflow",
"workflows.addStep": "Add Step",
"workflows.addTransition": "Add transition",
"workflows.createFailed": "Failed to create workflow. Please reload.",
"workflows.deleteConfirmText": "Do you really want to remove the workflow?",
"workflows.deleteConfirmTitle": "Delete workflow",

4
backend/src/Squidex/wwwroot/editor/squidex-editor.js

File diff suppressed because one or more lines are too long

48
frontend/.storybook/main.js

@ -1,48 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
const CopyPlugin = require('copy-webpack-plugin');
class FilterSassWarningsPlugin {
apply(compiler) {
compiler.hooks.done.tap('FilterSassWarningsPlugin', (stats) => {
stats.compilation.warnings = stats.compilation.warnings.filter(warning => {
const message = warning.message || warning.toString();
return !message.includes('sass-loader');
});
});
}
}
module.exports = {
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-interactions"
],
framework: {
name: "@storybook/angular",
options: {}
},
webpackFinal: async config => {
/*
* Copy lazy loaded libraries to output.
*/
config.plugins.push(new CopyPlugin({
patterns: [
{ from: './node_modules/ace-builds/src-min/', to: './dependencies/ace/' },
]
}));
config.plugins.push(new FilterSassWarningsPlugin());
config.resolve?.extensions?.push('.d.ts');
return config;
},
docs: {
autodocs: true
}
};

94
frontend/.storybook/main.ts

@ -0,0 +1,94 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import type { StorybookConfig } from '@analogjs/storybook-angular';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import tsconfigPaths from 'vite-tsconfig-paths';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links'],
framework: {
name: '@analogjs/storybook-angular',
options: {},
},
async viteFinal(config) {
const { mergeConfig } = await import('vite');
return mergeConfig(config, {
plugins: [
tsconfigPaths(),
viteStaticCopy({
targets: [
{
src: './node_modules/ace-builds/src-min/*',
dest: 'dependencies/ace',
},
],
}),
],
resolve: {
extensions: [
'.ts',
'.tsx',
'.mjs',
'.js',
'.jsx',
'.json',
'.d.ts',
],
alias: {
'@app/framework/internal': resolve(
__dirname,
'../src/app/framework/internal.ts',
),
'@app/framework': resolve(
__dirname,
'../src/app/framework/index.ts',
),
'@app/shared/internal': resolve(
__dirname,
'../src/app/shared/internal.ts',
),
'@app/shared': resolve(
__dirname,
'../src/app/shared/index.ts',
),
},
},
css: {
...config.css,
preprocessorOptions: {
scss: {
loadPaths: [resolve(__dirname, '../src/app/theme')],
includePaths: ['node_modules'],
quietDeps: true,
silenceDeprecations: ['color-functions', 'global-builtin', 'import', 'if-function'],
},
},
},
optimizeDeps: {
exclude: ['function-bind'],
},
ssr: {
noExternal: [
'@angular/core',
'@angular/common',
'@angular/forms',
'@angular/router',
],
},
});
},
};
export default config;

21
frontend/.storybook/preview.js

@ -1,21 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
export const parameters = {
actions: {
argTypesRegex: "^on[A-Z].*"
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
inlineStories: true
},
}

28
frontend/.storybook/preview.ts

@ -0,0 +1,28 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Preview } from "@storybook/angular";
import "./../src/styles.scss";
const preview: Preview = {
parameters: {
actions: {
argTypesRegex: "^on[A-Z].*",
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
inlineStories: true,
},
},
};
export default preview;

1
frontend/.storybook/tsconfig.json

@ -8,6 +8,7 @@
"../src/**/*.spec.ts",
],
"include": [
"preview.ts",
"../src/**/*.d.ts",
"../src/**/*.stories.ts",
],

10
frontend/angular.json

@ -178,18 +178,12 @@
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"builder": "@analogjs/storybook-angular:start-storybook",
"options": {
"configDir": ".storybook",
"browserTarget": "squidex:build",
"compodoc": false,
"port": 6006,
"stylePreprocessorOptions": {
"includePaths": [
"./src/app/theme",
"node_modules"
]
}
"open": false
}
},
"lint": {

3714
frontend/package-lock.json

File diff suppressed because it is too large

8
frontend/package.json

@ -66,11 +66,13 @@
"vis-data": "8.0.3",
"vis-network": "10.0.2",
"vis-util": "6.0.0",
"vite-plugin-static-copy": "^3.2.0",
"y-protocols": "^1.0.7",
"y-websocket": "^3.0.0",
"zone.js": "0.16.0"
},
"devDependencies": {
"@analogjs/storybook-angular": "^2.3.1",
"@angular-devkit/architect": "^0.2101.3",
"@angular-devkit/build-angular": "^21.1.3",
"@angular-eslint/builder": "21.2.0",
@ -84,7 +86,6 @@
"@angular/elements": "^21.1.3",
"@compodoc/compodoc": "^1.2.1",
"@storybook/addon-links": "^10.2.7",
"@storybook/angular": "^10.2.7",
"@storybook/testing-library": "^0.2.2",
"@types/ace": "^0.0.52",
"@types/codemirror": "5.60.17",
@ -111,9 +112,12 @@
"stylelint-config-standard-scss": "^17.0.0",
"stylelint-scss": "7.0.0",
"typescript": "5.9.3",
"vite-tsconfig-paths": "^6.0.5",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.0.8"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "*"
},
"overrides": {
"ng2-charts": {
"ng2-charts-schematics": "0.1.7"

163
frontend/refactor.js

@ -1,35 +1,152 @@
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
class AddButtonTypesStrategy {
#buttonTagPattern = /<button(\s[^>]*?)?>/gi;
fix(html) {
return html.replace(this.#buttonTagPattern, (match, attributes) => {
if (attributes && /\btype\s*=/i.test(attributes)) {
return match;
}
return attributes ? `<button type="button"${attributes}>` : `<button type="button">`;
});
}
}
class FixIconOnlyInteractivesStrategy {
#interactiveTagPattern = /<(a|button)(\s[^>]*)?>/gi;
#visuallyHiddenSpanPattern = /\s*<span\s+class="visually-hidden">([\s\S]*?)<\/span>/i;
#nestedInteractiveOpenPattern = /<(a|button)[\s>]/gi;
#nestedInteractiveClosePattern = /<\/(a|button)\s*>/gi;
#findClosingTag(html, from, tag) {
const openingTagPattern = new RegExp(`<${tag}[\\s>]`, "gi");
const closingTagPattern = new RegExp(`<\\/${tag}\\s*>`, "gi");
let depth = 1;
let position = from;
while (depth > 0) {
openingTagPattern.lastIndex = position;
closingTagPattern.lastIndex = position;
const nextOpening = openingTagPattern.exec(html);
const nextClosing = closingTagPattern.exec(html);
if (!nextClosing) {
return html.length;
}
if (nextOpening && nextOpening.index < nextClosing.index) {
depth++;
position = nextOpening.index + 1;
} else {
depth--;
position = nextClosing.index + nextClosing[0].length;
}
}
return position;
}
#isDirectChild(inner, spanIndex) {
let depth = 0;
let position = 0;
while (position < spanIndex) {
this.#nestedInteractiveOpenPattern.lastIndex = position;
this.#nestedInteractiveClosePattern.lastIndex = position;
const nextOpening = this.#nestedInteractiveOpenPattern.exec(inner);
const nextClosing = this.#nestedInteractiveClosePattern.exec(inner);
const openingBeforeSpan = nextOpening && nextOpening.index < spanIndex;
const closingBeforeSpan = nextClosing && nextClosing.index < spanIndex;
if (openingBeforeSpan && (!closingBeforeSpan || nextOpening.index < nextClosing.index)) {
depth++;
position = nextOpening.index + 1;
} else if (closingBeforeSpan) {
depth--;
position = nextClosing.index + 1;
} else {
break;
}
}
return depth === 0;
}
fix(html) {
const fixes = [];
let match;
this.#interactiveTagPattern.lastIndex = 0;
while ((match = this.#interactiveTagPattern.exec(html)) !== null) {
const tag = match[1];
const attributes = match[2] || "";
const openEnd = match.index + match[0].length;
const closeEnd = this.#findClosingTag(html, openEnd, tag);
const inner = html.slice(openEnd, closeEnd - `</${tag}>`.length);
const spanMatch = this.#visuallyHiddenSpanPattern.exec(inner);
if (!spanMatch || !this.#isDirectChild(inner, spanMatch.index)) {
continue;
}
const ariaLabel = spanMatch[1].trim().replace(/"/g, "'");
const innerWithout = inner.slice(0, spanMatch.index) + inner.slice(spanMatch.index + spanMatch[0].length);
const newOpenTag = `<${tag}${attributes} attr.aria-label="${ariaLabel}">`;
fixes.push({
from: match.index,
to: openEnd + inner.length,
newOpenTag,
newInner: innerWithout,
});
}
let result = html;
for (const fix of fixes.sort((a, b) => b.from - a.from)) {
result = result.slice(0, fix.from) + fix.newOpenTag + fix.newInner + result.slice(fix.to);
}
return result;
}
}
const ROOT_DIR = ".";
const SKIP_DIRS = ["node_modules", ".git", "dist", "build", ".cache"];
const BUTTON_TAG_RE = /<button(\s[^>]*?)?>/gi;
const SKIP_FILES = ["_theme.html"];
function fixButtons(html) {
return html.replace(BUTTON_TAG_RE, (match, attrs) => {
if (attrs && /\btype\s*=/i.test(attrs)) return match;
return attrs ? `<button type="button"${attrs}>` : `<button type="button">`;
});
}
const strategies = [
new AddButtonTypesStrategy(),
new FixIconOnlyInteractivesStrategy(),
];
function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (!SKIP_DIRS.includes(entry.name)) walk(fullPath);
} else if (entry.isFile() && entry.name.endsWith(".component.html")) {
const original = fs.readFileSync(fullPath, "utf8");
const fixed = fixButtons(original);
if (fixed !== original) {
fs.writeFileSync(fullPath, fixed, "utf8");
console.log(`Fixed: ${fullPath}`);
}
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (!SKIP_DIRS.includes(entry.name)) {
walk(fullPath);
}
} else if (entry.isFile() && entry.name.endsWith(".component.html") && !SKIP_FILES.includes(entry.name)) {
let content = fs.readFileSync(fullPath, "utf8");
let changed = false;
for (const strategy of strategies) {
const updated = strategy.fix(content, fullPath);
if (updated !== content) {
content = updated;
changed = true;
}
}
if (changed) {
fs.writeFileSync(fullPath, content, "utf8");
}
}
}
}
}
walk(path.resolve(ROOT_DIR));

8
frontend/src/app/_theme.html

@ -1503,7 +1503,7 @@
<i class="icon-settings"></i>
</button>
<button class="btn btn-text-danger btn-outline-secondary" type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -1539,7 +1539,7 @@
<i class="icon-settings"></i>
</button>
<button class="btn btn-text-danger btn-outline-secondary" type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -1556,7 +1556,7 @@
<i class="icon-settings"></i>
</button>
<button class="btn btn-text-danger btn-outline-secondary" type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -1572,7 +1572,7 @@
<i class="icon-settings"></i>
</button>
<button class="btn btn-text-danger btn-outline-secondary" type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

21
frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.html

@ -16,19 +16,34 @@
<td class="cell-actions-lg">
@if (eventConsumer.canReset) {
<button class="btn btn-text-secondary" (click)="reset()" title="i18n:eventConsumers.resetTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'eventConsumers.resetTooltip' | sqxTranslate }}"
(click)="reset()"
title="i18n:eventConsumers.resetTooltip"
type="button">
<i class="icon icon-reset"></i>
</button>
}
@if (eventConsumer.canStart) {
<button class="btn btn-text-secondary" (click)="start()" title="i18n:eventConsumers.startTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'eventConsumers.startTooltip' | sqxTranslate }}"
(click)="start()"
title="i18n:eventConsumers.startTooltip"
type="button">
<i class="icon icon-play"></i>
</button>
}
@if (eventConsumer.canStop) {
<button class="btn btn-text-secondary" (click)="stop()" title="i18n:eventConsumers.stopTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'eventConsumers.stopTooltip' | sqxTranslate }}"
(click)="stop()"
title="i18n:eventConsumers.stopTooltip"
type="button">
<i class="icon icon-pause"></i>
</button>
}

3
frontend/src/app/features/administration/pages/event-consumers/event-consumer.component.ts

@ -9,7 +9,7 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { EventConsumerDto, TooltipDirective } from '@app/shared';
import { EventConsumerDto, TooltipDirective, TranslatePipe } from '@app/shared';
import { EventConsumersState } from '../../internal';
@Component({
@ -18,6 +18,7 @@ import { EventConsumersState } from '../../internal';
templateUrl: './event-consumer.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
TranslatePipe,
TooltipDirective,
],
})

2
frontend/src/app/features/administration/pages/event-consumers/event-consumers-page.component.html

@ -50,7 +50,7 @@
</sqx-layout>
<router-outlet />
<sqx-modal-dialog (dialogClose)="eventConsumerErrorDialog.hide()" *sqxModal="eventConsumerErrorDialog">
<ng-container title> {{ "common.error" | sqxTranslate }} </ng-container>
<ng-container title> {{ "common.error" | sqxTranslate }}</ng-container>
<ng-container content>
<textarea class="form-control error-message small" readonly>{{ eventConsumerError }}</textarea>
</ng-container>

19
frontend/src/app/features/administration/pages/users/user.component.html

@ -11,18 +11,31 @@
<td class="cell-actions-lg">
@if (user.canLock) {
<button class="btn btn-text-secondary" (click)="lock()" sqxStopClick title="i18n:users.lockTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'users.lockTooltip' | sqxTranslate }}"
(click)="lock()"
sqxStopClick
title="i18n:users.lockTooltip"
type="button">
<i class="icon icon-unlocked"></i>
</button>
}
@if (user.canUnlock) {
<button class="btn btn-text-secondary" (click)="unlock()" sqxStopClick title="i18n:users.unlockTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'users.unlockTooltip' | sqxTranslate }}"
(click)="unlock()"
sqxStopClick
title="i18n:users.unlockTooltip"
type="button">
<i class="icon icon-lock"></i>
</button>
}
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="deleteUser"
confirmText="i18n:users.deleteConfirmText"
confirmTitle="i18n:users.deleteConfirmTitle"
@ -30,7 +43,7 @@
(sqxConfirmClick)="delete()"
sqxStopClick
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</td>
</tr>

3
frontend/src/app/features/administration/pages/users/user.component.ts

@ -10,7 +10,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
import { ConfirmClickDirective, StopClickDirective, TooltipDirective, UserDtoPicture } from '@app/shared';
import { ConfirmClickDirective, StopClickDirective, TooltipDirective, TranslatePipe, UserDtoPicture } from '@app/shared';
import { UserDto, UsersState } from '../../internal';
@Component({
@ -24,6 +24,7 @@ import { UserDto, UsersState } from '../../internal';
RouterLinkActive,
StopClickDirective,
TooltipDirective,
TranslatePipe,
UserDtoPicture,
],
})

4
frontend/src/app/features/api/api-area.component.html

@ -3,7 +3,7 @@
<ng-container>
<ul class="nav nav-light flex-column">
<li class="nav-item" sqxTourStep="graphql">
<a class="nav-link" routerLink="graphql" routerLinkActive="active"> {{ "api.graphql" | sqxTranslate }} </a>
<a class="nav-link" routerLink="graphql" routerLinkActive="active"> {{ "api.graphql" | sqxTranslate }}</a>
</li>
<li class="nav-item nav-heading">{{ "common.openAPI" | sqxTranslate }}</li>
@ -15,7 +15,7 @@
</li>
<li class="nav-item" sqxTourStep="generalAPi">
<a class="nav-link" href="api/docs" sqxExternalLink> {{ "api.generalApi" | sqxTranslate }} </a>
<a class="nav-link" href="api/docs" sqxExternalLink> {{ "api.generalApi" | sqxTranslate }}</a>
</li>
</ul>
</ng-container>

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

@ -3,13 +3,15 @@
<div #graphiQLContainer inner sqxTourStep="graphQLExplorer"></div>
@if (clientsReadable) {
<button class="btn btn-simple btn-options" (click)="clientsDialog.show()" type="button"><i class="icon-clients"></i></button>
<button class="btn btn-simple btn-options" attr.aria-label="{{ 'api.graphqlShowClients' | sqxTranslate }}" (click)="clientsDialog.show()" type="button">
<i class="icon-clients"></i>
</button>
}
</sqx-layout>
<sqx-modal-dialog (dialogClose)="clientsDialog.hide()" *sqxModal="clientsDialog">
<ng-container title> {{ "api.selectClient" | sqxTranslate }} </ng-container>
<ng-container title> {{ "api.selectClient" | sqxTranslate }}</ng-container>
<ng-container content>
<sqx-form-hint> {{ "api.selectClientDescription" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "api.selectClientDescription" | sqxTranslate }}</sqx-form-hint>
<sqx-form-row for="client" label="common.client" vertical>
<select class="form-control" id="client" [ngModel]="clientSelected" (ngModelChange)="selectClient($event)">
<option [ngValue]="null">{{ "api.noClient" | sqxTranslate }}</option>

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

@ -35,7 +35,7 @@
<sqx-app [app]="app" (leave)="leaveApp($event)" />
</div>
} @empty {
<small class="team-empty"> {{ "teams.empty" | sqxTranslate }} </small>
<small class="team-empty"> {{ "teams.empty" | sqxTranslate }}</small>
}
</div>
</div>

2
frontend/src/app/features/apps/pages/news-dialog.component.html

@ -1,5 +1,5 @@
<sqx-modal-dialog (dialogClose)="dialogClose.emit()" size="lg">
<ng-container title> {{ "news.title" | sqxTranslate }} </ng-container>
<ng-container title> {{ "news.title" | sqxTranslate }}</ng-container>
<ng-container content>
<div class="help">
<h1>{{ "news.headline" | sqxTranslate }}</h1>

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

@ -9,7 +9,7 @@
<img class="header-left" animate.enter="fade-in" animate.leave="fade-out" src="./images/logo-white-small.png" />
<div class="onboarding-enter-leave text-center" animate.enter="slide-left-in" animate.leave="slide-left-out">
<h1>
{{ "tour.welcome" | sqxTranslate }} <span class="header-focus">{{ "tour.welcomeProduct" | sqxTranslate }}</span>
{{ "tour.welcome" | sqxTranslate }}<span class="header-focus">{{ "tour.welcomeProduct" | sqxTranslate }}</span>
</h1>
<div inline="false" [sqxMarkdown]="'tour.stepIntroText' | sqxTranslate"></div>

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

@ -1,6 +1,6 @@
<form [formGroup]="editForm.form" (ngSubmit)="renameAssetTag()">
<sqx-modal-dialog (dialogClose)="emitClose()">
<ng-container title> {{ "common.renameTag" | sqxTranslate }} </ng-container>
<ng-container title> {{ "common.renameTag" | sqxTranslate }}</ng-container>
<ng-container content>
<sqx-form-error [error]="editForm.error | async" />
<sqx-form-row for="tagName" label="common.name" required vertical>

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

@ -15,7 +15,12 @@
<div class="badge badge-secondary rounded-pill">{{ tag.count }}</div>
@if (canRename) {
<button class="btn-sm btn-text-secondary btn-rename" (click)="renameTag(tag)" sqxStopClick type="button">
<button
class="btn-sm btn-text-secondary btn-rename"
attr.aria-label="{{ 'common.rename' | sqxTranslate }}"
(click)="renameTag(tag)"
sqxStopClick
type="button">
<i class="icon-pencil"></i>
</button>
}

9
frontend/src/app/features/assets/pages/assets-page.component.html

@ -37,11 +37,18 @@
<div class="col-auto">
<div class="btn-group">
<button class="btn btn-secondary btn-toggle" [class.btn-primary]="listMode" (click)="changeView(true)" [disabled]="listMode" type="button">
<button
class="btn btn-secondary btn-toggle"
attr.aria-label="{{ 'assets.listView' | sqxTranslate }}"
[class.btn-primary]="listMode"
(click)="changeView(true)"
[disabled]="listMode"
type="button">
<i class="icon-list"></i>
</button>
<button
class="btn btn-secondary btn-toggle"
attr.aria-label="{{ 'assets.gridView' | sqxTranslate }}"
[class.btn-primary]="!listMode"
(click)="changeView(false)"
[disabled]="!listMode"

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

@ -9,10 +9,20 @@
<option ngValue="month">{{ "common.monthly" | sqxTranslate }}</option>
</select>
<button class="btn btn-text-secondary btn-navigate ms-2" (click)="goPrev()" [disabled]="isLoading" type="button">
<button
class="btn btn-text-secondary btn-navigate ms-2"
attr.aria-label="{{ 'common.prev' | sqxTranslate }}"
(click)="goPrev()"
[disabled]="isLoading"
type="button">
<i class="icon-caret-left"></i>
</button>
<button class="btn btn-text-secondary btn-navigate ms-2" (click)="goNext()" [disabled]="isLoading" type="button">
<button
class="btn btn-text-secondary btn-navigate ms-2"
attr.aria-label="{{ 'common.next' | sqxTranslate }}"
(click)="goNext()"
[disabled]="isLoading"
type="button">
<i class="icon-caret-right"></i>
</button>
</ng-container>
@ -21,14 +31,14 @@
</ng-container>
</sqx-layout>
<sqx-modal-dialog (dialogClose)="contentDialog.hide()" *sqxModal="contentDialog">
<ng-container title> {{ "common.content" | sqxTranslate }} </ng-container>
<ng-container title> {{ "common.content" | sqxTranslate }}</ng-container>
<ng-container content>
@if (contentSelected && contentSelected.scheduleJob) {
<div>
<sqx-form-row for="id" hideError label="common.id" labelSize="lg">
<div class="input-group">
<input class="form-control" id="id" #inputId name="id" readonly value="{{ contentSelected.id }}" />
<button class="btn btn-outline-secondary" [sqxCopy]="inputId" type="button">
<button class="btn btn-outline-secondary" attr.aria-label="{{ 'common.copy' | sqxTranslate }}" [sqxCopy]="inputId" type="button">
<i class="icon-copy"></i>
</button>
</div>

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

@ -1,3 +1,25 @@
<sqx-layout layout="right" titleText="i18n:comments.title" white="true" width="20">
<sqx-comments [commentsId]="commentsId | async" />
</sqx-layout>
@if (commentsTab | async; as tab) {
<sqx-layout layout="right" white="true" width="20">
<ng-container menu>
<ul class="nav nav-tabs2">
<li class="nav-item">
<a
class="nav-link"
[class.active]="tab === 'unresolved'"
[queryParams]="{ commentsTab: 'unresolved' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "comments.unresolved" | sqxTranslate }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" [class.active]="tab === 'all'" [queryParams]="{ commentsTab: 'all' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "comments.all" | sqxTranslate }}
</a>
</li>
</ul>
</ng-container>
<sqx-comments [content]="content | async" [commentsId]="commentsId | async" [resolved]="tab === 'all'" />
</sqx-layout>
}

9
frontend/src/app/features/content/pages/comments/comments-page.component.scss

@ -6,4 +6,13 @@ sqx-comments {
flex-direction: column;
flex-grow: 1;
height: 100%;
}
.nav-tabs2 {
@include absolute(auto, auto, 0, 1rem);
flex-wrap: nowrap;
a {
padding-bottom: 1.75rem;
}
}

12
frontend/src/app/features/content/pages/comments/comments-page.component.ts

@ -7,9 +7,11 @@
import { AsyncPipe } from '@angular/common';
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { map } from 'rxjs/operators';
import { CommentsComponent, LayoutComponent } from '@app/shared';
import { CommentsComponent, ContentsState, LayoutComponent, TranslatePipe } from '@app/shared';
type Tab = 'unresolved' | 'all';
@Component({
selector: 'sqx-comments-page',
@ -19,13 +21,19 @@ import { CommentsComponent, LayoutComponent } from '@app/shared';
AsyncPipe,
CommentsComponent,
LayoutComponent,
RouterLink,
TranslatePipe,
],
})
export class CommentsPageComponent {
public commentsId = this.route.parent!.params.pipe(map(x => x['contentId']));
public commentsTab = this.route.queryParams.pipe(map(x => x['commentsTab'] as Tab || 'unresolved'));
public readonly content = this.contentStates.selectedContent;
constructor(
private readonly route: ActivatedRoute,
private readonly contentStates: ContentsState,
) {
}
}

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

@ -4,7 +4,7 @@
<label for="id">{{ "common.id" | sqxTranslate }}</label>
<div class="input-group">
<input class="form-control" id="id" #inputId name="id" readonly value="{{ content.id }}" />
<button class="btn btn-outline-secondary" [sqxCopy]="inputId" type="button">
<button class="btn btn-outline-secondary" attr.aria-label="{{ 'common.copy' | sqxTranslate }}" [sqxCopy]="inputId" type="button">
<i class="icon-copy"></i>
</button>
</div>

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

@ -19,32 +19,57 @@
@if (content && contentTab | async; as tab) {
<ul class="nav nav-tabs2">
<li class="nav-item">
<a class="nav-link" [class.active]="tab === 'editor'" [queryParams]="{ tab: 'editor' }" [routerLink]="[]">
<a
class="nav-link"
[class.active]="tab === 'editor'"
[queryParams]="{ tab: 'editor' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "contents.contentTab.editor" | sqxTranslate }}
</a>
</li>
<li>
<a class="nav-link" [class.active]="tab === 'references'" [queryParams]="{ tab: 'references' }" [routerLink]="[]">
<a
class="nav-link"
[class.active]="tab === 'references'"
[queryParams]="{ tab: 'references' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "contents.contentTab.references" | sqxTranslate }}
</a>
</li>
<li>
<a class="nav-link" [class.active]="tab === 'referencing'" [queryParams]="{ tab: 'referencing' }" [routerLink]="[]">
<a
class="nav-link"
[class.active]="tab === 'referencing'"
[queryParams]="{ tab: 'referencing' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "contents.contentTab.referencing" | sqxTranslate }}
</a>
</li>
<li>
<a class="nav-link" [class.active]="tab === 'inspect'" [queryParams]="{ tab: 'inspect' }" [routerLink]="[]">
<a
class="nav-link"
[class.active]="tab === 'inspect'"
[queryParams]="{ tab: 'inspect' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "contents.contentTab.inspect" | sqxTranslate }}
</a>
</li>
@if (schema.properties.contentEditorUrl) {
<li>
<a class="nav-link" [class.active]="tab === 'extension'" [queryParams]="{ tab: 'extension' }" [routerLink]="[]">
<a
class="nav-link"
[class.active]="tab === 'extension'"
[queryParams]="{ tab: 'extension' }"
queryParamsHandling="merge"
[routerLink]="[]">
{{ "common.extension" | sqxTranslate }}
</a>
</li>

12
frontend/src/app/features/content/pages/content/content-page.component.ts

@ -11,7 +11,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { Observable, of } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators';
import { AnnotationCreate, AnnotationCreateAfterNavigate, AnnotationsSelect, AnnotationsSelectAfterNavigate, ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, CollaborationService, CommentsState, ConfirmClickDirective, ContentDto, ContentsState, defined, DialogService, DropdownMenuComponent, EditContentForm, LanguageSelectorComponent, LanguagesState, LayoutComponent, LocalStoreService, MessageBus, ModalDirective, ModalModel, ModalPlacementDirective, NotifoComponent, PreviousUrl, ResolveAssets, ResolveContents, SchemaDto, SchemasState, Settings, ShortcutDirective, SidebarMenuDirective, Subscriptions, TempService, TitleComponent, ToolbarComponent, ToolbarService, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe, Types, Version, WatchingUsersComponent } from '@app/shared';
import { AnnotationCreate, AnnotationCreateAfterNavigate, AnnotationsSelected, AnnotationsSelectedAfterNavigate, ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, CollaborationService, CommentsState, ConfirmClickDirective, ContentDto, ContentsState, defined, DialogService, DropdownMenuComponent, EditContentForm, LanguageSelectorComponent, LanguagesState, LayoutComponent, LocalStoreService, MessageBus, ModalDirective, ModalModel, ModalPlacementDirective, NotifoComponent, PreviousUrl, ResolveAssets, ResolveContents, SchemaDto, SchemasState, Settings, ShortcutDirective, SidebarMenuDirective, Subscriptions, TempService, TitleComponent, ToolbarComponent, ToolbarService, TooltipDirective, TourHintDirective, TourStepDirective, TranslatePipe, Types, Version, WatchingUsersComponent } from '@app/shared';
import { ContentExtensionComponent } from '../../shared/content-extension.component';
import { PreviewButtonComponent } from '../../shared/preview-button.component';
import { ContentEditorComponent } from './editor/content-editor.component';
@ -20,6 +20,8 @@ import { ContentReferencesComponent } from './references/content-references.comp
type SaveNavigationMode = 'Close' | 'Add' | 'Edit';
type Tab = 'editor' | 'references' | 'referencing' | 'inspect' | 'extension';
@Component({
selector: 'sqx-content-page',
styleUrls: ['./content-page.component.scss'],
@ -71,7 +73,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnInit {
public formContext: any;
public contentTab = this.route.queryParams.pipe(map(x => x['tab'] || 'editor'));
public contentTab = this.route.queryParams.pipe(map(x => x['tab'] as Tab || 'editor'));
public contentId = '';
public content?: ContentDto | null;
public contentVersion: Version | null = null;
@ -135,14 +137,14 @@ export class ContentPageComponent implements CanComponentDeactivate, OnInit {
}));
this.subscriptions.add(
this.messageBus.of(AnnotationsSelect)
this.messageBus.of(AnnotationsSelected)
.subscribe(async message => {
if (message.annotations.length > 0) {
if (message.commentIds.length > 0) {
await this.router.navigate(['comments'], { relativeTo: this.route });
}
setTimeout(() => {
this.messageBus.emit(new AnnotationsSelectAfterNavigate(message.annotations));
this.messageBus.emit(new AnnotationsSelectedAfterNavigate(message.commentIds));
});
}));

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

@ -4,7 +4,13 @@
<div class="row flex-nowrap flex-grow-1 gx-2 overflow-hidden">
<div class="col-auto ms-8">
<sqx-notifo position="bottom-left" topic="apps/{{ contentsState.appId }}/schemas/{{ schema.id }}/contents" />
<button class="btn btn-text-secondary" (click)="reload()" shortcut="CTRL + B" title="i18n:contents.refreshTooltip" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.refreshTooltip' | sqxTranslate }}"
(click)="reload()"
shortcut="CTRL + B"
title="i18n:contents.refreshTooltip"
type="button">
<i class="icon-reset"></i>
</button>
</div>

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

@ -10,7 +10,7 @@
<ng-container>
<ul class="nav nav-light mb-2 flex-column">
<li class="nav-item">
<a class="nav-link" routerLink="__calendar" routerLinkActive="active"> {{ "contents.calendar" | sqxTranslate }} </a>
<a class="nav-link" routerLink="__calendar" routerLinkActive="active"> {{ "contents.calendar" | sqxTranslate }}</a>
</li>
</ul>
<ng-container>

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

@ -76,7 +76,7 @@
}
@if (!hasField) {
<sqx-form-hint> {{ "contents.arrayNoFields" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "contents.arrayNoFields" | sqxTranslate }}</sqx-form-hint>
}
} @else {
@if (schemasList.length > 1) {
@ -102,7 +102,7 @@
}
@if (schemasList.length === 0) {
<sqx-form-hint> {{ "contents.componentsNoSchema" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "contents.componentsNoSchema" | sqxTranslate }}</sqx-form-hint>
}
}
</div>
@ -124,10 +124,20 @@
@if (items.length > 0) {
<div class="col-auto d-flex gap1">
<button class="btn btn-text-secondary" (click)="expandAll()" title="i18n:contents.arrayExpandAll" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayExpandAll' | sqxTranslate }}"
(click)="expandAll()"
title="i18n:contents.arrayExpandAll"
type="button">
<i class="icon-plus2"></i>
</button>
<button class="btn btn-text-secondary" (click)="collapseAll()" title="i18n:contents.arrayCollapseAll" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayCollapseAll' | sqxTranslate }}"
(click)="collapseAll()"
title="i18n:contents.arrayCollapseAll"
type="button">
<i class="icon-minus2"></i>
</button>
</div>

46
frontend/src/app/features/content/shared/forms/array-item.component.html

@ -10,17 +10,36 @@
</div>
<div class="col-auto d-flex gap-1 pe-4">
<button class="btn btn-text-secondary" (click)="moveTop()" [disabled]="isDisabled || isFirst" title="i18n:contents.arrayMoveTop" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayMoveTop' | sqxTranslate }}"
(click)="moveTop()"
[disabled]="isDisabled || isFirst"
title="i18n:contents.arrayMoveTop"
type="button">
<i class="icon-caret-top"></i>
</button>
<button class="btn btn-text-secondary" (click)="moveUp()" [disabled]="isDisabled || isFirst" title="i18n:contents.arrayMoveUp" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayMoveUp' | sqxTranslate }}"
(click)="moveUp()"
[disabled]="isDisabled || isFirst"
title="i18n:contents.arrayMoveUp"
type="button">
<i class="icon-caret-up"></i>
</button>
<button class="btn btn-text-secondary" (click)="moveDown()" [disabled]="isDisabled || isLast" title="i18n:contents.arrayMoveDown" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayMoveDown' | sqxTranslate }}"
(click)="moveDown()"
[disabled]="isDisabled || isLast"
title="i18n:contents.arrayMoveDown"
type="button">
<i class="icon-caret-down"></i>
</button>
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayMoveBottom' | sqxTranslate }}"
(click)="moveBottom()"
[disabled]="isDisabled || isLast"
title="i18n:contents.arrayMoveBottom"
@ -29,6 +48,7 @@
</button>
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayExpandItem' | sqxTranslate }}"
[class.hidden]="!(isCollapsed | async)"
(click)="expand()"
title="i18n:contents.arrayExpandItem"
@ -37,6 +57,7 @@
</button>
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayCollapseItem' | sqxTranslate }}"
[class.hidden]="isCollapsed | async"
(click)="collapse()"
title="i18n:contents.arrayCollapseItem"
@ -46,11 +67,22 @@
</div>
<div class="col-auto">
<button class="btn btn-text-secondary" (click)="clone.emit()" [disabled]="isDisabled" title="i18n:contents.arrayCloneItem" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'contents.arrayCloneItem' | sqxTranslate }}"
(click)="clone.emit()"
[disabled]="isDisabled"
title="i18n:contents.arrayCloneItem"
type="button">
<i class="icon-clone"></i>
</button>
<button class="btn btn-text-danger" (click)="itemRemove.emit()" [disabled]="isDisabled" type="button">
<i class="icon-bin2"></i>
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
(click)="itemRemove.emit()"
[disabled]="isDisabled"
type="button">
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -73,7 +105,7 @@
}
@if (isInvalidComponent | async) {
<sqx-form-hint> {{ "contents.componentInvalid" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "contents.componentInvalid" | sqxTranslate }}</sqx-form-hint>
}
</div>
</div>

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

@ -26,6 +26,7 @@
<div class="btn-group">
<button
class="btn btn-secondary btn-toggle"
attr.aria-label="{{ 'contents.assetsListView' | sqxTranslate }}"
[class.btn-primary]="snapshot.isListView"
(click)="changeView(true)"
[disabled]="snapshot.isListView"
@ -34,6 +35,7 @@
</button>
<button
class="btn btn-secondary btn-toggle"
attr.aria-label="{{ 'contents.assetsGridView' | sqxTranslate }}"
[class.btn-primary]="!snapshot.isListView"
(click)="changeView(false)"
[disabled]="!snapshot.isListView"

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

@ -40,7 +40,7 @@
}
@if (schemasList.length === 0) {
<sqx-form-hint> {{ "contents.componentNoSchema" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "contents.componentNoSchema" | sqxTranslate }}</sqx-form-hint>
}
}
</div>

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

@ -2,7 +2,7 @@
<div [class.col-12]="!formModelCompare" [class.col-6]="formModelCompare">
<sqx-focus-marker [controlId]="formModel.path">
@if (!(formModel.hiddenChanges | async)) {
<div class="table-items-row table-items-row-summary" [class.field-invalid]="isInvalid | async">
<div class="table-items-row table-items-row-summary" [class.bounce]="isSelected | async" [class.field-invalid]="isInvalid | async">
@if (formModel.hasChanges | async) {
<div class="change-marker-host">
<div class="change-marker">{{ "contents.pendingChangesTitle" | sqxTranslate }}</div>
@ -59,7 +59,7 @@
@if (!(isDisabled | async)) {
<div class="copy-button-container">
@if (isDifferent | async) {
<button class="btn btn-primary btn-sm field-copy" (click)="copy()" type="button">
<button class="btn btn-primary btn-sm field-copy" attr.aria-label="{{ 'common.copy' | sqxTranslate }}" (click)="copy()" type="button">
<i class="icon-arrow_back"></i>
</button>
}

20
frontend/src/app/features/content/shared/forms/content-field.component.ts

@ -7,8 +7,8 @@
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
import { booleanAttribute, Component, EventEmitter, HostBinding, inject, Input, numberAttribute, Optional, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { AppLanguageDto, AppsState, changed$, CommentsState, disabled$, EditContentForm, FieldForm, FocusMarkerComponent, invalid$, LocalStoreService, MenuItemComponent, SchemaDto, Settings, TranslateDto, TranslatePipe, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared';
import { BehaviorSubject, Observable } from 'rxjs';
import { AppLanguageDto, AppsState, changed$, CommentSelected, CommentsState, disabled$, EditContentForm, FieldForm, FocusMarkerComponent, invalid$, LocalStoreService, MenuItemComponent, MessageBus, SchemaDto, Settings, Subscriptions, TranslateDto, TranslatePipe, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared';
import { FieldCopyButtonComponent } from './field-copy-button.component';
import { FieldEditorComponent } from './field-editor.component';
import { FieldLanguagesComponent } from './field-languages.component';
@ -29,6 +29,8 @@ import { FieldLanguagesComponent } from './field-languages.component';
],
})
export class ContentFieldComponent {
private readonly subscriptions = new Subscriptions();
@Output()
public languageChange = new EventEmitter<AppLanguageDto>();
@ -68,6 +70,7 @@ export class ContentFieldComponent {
public isInvalid?: Observable<boolean>;
public isCollapsed = false;
public isDisabled?: Observable<boolean>;
public isSelected = new BehaviorSubject<boolean>(false);
public readonly hasTranslator = inject(UIOptions).value.canUseTranslator;
public readonly hasChatBot = inject(UIOptions).value.canUseChatBot;
@ -92,10 +95,23 @@ export class ContentFieldComponent {
@Optional() public readonly commentsState: CommentsState,
private readonly appsState: AppsState,
private readonly localStore: LocalStoreService,
private readonly messageBus: MessageBus,
private readonly translations: TranslationsService,
) {
}
public ngOnInit() {
this.subscriptions.add(
this.messageBus.of(CommentSelected)
.subscribe(message => {
const isSelected = message.editorId.indexOf(this.formModel.path) === 0;
if (isSelected !== this.isSelected.value) {
this.isSelected.next(isSelected);
}
}));
}
public ngOnChanges(changes: TypedSimpleChanges<this>) {
this.showAllControls = this.localStore.getBoolean(this.showAllControlsKey());

1
frontend/src/app/features/content/shared/forms/field-copy-button.component.html

@ -2,6 +2,7 @@
<button
class="btn btn-outline-secondary btn-sm dropdown-toggle"
#button
attr.aria-label="{{ 'common.copy' | sqxTranslate }}"
(click)="dropdown.toggle()"
tabindex="-1"
title="{{ 'common.copy' | sqxTranslate }}"

17
frontend/src/app/features/content/shared/forms/field-editor.component.html

@ -1,5 +1,5 @@
@if (formModel) {
<div class="field" [class.expanded]="isExpanded && !isCollapsed">
<div class="field" (click)="select()" [class.expanded]="isExpanded && !isCollapsed" #root>
<div class="row">
<div class="col-4">
<div class="truncate">
@ -27,8 +27,19 @@
tabIndex="-1" />
}
@if (!hasComments) {
<sqx-menu-item
(action)="annotationCreate()"
[disabled]="isCollapsed || !hasChatBot"
icon="comments"
menuLabel="i18n:contents.comment"
small
tabIndex="-1"
tooltip="i18n:contents.comment" />
}
<sqx-menu-item
(action)="this.toggleExpanded()"
(action)="toggleExpanded()"
[disabled]="(isDisabled | async) || isCollapsed"
icon="fullscreen"
menuLabel="i18n:contents.fieldFullscreenMenu"
@ -37,7 +48,7 @@
tooltip="i18n:contents.fieldFullscreen" />
<sqx-menu-item
(action)="this.unset()"
(action)="unset()"
confirmRememberKey="unsetValue"
confirmText="i18n:contents.unsetValueConfirmText"
confirmTitle="i18n:contents.unsetValueConfirmTitle"

40
frontend/src/app/features/content/shared/forms/field-editor.component.ts

@ -8,8 +8,8 @@
import { AsyncPipe } from '@angular/common';
import { booleanAttribute, Component, ElementRef, EventEmitter, Input, numberAttribute, Output, ViewChild } from '@angular/core';
import { AbstractControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Observable } from 'rxjs';
import { AbstractContentForm, AnnotationCreate, AnnotationsSelect, AnyFieldDto, AppLanguageDto, ChatDialogComponent, CheckboxGroupComponent, CodeEditorComponent, ColorPickerComponent, CommentsState, ControlErrorsComponent, DateTimeEditorComponent, DialogModel, disabled$, EditContentForm, FormHintComponent, GeolocationEditorComponent, hasNoValue$, HTTP, IndeterminateValueDirective, MarkdownDirective, MathHelper, MenuComponent, MenuItemComponent, MessageBus, ModalDirective, RadioGroupComponent, ReferenceInputComponent, ReferencesFieldPropertiesDto, RichEditorComponent, StarsComponent, TagEditorComponent, ToggleComponent, TransformInputDirective, TypedSimpleChanges, Types } from '@app/shared';
import { BehaviorSubject, Observable } from 'rxjs';
import { AbstractContentForm, AnnotationCreate, AnnotationsSelected, AnyFieldDto, AppLanguageDto, ChatDialogComponent, CheckboxGroupComponent, CodeEditorComponent, ColorPickerComponent, CommentSelected, CommentsState, ControlErrorsComponent, DateTimeEditorComponent, DialogModel, disabled$, EditContentForm, FieldSelected, FormHintComponent, GeolocationEditorComponent, hasNoValue$, HTTP, IndeterminateValueDirective, MarkdownDirective, MathHelper, MenuComponent, MenuItemComponent, MessageBus, ModalDirective, RadioGroupComponent, ReferenceInputComponent, ReferencesFieldPropertiesDto, RichEditorComponent, StarsComponent, Subscriptions, TagEditorComponent, ToggleComponent, TransformInputDirective, TypedSimpleChanges, Types } from '@app/shared';
import { ReferenceDropdownComponent } from '../references/reference-dropdown.component';
import { ReferencesCheckboxesComponent } from '../references/references-checkboxes.component';
import { ReferencesEditorComponent } from '../references/references-editor.component';
@ -64,7 +64,7 @@ import { UserInfoEditorComponent } from './user-info-editor.component';
],
})
export class FieldEditorComponent {
public readonly uniqueId = MathHelper.guid();
private readonly subscriptions = new Subscriptions();
@Output()
public expandedChange = new EventEmitter();
@ -105,12 +105,18 @@ export class FieldEditorComponent {
@Input()
public displaySuffix = '';
@ViewChild('root', { static: false })
public root!: ElementRef<HTMLElement>;
@ViewChild('editor', { static: false })
public editor!: ElementRef;
public readonly uniqueId = MathHelper.guid();
public isDisabled!: Observable<boolean>;
public isEmpty!: Observable<boolean>;
public isExpanded = false;
public isSelected = new BehaviorSubject<boolean>(false);
public chatDialog = new DialogModel();
public annotations?: Observable<ReadonlyArray<Annotation>>;
@ -126,6 +132,10 @@ export class FieldEditorComponent {
return this.field.properties.fieldType === 'String';
}
public get hasComments() {
return this.field.properties.hasComments;
}
public get schemaIds() {
return Types.is(this.field.properties, ReferencesFieldPropertiesDto) ? this.field.properties.schemaIds : undefined;
}
@ -135,6 +145,22 @@ export class FieldEditorComponent {
) {
}
public ngOnInit() {
this.subscriptions.add(
this.messageBus.of(CommentSelected)
.subscribe(message => {
const isSelected = message.editorId.indexOf(this.formModel.path) === 0;
if (isSelected) {
this.root.nativeElement.scrollIntoView();
}
if (isSelected !== this.isSelected.value) {
this.isSelected.next(isSelected);
}
}));
}
public ngOnChanges(changes: TypedSimpleChanges<this>) {
if (changes.formModel) {
this.isDisabled = disabled$(this.formModel.form);
@ -167,12 +193,16 @@ export class FieldEditorComponent {
this.isExpanded = !this.isExpanded;
}
public annotationCreate(annotation: AnnotationSelection) {
public select() {
this.messageBus.emit(new FieldSelected(this.formModel.path));
}
public annotationCreate(annotation?: AnnotationSelection) {
this.messageBus.emit(new AnnotationCreate(this.formModel.path, annotation));
}
public annotationsSelect(annotation: ReadonlyArray<string>) {
this.messageBus.emit(new AnnotationsSelect(annotation));
this.messageBus.emit(new AnnotationsSelected(annotation));
}
public annotationsUpdate(annotations: ReadonlyArray<Annotation>) {

12
frontend/src/app/features/content/shared/forms/stock-photo-editor.component.html

@ -1,8 +1,16 @@
<div class="input-group">
<button class="btn btn-outline-secondary" (click)="reset()" [disabled]="!valueControl.value" type="button">
<button
class="btn btn-outline-secondary"
attr.aria-label="{{ 'common.clear' | sqxTranslate }}"
(click)="reset()"
[disabled]="!valueControl.value"
type="button">
<i class="icon-close"></i>
</button>
<button class="btn btn-outline-secondary" (click)="searchDialog.show()" type="button"><i class="icon-search"></i></button>
<button class="btn btn-outline-secondary" attr.aria-label="{{ 'common.search' | sqxTranslate }}" (click)="searchDialog.show()" type="button">
<i class="icon-search"></i>
</button>
<input class="form-control" [disabled]="true" [formControl]="valueControl" readonly />
</div>

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

@ -7,10 +7,17 @@
@if (isDirty) {
<div class="edit-menu">
<button class="btn btn-text-secondary btn-cancel me-2" (click)="cancel()" sqxStopClick type="button">
<button
class="btn btn-text-secondary btn-cancel me-2"
attr.aria-label="{{ 'common.cancel' | sqxTranslate }}"
(click)="cancel()"
sqxStopClick
type="button">
<i class="icon-close"></i>
</button>
<button class="btn btn-success" (click)="save()" sqxStopClick type="button"><i class="icon-checkmark"></i></button>
<button class="btn btn-success" attr.aria-label="{{ 'common.save' | sqxTranslate }}" (click)="save()" sqxStopClick type="button">
<i class="icon-checkmark"></i>
</button>
</div>
}
</td>
@ -20,7 +27,7 @@
<i class="icon-dots"></i>
</button>
<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) {
<button class="dropdown-item" (click)="statusChange.emit(info.status)" type="button">
{{ "common.statusChangeTo" | sqxTranslate }}

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

@ -44,12 +44,18 @@
<i class="icon-dots"></i>
</button>
<div class="reference-menu">
<button class="btn btn-text-secondary" (click)="clone.emit()" title="{{ 'common.clone' | sqxTranslate }}" type="button">
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'common.clone' | sqxTranslate }}"
(click)="clone.emit()"
title="{{ 'common.clone' | sqxTranslate }}"
type="button">
<i class="icon-copy"></i>
</button>
@if (canRemove) {
<button
class="btn btn-text-secondary"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmRememberKey="removeReference"
confirmText="i18n:contents.removeConfirmText"
confirmTitle="i18n:contents.removeConfirmTitle"
@ -58,7 +64,11 @@
<i class="icon-close"></i>
</button>
}
<a class="btn btn-text-secondary" [routerLink]="['../..', content.schemaName, content.id]" target="_blank">
<a
class="btn btn-text-secondary"
attr.aria-label="{{ 'common.edit' | sqxTranslate }}"
[routerLink]="['../..', content.schemaName, content.id]"
target="_blank">
<i class="icon-pencil"></i>
</a>
</div>

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

@ -39,7 +39,7 @@
</button>
</sqx-dropdown-menu>
<sqx-modal-dialog (dialogClose)="expertDialog.hide()" fullHeight="true" size="lg" *sqxModal="expertDialog">
<ng-container title> {{ "dashboard.editConfig" | sqxTranslate }} </ng-container>
<ng-container title> {{ "dashboard.editConfig" | sqxTranslate }}</ng-container>
<ng-container content>
<div class="json-editor"><sqx-code-editor borderless="true" [(ngModel)]="expertConfig" valueMode="Json" /></div>
</ng-container>

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

@ -14,6 +14,7 @@
@if (isManual) {
<button
class="btn btn-outline-secondary btn-run ms-2"
attr.aria-label="{{ 'rules.trigger' | sqxTranslate }}"
confirmRememberKey="triggerRule"
confirmText="i18n:rules.triggerConfirmText"
confirmTitle="i18n:rules.triggerConfirmTitle"
@ -31,9 +32,9 @@
<div>
<div class="btn btn-outline-secondary btn-enabled">
@if (rule.isEnabled) {
<span class="me-2"> {{ "common.enabled" | sqxTranslate }} </span>
<span class="me-2"> {{ "common.enabled" | sqxTranslate }}</span>
} @else {
<span class="me-2"> {{ "common.disabled" | sqxTranslate }} </span>
<span class="me-2"> {{ "common.disabled" | sqxTranslate }}</span>
}
<sqx-toggle [ngModel]="rule.isEnabled" (ngModelChange)="changeEnabled($event)" />

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

@ -117,6 +117,7 @@
@if (rule.canTrigger) {
<button
class="btn btn-outline-secondary btn-run"
attr.aria-label="{{ 'rules.trigger' | sqxTranslate }}"
confirmRememberKey="triggerRule"
confirmText="i18n:rules.triggerConfirmText"
confirmTitle="i18n:rules.triggerConfirmTitle"
@ -150,7 +151,7 @@
@if (rule.canReadLogs) {
<div class="col-auto">
<a [queryParams]="{ ruleId: rule.id }" routerLink="events"> {{ "common.logs" | sqxTranslate }} </a>
<a [queryParams]="{ ruleId: rule.id }" routerLink="events"> {{ "common.logs" | sqxTranslate }}</a>
<a class="ms-2" [queryParams]="{ ruleId: rule.id }" routerLink="simulator">
{{ "rules.simulator" | sqxTranslate }}
</a>

2
frontend/src/app/features/rules/shared/actions/branches-input.component.html

@ -12,7 +12,7 @@
(click)="control.removeAt(i)"
[disabled]="!isEditable"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

3
frontend/src/app/features/rules/shared/rule-element.component.html

@ -10,12 +10,13 @@
@if (showRemove) {
<button
class="btn btn-sm rounded-full btn-outline-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmText="i18n:rules.removeElementText"
confirmTitle="i18n:rules.removeElementTitle"
(sqxConfirmClick)="remove.emit()"
sqxStopClick=""
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
}
</div>

2
frontend/src/app/features/rules/shared/triggers/asset-changed-trigger.component.html

@ -4,7 +4,7 @@
<div class="help">
<h4>{{ "common.conditions" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }}</sqx-form-hint>
<ul class="help-examples">
<li class="help-example">
{{ "rules.conditions.event" | sqxTranslate }}: <br />

2
frontend/src/app/features/rules/shared/triggers/comment-trigger.component.html

@ -4,7 +4,7 @@
<div class="help">
<h4>{{ "common.conditions" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }}</sqx-form-hint>
<ul class="help-examples">
<li class="help-example">
{{ "rules.conditions.commentUser" | sqxTranslate }}: <br />

3
frontend/src/app/features/rules/shared/triggers/content-changed-schema.component.html

@ -20,13 +20,14 @@
<div class="col-auto">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmRememberKey="deleteContentChangedSchema"
confirmText="i18n:rules.deleteContentChangedSchemaText"
confirmTitle="i18n:rules.deleteContentChangedSchemaTitle"
[disabled]="form.disabled"
(sqxConfirmClick)="remove.emit()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

20
frontend/src/app/features/rules/shared/triggers/content-changed-trigger.component.html

@ -1,7 +1,7 @@
<ng-container>
<div class="section">
<h4>{{ "common.schemas" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.schemas.hint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.schemas.hint" | sqxTranslate }}</sqx-form-hint>
@if (!triggerForm.form.controls.handleAll.value) {
<div>
@for (form of schemasForm.controls; track form; let i = $index) {
@ -22,7 +22,9 @@
</div>
<div class="col-auto">
<button class="btn btn-success" (click)="addSchema()" type="button"><i class="icon-add"></i></button>
<button class="btn btn-success" attr.aria-label="{{ 'rules.schemas.addSchema' | sqxTranslate }}" (click)="addSchema()" type="button">
<i class="icon-add"></i>
</button>
</div>
</div>
</div>
@ -31,14 +33,14 @@
<div class="form-group" [formGroup]="triggerForm.form">
<div class="form-check">
<input class="form-check-input" id="handleAll" formControlName="handleAll" type="checkbox" />
<label class="form-check-label" for="handleAll"> {{ "rules.triggerAll" | sqxTranslate }} </label>
<label class="form-check-label" for="handleAll"> {{ "rules.triggerAll" | sqxTranslate }}</label>
</div>
</div>
</div>
<div class="section">
<h4>{{ "rules.referencedSchemas" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.referencedSchemasHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.referencedSchemasHint" | sqxTranslate }}</sqx-form-hint>
@for (form of referencedSchemasForm.controls; track form; let i = $index) {
<div class="mb-2">
<sqx-content-changed-schema [form]="$any(form)" (remove)="removeReferencedSchema(i)" [schemas]="schemas" />
@ -59,14 +61,20 @@
</div>
<div class="col-auto">
<button class="btn btn-success" (click)="addReferencedSchema()" type="button"><i class="icon-add"></i></button>
<button
class="btn btn-success"
attr.aria-label="{{ 'rules.addReferencedSchema' | sqxTranslate }}"
(click)="addReferencedSchema()"
type="button">
<i class="icon-add"></i>
</button>
</div>
</div>
</div>
<div class="help">
<h4>{{ "common.conditions" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }}</sqx-form-hint>
<ul class="help-examples">
<li class="help-example">
{{ "rules.conditions.event" | sqxTranslate }}: <br />

2
frontend/src/app/features/rules/shared/triggers/cron-job-trigger.component.html

@ -14,7 +14,7 @@
<div class="help">
<h4>{{ "rules.conditions.cronExpressionsTitle" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.conditions.cronExpressionsHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.conditions.cronExpressionsHint" | sqxTranslate }}</sqx-form-hint>
<ul class="help-examples">
<li class="help-example">
{{ "rules.conditions.cronExpressionEvery4Hours" | sqxTranslate }}: <br />

2
frontend/src/app/features/rules/shared/triggers/schema-changed-trigger.component.html

@ -4,7 +4,7 @@
<div class="help">
<h4>{{ "common.conditions" | sqxTranslate }}</h4>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "rules.conditionHint2" | sqxTranslate }}</sqx-form-hint>
<ul class="help-examples">
<li class="help-example">
{{ "rules.conditions.event" | sqxTranslate }}: <br />

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

@ -32,7 +32,7 @@
</sqx-form-row>
<sqx-form-row alert="schemas.searchFieldsHelp" for="searchFields" hint="schemas.searchFieldsHint" label="schemas.searchFields" vertical>
<sqx-tag-editor allowOpen="true" id="searchFields" formControlName="searchFields" [itemsSource]="fieldNames" />
<sqx-tag-editor id="searchFields" allowOpen="true" formControlName="searchFields" [itemsSource]="fieldNames" />
</sqx-form-row>
<sqx-form-row for="tags" hint="schemas.schemaTagsHint" label="common.tags" vertical>

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

@ -7,14 +7,14 @@
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" id="fieldsDelete" formControlName="fieldsDelete" type="checkbox" />
<label class="form-check-label" for="fieldsDelete"> {{ "schemas.export.deleteFields" | sqxTranslate }} </label>
<label class="form-check-label" for="fieldsDelete"> {{ "schemas.export.deleteFields" | sqxTranslate }}</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" id="fieldsRecreate" formControlName="fieldsRecreate" type="checkbox" />
<label class="form-check-label" for="fieldsRecreate"> {{ "schemas.export.recreateFields" | sqxTranslate }} </label>
<label class="form-check-label" for="fieldsRecreate"> {{ "schemas.export.recreateFields" | sqxTranslate }}</label>
</div>
</div>

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

@ -63,7 +63,7 @@
</sqx-form-row>
}
<sqx-form-alert class="mt-4"> {{ "schemas.nameWarning" | sqxTranslate }} </sqx-form-alert>
<sqx-form-alert class="mt-4"> {{ "schemas.nameWarning" | sqxTranslate }}</sqx-form-alert>
</form>
}
</ng-container>

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

@ -25,15 +25,15 @@
<div class="col-4 flex-nowrap">
<div class="float-end">
@if (field.isLocked) {
<span class="ms-1 badge rounded-pill badge-danger"> {{ "schemas.field.lockedMarker" | sqxTranslate }} </span>
<span class="ms-1 badge rounded-pill badge-danger"> {{ "schemas.field.lockedMarker" | sqxTranslate }}</span>
}
@if (!field.isDisabled) {
<span class="ms-1 badge rounded-pill badge-success"> {{ "schemas.field.enabledMarker" | sqxTranslate }} </span>
<span class="ms-1 badge rounded-pill badge-success"> {{ "schemas.field.enabledMarker" | sqxTranslate }}</span>
}
@if (field.isDisabled) {
<span class="ms-1 badge rounded-pill badge-danger"> {{ "schemas.field.disabledMarker" | sqxTranslate }} </span>
<span class="ms-1 badge rounded-pill badge-danger"> {{ "schemas.field.disabledMarker" | sqxTranslate }}</span>
}
</div>
</div>

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

@ -1,6 +1,6 @@
<form (ngSubmit)="createSchema()">
<sqx-modal-dialog (dialogClose)="dialogClose.emit()" size="md">
<ng-container title> {{ "schemas.indexes.addTitle" | sqxTranslate }} </ng-container>
<ng-container title> {{ "schemas.indexes.addTitle" | sqxTranslate }}</ng-container>
<ng-container content>
<sqx-form-hint>
<span [sqxMarkdown]="'schemas.indexes.hint' | sqxTranslate" trusted="true"></span>
@ -34,7 +34,7 @@
confirmTitle="i18n:schemas.indexes.deleteFieldConfirmTitle"
(sqxConfirmClick)="createForm.form.removeAt(i)"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

2
frontend/src/app/features/schemas/pages/schema/indexes/index.component.html

@ -23,7 +23,7 @@
[disabled]="!index.canDelete"
(sqxConfirmClick)="delete()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

13
frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.html

@ -3,7 +3,7 @@
<div class="card mb-4">
<div class="card-body">
<sqx-form-alert> {{ "schemas.previewUrls.help" | sqxTranslate }} </sqx-form-alert>
<sqx-form-alert> {{ "schemas.previewUrls.help" | sqxTranslate }}</sqx-form-alert>
<div class="content">
@if (!isEditable && editForm.previewControls.length === 0) {
<div class="mt-4">{{ "schemas.previewUrls.empty" | sqxTranslate }}</div>
@ -28,13 +28,14 @@
<div class="col-auto">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmRememberKey="removePreviewUrl"
confirmText="i18n:schemas.deleteUrlConfirmText"
confirmTitle="i18n:schemas.deleteUrlConfirmTitle"
[disabled]="!isEditable"
(sqxConfirmClick)="editForm.form.removeAt(i)"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -51,7 +52,13 @@
</div>
<div class="col-auto col-options">
<button class="btn btn-success" (click)="editForm.form.add()" type="button"><i class="icon-add"></i></button>
<button
class="btn btn-success"
attr.aria-label="{{ 'schemas.previewUrls.add' | sqxTranslate }}"
(click)="editForm.form.add()"
type="button">
<i class="icon-add"></i>
</button>
</div>
</div>
}

7
frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.html

@ -40,13 +40,14 @@
<div class="col-auto">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmRememberKey="deleteFieldRule"
confirmText="i18n:schemas.deleteRuleConfirmText"
confirmTitle="i18n:schemas.deleteRuleConfirmTitle"
[disabled]="!isEditable"
(sqxConfirmClick)="editForm.remove(i)"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -71,7 +72,9 @@
</div>
<div class="col-auto">
<button class="btn btn-success" (click)="add()" type="button"><i class="icon-add"></i></button>
<button class="btn btn-success" attr.aria-label="{{ 'schemas.rules.add' | sqxTranslate }}" (click)="add()" type="button">
<i class="icon-add"></i>
</button>
</div>
</div>
}

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

@ -4,14 +4,14 @@
<ng-container title>
<ul class="nav nav-tabs2">
<li class="nav-item">
<a class="nav-link" [class.active]="tab === 'fields'" [queryParams]="{ tab: 'fields' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'fields'" [queryParams]="{ tab: 'fields' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabFields" | sqxTranslate }}
</a>
</li>
@if (schema.type !== "Component") {
<li>
<a class="nav-link" [class.active]="tab === 'ui'" [queryParams]="{ tab: 'ui' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'ui'" [queryParams]="{ tab: 'ui' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabUI" | sqxTranslate }}
</a>
</li>
@ -19,28 +19,28 @@
@if (schema.type !== "Component") {
<li>
<a class="nav-link" [class.active]="tab === 'scripts'" [queryParams]="{ tab: 'scripts' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'scripts'" [queryParams]="{ tab: 'scripts' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabScripts" | sqxTranslate }}
</a>
</li>
}
<li>
<a class="nav-link" [class.active]="tab === 'json'" [queryParams]="{ tab: 'json' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'json'" [queryParams]="{ tab: 'json' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabJson" | sqxTranslate }}
</a>
</li>
@if (!hideIndexes) {
<li>
<a class="nav-link" [class.active]="tab === 'indexes'" [queryParams]="{ tab: 'indexes' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'indexes'" [queryParams]="{ tab: 'indexes' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabIndexes" | sqxTranslate }}
</a>
</li>
}
<li>
<a class="nav-link" [class.active]="tab === 'more'" [queryParams]="{ tab: 'more' }" [routerLink]="[]">
<a class="nav-link" [class.active]="tab === 'more'" [queryParams]="{ tab: 'more' }" queryParamsHandling="merge" [routerLink]="[]">
{{ "schemas.tabMore" | sqxTranslate }}
</a>
</li>

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

@ -3,13 +3,13 @@
<ul class="nav nav-tabs2">
<li class="nav-item">
<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>
</li>
<li class="nav-item">
<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>
</li>
</ul>

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

@ -43,7 +43,7 @@
<input class="form-control" id="name" autocomplete="off" formControlName="name" sqxFocusOnInit sqxTransformInput="LowerCase" />
</sqx-form-row>
<sqx-form-alert> {{ "schemas.nameWarning" | sqxTranslate }} </sqx-form-alert>
<sqx-form-alert> {{ "schemas.nameWarning" | sqxTranslate }}</sqx-form-alert>
}
@if (selectedTab === 0) {
@ -127,7 +127,7 @@
<sqx-form-row for="prompt" label="common.prompt">
<div class="row g-2 form-group">
<div class="col">
<sqx-form-hint> {{ "schemas.promptHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "schemas.promptHint" | sqxTranslate }}</sqx-form-hint>
</div>
<div class="col-auto">
<label>&nbsp;</label>
@ -144,7 +144,7 @@
</div>
</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 }" />
}

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

@ -25,5 +25,5 @@
</div>
</div>
</form>
<sqx-form-hint> {{ "clients.add.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.add.description" | sqxTranslate }}</sqx-form-hint>
</div>

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

@ -1,5 +1,5 @@
<sqx-modal-dialog (dialogClose)="dialogClose.emit()" size="lg">
<ng-container title> {{ "clients.connect" | sqxTranslate }} </ng-container>
<ng-container title> {{ "clients.connect" | sqxTranslate }}</ng-container>
<ng-container content>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-4 steps">
@ -7,7 +7,7 @@
@if (step === "Start") {
<li class="breadcrumb-item active">
<span> <i class="icon-checkmark"></i> {{ "clients.connectWizard.step1Title" | sqxTranslate }} </span>
<span> <i class="icon-checkmark"></i> {{ "clients.connectWizard.step1Title" | sqxTranslate }}</span>
</li>
} @else {
<li class="breadcrumb-item done">
@ -32,19 +32,19 @@
<div class="section">
<div class="option" (click)="go('HTTP')">
<h5>{{ "clients.connectWizard.manually" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "clients.connectWizard.manuallyHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.manuallyHint" | sqxTranslate }}</sqx-form-hint>
<i class="icon-angle-right"></i>
</div>
<div class="option" (click)="go('CLI')">
<h5>{{ "clients.connectWizard.cli" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "clients.connectWizard.cliHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.cliHint" | sqxTranslate }}</sqx-form-hint>
<i class="icon-angle-right"></i>
</div>
<div class="option" (click)="go('SDK')">
<h5>{{ "clients.connectWizard.sdk" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "clients.connectWizard.sdkHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.sdkHint" | sqxTranslate }}</sqx-form-hint>
<i class="icon-angle-right"></i>
</div>
</div>
@ -86,7 +86,7 @@
<p><sqx-code>Authorization: Bearer [YOUR_TOKEN]</sqx-code></p>
</div>
<sqx-form-hint> {{ "clients.connectWizard.manuallyTokenHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.manuallyTokenHint" | sqxTranslate }}</sqx-form-hint>
}
@case ("CLI") {
@ -94,7 +94,7 @@
<h5><span class="badge rounded-pill bg-dark">1</span> {{ "clients.connectWizard.cliStep1" | sqxTranslate }}</h5>
<div [sqxMarkdown]="'clients.connectWizard.cliStep1Download' | sqxTranslate" trusted="true"></div>
<sqx-form-hint> {{ "clients.connectWizard.cliStep1Hint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.cliStep1Hint" | sqxTranslate }}</sqx-form-hint>
</div>
<div class="section step">
@ -109,7 +109,7 @@
<p>
<sqx-code> sq config add {{ appName }} {{ appName }}:{{ client.id }} {{ client.secret }} -u {{ apiUrl.value }} --use </sqx-code>
<sqx-form-hint> {{ "clients.connectWizard.cliStep3Hint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "clients.connectWizard.cliStep3Hint" | sqxTranslate }}</sqx-form-hint>
</p>
</div>

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

@ -14,13 +14,14 @@
<div class="col-auto">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="revokeClient"
confirmText="i18n:clients.deleteConfirmText"
confirmTitle="i18n:clients.deleteConfirmTitle"
[disabled]="!client.canRevoke"
(sqxConfirmClick)="revoke()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

3
frontend/src/app/features/settings/pages/contributors/contributor.component.html

@ -16,13 +16,14 @@
<td class="cell-actions">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="removeContributor"
confirmText="i18n:contributors.deleteConfirmText"
confirmTitle="i18n:contributors.deleteConfirmTitle"
[disabled]="!contributor.canRevoke"
(sqxConfirmClick)="remove()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</td>
</tr>

3
frontend/src/app/features/settings/pages/contributors/contributor.component.ts

@ -10,7 +10,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AssignContributorDto, ConfirmClickDirective, ContributorDto, ContributorsState, HighlightPipe, RoleDto, TooltipDirective, UserPicturePipe } from '@app/shared';
import { AssignContributorDto, ConfirmClickDirective, ContributorDto, ContributorsState, HighlightPipe, RoleDto, TooltipDirective, TranslatePipe, UserPicturePipe } from '@app/shared';
@Component({
selector: '[sqxContributor][roles]',
@ -22,6 +22,7 @@ import { AssignContributorDto, ConfirmClickDirective, ContributorDto, Contributo
FormsModule,
HighlightPipe,
TooltipDirective,
TranslatePipe,
UserPicturePipe,
],
})

2
frontend/src/app/features/settings/pages/contributors/import-contributors-dialog.component.html

@ -1,6 +1,6 @@
<form [formGroup]="importForm.form" (ngSubmit)="detect()">
<sqx-modal-dialog (dialogClose)="dialogClose.emit()">
<ng-container title> {{ "contributors.importTitle" | sqxTranslate }} </ng-container>
<ng-container title> {{ "contributors.importTitle" | sqxTranslate }}</ng-container>
<ng-container content>
@switch (importStage) {
@case ("Start") {

4
frontend/src/app/features/settings/pages/jobs/job.component.html

@ -25,6 +25,7 @@
<div class="col-options text-right">
<a
class="btn btn-text-secondary"
attr.aria-label="{{ 'common.download' | sqxTranslate }}"
[class.invisible]="!job.downloadUrl"
href="{{ apiUrl.buildUrl(job.downloadUrl || '') }}"
sqxExternalLink="noicon">
@ -35,13 +36,14 @@
</button>
<button
class="btn btn-text-danger ms-1"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="deleteBackup"
confirmText="i18n:jobs.deleteConfirmText"
confirmTitle="i18n:jobs.deleteConfirmTitle"
[disabled]="!job.canDelete"
(sqxConfirmClick)="delete()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

2
frontend/src/app/features/settings/pages/languages/language-add-form.component.html

@ -20,5 +20,5 @@
</div>
</div>
</form>
<sqx-form-hint> {{ "languages.add.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "languages.add.description" | sqxTranslate }}</sqx-form-hint>
</div>

8
frontend/src/app/features/settings/pages/languages/language.component.html

@ -29,7 +29,7 @@
[disabled]="!language.canDelete"
(sqxConfirmClick)="remove()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -72,7 +72,11 @@
@if (isEditable) {
<div class="col-auto">
<button class="btn btn-text-secondary btn-sm" (click)="removeFallbackLanguage(language)" type="button">
<button
class="btn btn-text-secondary btn-sm"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
(click)="removeFallbackLanguage(language)"
type="button">
<i class="icon-close"></i>
</button>
</div>

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

@ -50,10 +50,11 @@
@if (isEditableImage && app.image) {
<button
class="btn btn-danger btn-sm app-image-remove"
attr.aria-label="{{ 'apps.removeImage' | sqxTranslate }}"
(click)="removeImage()"
title="i18n:apps.removeImage"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
}
</div>
@ -68,7 +69,7 @@
</div>
<div class="col align-self-center">
<sqx-form-hint> {{ "apps.uploadImage" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "apps.uploadImage" | sqxTranslate }}</sqx-form-hint>
<span class="btn btn-success upload-button" [class.disabled]="!isEditableImage" (click)="fileInput.click()">
<span>{{ "apps.uploadImageButton" | sqxTranslate }}</span>
<input
@ -90,7 +91,7 @@
<div class="row pt-2 g-2">
<div class="col-6">
<h5>{{ "apps.transferTitle" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "apps.transferWarning" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "apps.transferWarning" | sqxTranslate }}</sqx-form-hint>
</div>
<div class="col">
@ -116,7 +117,7 @@
<div class="row">
<div class="col">
<h5>{{ "apps.delete" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "apps.deleteWarning" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "apps.deleteWarning" | sqxTranslate }}</sqx-form-hint>
</div>
<div class="col-auto">

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

@ -3,7 +3,7 @@
<h4 class="card-title">{{ planInfo.plan.name }}</h4>
<h5 class="plan-price">{{ planInfo.plan.costs }}</h5>
<sqx-form-hint> {{ "plans.perMonth" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "plans.perMonth" | sqxTranslate }}</sqx-form-hint>
</div>
<div class="card-body">
@ -44,7 +44,7 @@
<div class="card-footer">
<div class="text-center">
<h5 class="plan-price">{{ planInfo.plan.yearlyCosts }}</h5>
<sqx-form-hint> {{ "plans.perYear" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "plans.perYear" | sqxTranslate }}</sqx-form-hint>
</div>
@if (planInfo.isYearlySelected) {

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

@ -11,7 +11,7 @@
@if ((plansState.isLoaded | async) && (plansState.plans | async); as plans) {
@if ((plansState.locked | async) === "NotOwner") {
<div class="alert alert-danger">
{{ "plans.notPlanOwner" | sqxTranslate }} {{ "plans.planOwner" | sqxTranslate }}:
{{ "plans.notPlanOwner" | sqxTranslate }}{{ "plans.planOwner" | sqxTranslate }}:
<strong className="no-wrap">{{ plansState.planOwner | async | sqxUserName }}</strong>
</div>
}

2
frontend/src/app/features/settings/pages/roles/role-add-form.component.html

@ -24,5 +24,5 @@
</div>
</div>
</form>
<sqx-form-hint> {{ "roles.add.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "roles.add.description" | sqxTranslate }}</sqx-form-hint>
</div>

23
frontend/src/app/features/settings/pages/roles/role.component.html

@ -23,13 +23,14 @@
</button>
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="deleteRole"
confirmText="i18n:roles.deleteConfirmText"
confirmTitle="i18n:roles.deleteConfirmTitle"
[disabled]="!role.canDelete"
(sqxConfirmClick)="delete()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -51,7 +52,7 @@
<div class="table-items-row-details-tab">
<h3>{{ "roles.permissions" | sqxTranslate }}</h3>
<sqx-form-hint marginBottom="3"> {{ "roles.permissionsDescription" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint marginBottom="3"> {{ "roles.permissionsDescription" | sqxTranslate }}</sqx-form-hint>
@if (!role.isDefaultRole) {
@for (control of editForm.controls; track control; let i = $index) {
<div class="row gx-2 mb-1">
@ -62,15 +63,19 @@
@if (isEditable) {
<div class="col-auto">
<button class="btn btn-text-danger" (click)="editForm.form.removeAt(i)" type="button">
<i class="icon-bin2"></i>
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
(click)="editForm.form.removeAt(i)"
type="button">
<i class="icon-bin3"></i>
</button>
</div>
}
</div>
}
} @else {
<sqx-form-alert> {{ descriptions[role.name] | sqxTranslate }} </sqx-form-alert>
<sqx-form-alert> {{ descriptions[role.name] | sqxTranslate }}</sqx-form-alert>
<table class="table table-bordered table-fixed">
<tbody>
@for (control of editForm.controls; track control; let i = $index) {
@ -89,7 +94,11 @@
</div>
<div class="col-auto">
<button class="btn btn-success col-action" (click)="addPermission()" type="button">
<button
class="btn btn-success col-action"
attr.aria-label="{{ 'roles.addPermission' | sqxTranslate }}"
(click)="addPermission()"
type="button">
<i class="icon-plus"></i>
</button>
</div>
@ -99,7 +108,7 @@
@if (!role.isDefaultRole) {
<div class="mt-4">
<h3>{{ "roles.properties" | sqxTranslate }}</h3>
<sqx-form-hint marginBottom="3"> {{ "roles.propertiesDescription" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint marginBottom="3"> {{ "roles.propertiesDescription" | sqxTranslate }}</sqx-form-hint>
<div class="row g-0 rule-section">
@for (property of propertiesSimple; track property.key) {
<div class="form-check col-6">

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

@ -16,7 +16,7 @@
@if (defaultRoles.length > 0) {
<div>
<h5>{{ "roles.defaultRoles" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "roles.defaultRoles.hint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "roles.defaultRoles.hint" | sqxTranslate }}</sqx-form-hint>
@for (role of defaultRoles; track role.name) {
<sqx-role [allPermissions]="allPermissions" [role]="role" [schemas]="(schemasState.schemas | async)!" />
}

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

@ -11,7 +11,7 @@
<ng-container>
<sqx-list-view innerWidth="55rem">
<h5>{{ "appSettings.patterns.title" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "appSettings.patterns.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "appSettings.patterns.description" | sqxTranslate }}</sqx-form-hint>
<div class="card card-body mb-4" data-testid="patterns">
<div class="content">
@if (!isEditable && editForm.patternsControls.length === 0) {
@ -45,7 +45,7 @@
[disabled]="!isEditable"
(sqxConfirmClick)="editForm.patterns.removeAt(i)"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -80,7 +80,7 @@
</div>
<h5 class="mt-2">{{ "appSettings.editors.title" | sqxTranslate }}</h5>
<sqx-form-hint> {{ "appSettings.editors.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "appSettings.editors.description" | sqxTranslate }}</sqx-form-hint>
<div class="card card-body mb-4">
<div class="content" data-testid="pattern">
@if (!isEditable && editForm.editorsControls.length === 0) {
@ -109,7 +109,7 @@
[disabled]="!isEditable"
(sqxConfirmClick)="editForm.editors.removeAt(i)"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>
@ -145,7 +145,7 @@
<div class="card-body" [formGroup]="editForm.form">
<div class="form-check">
<input class="form-check-input" id="hideScheduler" formControlName="hideScheduler" type="checkbox" />
<label class="form-check-label" for="hideScheduler"> {{ "appSettings.hideScheduler" | sqxTranslate }} </label>
<label class="form-check-label" for="hideScheduler"> {{ "appSettings.hideScheduler" | sqxTranslate }}</label>
</div>
</div>
</div>

2
frontend/src/app/features/settings/pages/workflows/workflow-add-form.component.html

@ -24,5 +24,5 @@
</div>
</div>
</form>
<sqx-form-hint> {{ "workflows.add.description" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "workflows.add.description" | sqxTranslate }}</sqx-form-hint>
</div>

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

@ -41,8 +41,13 @@
<div class="col-auto">
@if (!step.isLocked && workflow.steps.length > 2) {
<button class="btn btn-text-danger" (click)="remove()" [disabled]="disabled" type="button">
<i class="icon-bin2"></i>
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
(click)="remove()"
[disabled]="disabled"
type="button">
<i class="icon-bin3"></i>
</button>
}
</div>
@ -70,7 +75,13 @@
</div>
<div class="col ps-2">
<button class="btn btn-outline-secondary" (click)="addTransition(openStep.name)" type="button"><i class="icon-plus"></i></button>
<button
class="btn btn-outline-secondary"
attr.aria-label="{{ 'workflows.addTransition' | sqxTranslate }}"
(click)="addTransition(openStep.name)"
type="button">
<i class="icon-plus"></i>
</button>
</div>
</div>
}
@ -143,7 +154,7 @@
[ngModel]="step.values.validate"
(ngModelChange)="changeValidate($event)"
type="checkbox" />
<label class="form-check-label" for="validate_{{ step.name }}"> {{ "workflows.validate" | sqxTranslate }} </label>
<label class="form-check-label" for="validate_{{ step.name }}"> {{ "workflows.validate" | sqxTranslate }}</label>
</div>
</div>
</div>

4
frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html

@ -39,6 +39,8 @@
</div>
<div class="col col-button ps-2">
<button class="btn btn-text-danger" (click)="remove()" [disabled]="disabled" type="button"><i class="icon-bin2"></i></button>
<button class="btn btn-text-danger" attr.aria-label="{{ 'common.remove' | sqxTranslate }}" (click)="remove()" [disabled]="disabled" type="button">
<i class="icon-bin3"></i>
</button>
</div>
</div>

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

@ -20,13 +20,14 @@
</button>
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.delete' | sqxTranslate }}"
confirmRememberKey="deleteWorkflow"
confirmText="i18n:workflows.deleteConfirmText"
confirmTitle="i18n:workflows.deleteConfirmTitle"
[disabled]="!workflowView.dto.canDelete"
(sqxConfirmClick)="remove()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</div>
</div>

14
frontend/src/app/features/settings/settings-menu.component.html

@ -5,7 +5,7 @@
@if (app.canReadClients) {
<li class="nav-item" sqxTourStep="clients">
<a class="nav-link" routerLink="clients" routerLinkActive="active"> <i class="icon-clients"></i> {{ "common.clients" | sqxTranslate }} </a>
<a class="nav-link" routerLink="clients" routerLinkActive="active"> <i class="icon-clients"></i> {{ "common.clients" | sqxTranslate }}</a>
</li>
}
@ -19,7 +19,7 @@
@if (app.canReadRoles) {
<li class="nav-item" sqxTourStep="roles">
<a class="nav-link" routerLink="roles" routerLinkActive="active"> <i class="icon-roles"></i> {{ "common.roles" | sqxTranslate }} </a>
<a class="nav-link" routerLink="roles" routerLinkActive="active"> <i class="icon-roles"></i> {{ "common.roles" | sqxTranslate }}</a>
</li>
}
@ -41,13 +41,13 @@
@if (app.canReadLanguages) {
<li class="nav-item" sqxTourStep="languages">
<a class="nav-link" routerLink="languages" routerLinkActive="active"> <i class="icon-languages"></i> {{ "common.languages" | sqxTranslate }} </a>
<a class="nav-link" routerLink="languages" routerLinkActive="active"> <i class="icon-languages"></i> {{ "common.languages" | sqxTranslate }}</a>
</li>
}
@if (app.canReadWorkflows) {
<li class="nav-item" sqxTourStep="workflows">
<a class="nav-link" routerLink="workflows" routerLinkActive="active"> <i class="icon-workflows"></i> {{ "common.workflows" | sqxTranslate }} </a>
<a class="nav-link" routerLink="workflows" routerLinkActive="active"> <i class="icon-workflows"></i> {{ "common.workflows" | sqxTranslate }}</a>
</li>
}
@ -55,17 +55,17 @@
@if (app.canReadJobs) {
<li class="nav-item" sqxTourStep="jobs">
<a class="nav-link" routerLink="jobs" routerLinkActive="active"> <i class="icon-backups"></i> {{ "common.jobsBackups" | sqxTranslate }} </a>
<a class="nav-link" routerLink="jobs" routerLinkActive="active"> <i class="icon-backups"></i> {{ "common.jobsBackups" | sqxTranslate }}</a>
</li>
}
@if (app.canReadPlans) {
<li class="nav-item" sqxTourStep="plans">
<a class="nav-link" routerLink="plans" routerLinkActive="active"> <i class="icon-subscription"></i> {{ "common.subscription" | sqxTranslate }} </a>
<a class="nav-link" routerLink="plans" routerLinkActive="active"> <i class="icon-subscription"></i> {{ "common.subscription" | sqxTranslate }}</a>
</li>
}
<li class="nav-item" sqxTourStep="more">
<a class="nav-link" routerLink="more" routerLinkActive="active"> <i class="icon-settings"></i> {{ "common.settings" | sqxTranslate }} </a>
<a class="nav-link" routerLink="more" routerLinkActive="active"> <i class="icon-settings"></i> {{ "common.settings" | sqxTranslate }}</a>
</li>
</ul>

10
frontend/src/app/features/teams/pages/auth/auth-page.component.html

@ -11,7 +11,7 @@
<div class="col">
<h5>{{ "teams.auth.use" | sqxTranslate }}</h5>
<sqx-form-hint marginTop="2"> {{ "teams.auth.useHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint marginTop="2"> {{ "teams.auth.useHint" | sqxTranslate }}</sqx-form-hint>
</div>
</div>
</div>
@ -23,7 +23,7 @@
<sqx-form-row for="domain" label="teams.auth.domain">
<input class="form-control" id="domain" formControlName="domain" />
<sqx-form-hint> {{ "teams.auth.domainHint" | sqxTranslate }} </sqx-form-hint>
<sqx-form-hint> {{ "teams.auth.domainHint" | sqxTranslate }}</sqx-form-hint>
<sqx-form-hint>
{{ "teams.auth.domainHintEmail" | sqxTranslate }}:
<strong>user&commat;{{ updateForm.domainValue$ | async }}</strong>
@ -53,7 +53,11 @@
<sqx-form-row for="redirectUrl" label="teams.auth.redirectUrl">
<div class="input-group">
<input class="form-control" #redirectUri readonly value="{{ urlToRedirect }}" />
<button class="btn btn-outline-secondary" [sqxCopy]="redirectUri" type="button">
<button
class="btn btn-outline-secondary"
attr.aria-label="{{ 'common.copy' | sqxTranslate }}"
[sqxCopy]="redirectUri"
type="button">
<i class="icon-copy"></i>
</button>
</div>

3
frontend/src/app/features/teams/pages/contributors/contributor.component.html

@ -8,13 +8,14 @@
<td class="cell-actions">
<button
class="btn btn-text-danger"
attr.aria-label="{{ 'common.remove' | sqxTranslate }}"
confirmRememberKey="removeContributor"
confirmText="i18n:contributors.deleteConfirmText"
confirmTitle="i18n:contributors.deleteConfirmTitle"
[disabled]="!contributor.canRevoke"
(sqxConfirmClick)="remove()"
type="button">
<i class="icon-bin2"></i>
<i class="icon-bin3"></i>
</button>
</td>
</tr>

3
frontend/src/app/features/teams/pages/contributors/contributor.component.ts

@ -8,7 +8,7 @@
/* eslint-disable @angular-eslint/component-selector */
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { AssignContributorDto, ConfirmClickDirective, ContributorDto, HighlightPipe, TooltipDirective, UserPicturePipe } from '@app/shared';
import { AssignContributorDto, ConfirmClickDirective, ContributorDto, HighlightPipe, TooltipDirective, TranslatePipe, UserPicturePipe } from '@app/shared';
import { TeamContributorsState } from '../../internal';
@Component({
@ -20,6 +20,7 @@ import { TeamContributorsState } from '../../internal';
ConfirmClickDirective,
HighlightPipe,
TooltipDirective,
TranslatePipe,
UserPicturePipe,
],
})

2
frontend/src/app/features/teams/pages/contributors/import-contributors-dialog.component.html

@ -1,6 +1,6 @@
<form [formGroup]="importForm.form" (ngSubmit)="detect()">
<sqx-modal-dialog (dialogClose)="dialogClose.emit()">
<ng-container title> {{ "contributors.importTitle" | sqxTranslate }} </ng-container>
<ng-container title> {{ "contributors.importTitle" | sqxTranslate }}</ng-container>
<ng-container content>
@switch (importStage) {
@case ("Start") {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save