diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index 8aa019d05..66d6463cb 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -483,6 +483,7 @@ "contents.scheduledBy": "by", "contents.scheduledTo": "to", "contents.scheduledToLabel": "Scheduled to", + "contents.scheduledTooltip": "Will be set to '{status}' at {time}.", "contents.schemasPageTitle": "Contents", "contents.searchPlaceholder": "Fulltext search", "contents.searchSchemasPlaceholder": "Search", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index f48a4a2a6..38e3a035e 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -483,6 +483,7 @@ "contents.scheduledBy": "by", "contents.scheduledTo": "a", "contents.scheduledToLabel": "Scheduled to", + "contents.scheduledTooltip": "Will be set to '{status}' at {time}.", "contents.schemasPageTitle": "Contenuti", "contents.searchPlaceholder": "Ricerca testuale", "contents.searchSchemasPlaceholder": "Cerca schemi...", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index bf06dc5ef..fc027ab47 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -483,6 +483,7 @@ "contents.scheduledBy": "door", "contents.scheduledTo": "naar", "contents.scheduledToLabel": "Ingepland tot", + "contents.scheduledTooltip": "Will be set to '{status}' at {time}.", "contents.schemasPageTitle": "Inhoud", "contents.searchPlaceholder": "Zoeken in volledige tekst", "contents.searchSchemasPlaceholder": "Zoek schema's ...", diff --git a/backend/i18n/frontend_zh.json b/backend/i18n/frontend_zh.json index e6eadb903..1a66de1af 100644 --- a/backend/i18n/frontend_zh.json +++ b/backend/i18n/frontend_zh.json @@ -483,6 +483,7 @@ "contents.scheduledBy": "by", "contents.scheduledTo": "to", "contents.scheduledToLabel": "Scheduled to", + "contents.scheduledTooltip": "Will be set to '{status}' at {time}.", "contents.schemasPageTitle": "内容", "contents.searchPlaceholder": "全文搜索", "contents.searchSchemasPlaceholder": "搜索Schemas...", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index 8aa019d05..66d6463cb 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -483,6 +483,7 @@ "contents.scheduledBy": "by", "contents.scheduledTo": "to", "contents.scheduledToLabel": "Scheduled to", + "contents.scheduledTooltip": "Will be set to '{status}' at {time}.", "contents.schemasPageTitle": "Contents", "contents.searchPlaceholder": "Fulltext search", "contents.searchSchemasPlaceholder": "Search", diff --git a/backend/src/Squidex.Shared/Texts.nl.resx b/backend/src/Squidex.Shared/Texts.nl.resx index 0b277ae8a..8f62887c2 100644 --- a/backend/src/Squidex.Shared/Texts.nl.resx +++ b/backend/src/Squidex.Shared/Texts.nl.resx @@ -1,4 +1,4 @@ - + @@ -53,9 +53,1179 @@ 2.0 - System.Resources.NetStandard.ResXResourceReader, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.NetStandard.ResXResourceWriter, System.Resources.NetStandard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Het veld '{name|lower}' moet een absolute URL zijn. + + + Het veld '{0}' moet een absolute URL zijn. + + + Het veld '{name|lower}' moet hetzelfde zijn als {other|lower}. + + + Het veld '{0}' moet hetzelfde zijn als {1}. + + + Het veld '{name|lower}' is geen geldig e-mailadres. + + + Het veld '{0}' is geen geldig e-mailadres. + + + Het veld '{name|lower}' moet tussen {min} en {max} zijn. + + + Het veld '{0}' moet tussen {1} en {2} zijn. + + + Het veld '{name|lower}' is niet. + + + Het veld '{0}' is niet. + + + Het veld '{name|lower}' is verplicht. + + + Het veld '{0}' is verplicht. + + + Het veld '{name|lower}' moet een string zijn met een maximale lengte van {max}. + + + Het veld '{0}' moet een string zijn met een maximale lengte van {1}. + + + Het veld '{name|lower}' moet een string zijn met een minimum lengte van {min} en een maximum lengte van {max}. + + + Het veld '{0}' moet een string zijn met een minimum lengte van {1} en een maximum lengte van {2}. + + + Er bestaat al een client met dezelfde id. + + + Je kunt jouw eigen rol niet wijzigen. + + + Je heeft het maximale aantal bijdragers voor jouw plan bereikt. + + + Kan de enige eigenaar niet verwijderen. + + + App heeft geen terugvaltaal '{fallback}'. + + + Taal is al toegevoegd. + + + Hoofdtaal kan geen terugval talen hebben. + + + Hoofdtaal kan niet optioneel worden gemaakt. + + + Hoofdtaal kan niet worden verwijderd. + + + U kunt niet meer apps maken. Neem contact op met de support om deze beperking van uw account te verwijderen. + + + Er bestaat al een app met dezelfde naam. + + + Bestand is geen afbeelding + + + Een plan met deze id bestaat niet. + + + Plan kan alleen worden gewijzigd van de gebruiker die het plan aanvankelijk heeft geconfigureerd. + + + Kan een standaardrol niet verwijderen. + + + Kan een standaardrol niet bijwerken. + + + Er bestaat al een rol met dezelfde naam. + + + Kan een rol niet verwijderen wanneer een client is toegewezen. + + + Kan een rol niet verwijderen wanneer een bijdrager is toegewezen. + + + Assetmap bestaat niet. + + + Kan map niet toevoegen aan zijn eigen kind. + + + Je hebt jouw maximale assetgrootte bereikt. + + + Er wordt naar dit bestand verwezen door een contentitem en kan niet worden verwijderd. + + + Er wordt al een ander back-upproces uitgevoerd. + + + Je kunt niet meer dan {max} backups hebben. + + + Er wordt al een herstelbewerking uitgevoerd. + + + Je hebt alleen toegang tot jouw notificaties. + + + Reactie is gemaakt door een andere gebruiker. + + + Actie + + + Hoogte aspect + + + Aspect breedte + + + Berekende standaardwaarde + + + Client-ID + + + Client-ID + + + Clientgeheim + + + Inhoudstype + + + Bijdrager-ID of e-mailadres + + + Kritiek + + + Gegevens + + + Standaardwaarde + + + Weergavenaam + + + Documentatie + + + Editor + + + E-mail + + + Je hebt niet de benodigde rechten. + + + Veld + + + Veld-ID's + + + Veldnaam + + + Bestand + + + Mapnaam + + + Query-zoekclausule niet ondersteund. + + + Bestandstype inhoud is niet gedefinieerd. + + + Bestandsnaam is niet gedefinieerd. + + + Het model is niet geldig. + + + Verzoektekst heeft een ongeldige indeling. + + + Niet toegestaan ​​voor klanten. + + + Validatiefout + + + Eerste stap + + + Kan script niet uitvoeren met Javascript-fout: {message} + + + Script heeft de bewerking verboden. + + + Kan script niet uitvoeren met Javascript-syntaxisfout: {message} + + + Script heeft de bewerking afgewezen. + + + Taalcode + + + Inloggen + + + Uitloggen + + + Max. karakters + + + Max. hoogte + + + Max. aantal items + + + Max. lengte + + + Max. grootte + + + Max. waarde + + + Max. breedte + + + Max. woorden + + + Min. karakters + + + Min. hoogte + + + Min items + + + Min. lengte + + + Min. grootte + + + Min. waarde + + + Min. breedte + + + Min. woorden + + + Naam + + + - niet gevonden - + + + Aantal dagen + + + Ontleden zoekopdracht: {message} + + + OData $ filterclausule niet geldig: {message} + + + OData-bewerking wordt niet ondersteund. + + + OData $ zoekclausule niet geldig: {message} + + + Oud wachtwoord + + + Anders + + + Partitioneren + + + Wachtwoord + + + Bevestigen + + + Patroon + + + Rechten + + + Plan-ID + + + Voorbeeld-URL's + + + Squidex Headless CMS + + + Eigenschappen + + + Eigenschap + + + Applicatie is momenteel in de alleen-lezen modus. + + + Verwijderen + + + Resultaatset is te groot om opgehaald te worden. Gebruik $ take parameter om het aantal items te verminderen. + + + Rol + + + Opslaan + + + Schema-ID + + + Aanmelden + + + Succes + + + Tekst + + + Trigger + + + Waarschuwing + + + Workflow + + + Stap + + + Overgang + + + Meer dan één inhoud komt overeen met de zoekopdracht. + + + Je kunt alleen een nieuwe versie maken wanneer de inhoud is gepubliceerd. + + + Er is niets te verwijderen. + + + Ofwel Ids of Schedule bereik moeten worden gedefinieerd. + + + Ongeldig json-type, verwachte reeks objecten. + + + Ongeldig json-type, verwachte reeks strings. + + + Ongeldig json-type, verwachte boolean. + + + Ongelid json object, verwacht object met een 'schemaId' veld + + + Ongeldig component. Het veld 'schemaId' is niet gevonden. + + + Ongeldig component. Kan schema niet vinden. + + + Ongeldig json-type, verwacht object voor lengte- / breedtegraad. + + + Breedtegraad moet tussen -90 en 90 zijn. + + + Lengtegraad moet tussen -180 en 180 liggen. + + + Ongeldig json-type, verwacht aantal. + + + Ongeldig json-type, verwachte string. + + + {count} referentie (s) + + + Er wordt naar de inhoud verwezen door een andere item en kan niet worden verwijderd. + + + Schema is niet gepubliceerd. + + + Singleton-inhoud kan niet worden bijgewerkt. + + + Singleton-inhoud kan niet worden gemaakt. + + + Singleton-inhoud kan niet worden verwijderd. + + + Status is niet gedefineerd in de werkstroom. + + + Kan status niet wijzigen van {oldStatus} in {newStatus}. + + + Moet aspectverhouding {breedte}: {hoogte} hebben. + + + Id {id} niet gevonden. + + + Niet van het verwachte type{type}. + + + Moet tussen {min} en {max} liggen. + + + Moet exact {count} teken (s) bevatten. + + + Moet tussen {min} en {max} teken (s) bevatten. + + + Mag geen dubbele waarden bevatten. + + + Validatie mislukt met interne fout. + + + Moet exact {waarde} zijn. + + + Moet een toegestane extensie zijn. + + + Geen geldige waarde. + + + Moet exact {count} item (s) bevatten. + + + Moet tussen {min} en {max} item (s) bevatten. + + + Moet kleiner zijn dan of gelijk zijn aan {max}. + + + Mag niet meer dan {max} tekstteken (s) bevatten. + + + Hoogte {hoogte} px moet kleiner zijn dan {max} px. + + + Grootte van {size} moet kleiner zijn dan {max}. + + + Breedte {breedte} px moet kleiner zijn dan {max} px. + + + Mag niet meer dan {max} item (s) bevatten. + + + Mag niet meer dan {max} teken (s) bevatten. + + + Mag niet meer dan {max} woord(en) bevatten. + + + Moet groter zijn dan of gelijk zijn aan {min}. + + + Hoogte {hoogte} px moet groter zijn dan {min} px. + + + Grootte van {size} moet groter zijn dan {min}. + + + Breedte {breedte} px moet groter zijn dan {min} px. + + + Moet minstens {min} item (s) bevatten. + + + Moet minstens {min} teken (s) bevatten. + + + Moet minstens {min} tekstteken (s) bevatten. + + + Moet minstens {min} woord(en) bevatten. + + + Waarde mag niet worden gedefinieerd. + + + Moet exact {count} tekstteken (s) bevatten. + + + Moet tussen {min} en {max} tekstteken (s) bevatten. + + + Geen toegestane waarde. + + + Moet het patroon volgen. + + + Bevat ongeldige referentie '{id}'. + + + Bevat verwijzing '{id}' naar ongeldig schema. + + + Regex is te traag. + + + Veld is verplicht. + + + Er bestaat een andere inhoud met dezelfde waarde. + + + Mag geen items bevatten met dubbele '{field}' velden. + + + Onbekend {fieldType}. + + + Moet exact {count} woord (en) bevatten. + + + Moet tussen {min} en {max} woord (en) bevatten. + + + De werkstroom staat geen updates toe met status {status} + + + Er is een onbekende fout opgetreden. + + + E-mail is al in gebruik. + + + Gebruikersnaam is al in gebruik. + + + E-mail is ongeldig. + + + Gebruikersnaam '{0}' is ongeldig, mag alleen letters of cijfers bevatten. + + + Er bestaat al een gebruiker met deze login. + + + Onjuist wachtwoord. + + + Wachtwoorden moeten minstens één cijfer bevatten ('0' - '9'). + + + Wachtwoorden moeten minstens één kleine letter ('a' - 'z') bevatten. + + + Wachtwoorden moeten minstens één niet-alfanumeriek teken bevatten. + + + Wachtwoorden moeten minstens {0} verschillende tekens bevatten. + + + Wachtwoorden moeten minstens één hoofdletter ('A' - 'Z') hebben. + + + Wachtwoorden zijn te kort. + + + Dit wachtwoord is eerder verschenen in een datalek en mag nooit worden gebruikt. Als je het ooit eerder ergens hebt gebruikt, verander het dan! + + + Gebruiker is uitgesloten. + + + Json-query niet geldig: {message} + + + Json-query is niet geldig json: {message} + + + Entiteit ({id}) bestaat al. + + + Entiteit ({id}) is verwijderd. + + + Entiteit ({id}) bestaat niet. + + + Entiteit ({id}) heeft versie {verwachteVersion} aangevraagd, maar heeft {currentVersion} gevonden. + + + client {[Id]} toegevoegd aan app + + + ingetrokken klant {[Id]} + + + bijgewerkte client {[Id]} + + + heeft {user:[Contributor]} toegewezen als {[Role]} + + + heeft {user:[Contributor]} verwijderd uit app + + + taal toegevoegd {[Language]} + + + verwijderde taal {[Language]} + + + hoofdtaal gewijzigd in {[Language]} + + + bijgewerkte taal {[Language]} + + + plan gewijzigd in {[Plan]} + + + gereset plan + + + toegevoegde rol {[Name]} + + + verwijderde rol {[Name]} + + + bijgewerkte rol {[Name]} + + + Bijgewerkte UI instellingen + + + item vervangen. + + + bijgewerkt item. + + + geüpload item. + + + heeft {[Schema]} inhoud gemaakt. + + + heeft {[Schema]} inhoud verwijderd. + + + nieuw concept gemaakt. + + + concept verwijderd. + + + gepland om de status van {[Schema]} inhoud te wijzigen in {[Status]}. + + + kon statuswijziging voor {[Schema]} - inhoud niet plannen. + + + heeft {[Schema]} inhoud bijgewerkt. + + + heeft schema {[Name]} gemaakt. + + + verwijderde schema {[Name]}. + + + veld {[Field]} toegevoegd aan schema {[Name]}. + + + heeft veld {[Field]} verwijderd uit schema {[Name]}. + + + uitgeschakeld veld {[Field]} van schema {[Name]}. + + + heeft veld {[Field]} van schema {[Name]} verborgen. + + + heeft veld {[Field]} van schema {[Name]} vergrendeld. + + + heeft veld {[Field]} van schema {[Name]} getoond. + + + opnieuw geordende velden van schema {[Name]}. + + + heeft veld {[Field]} van schema {[Name]} bijgewerkt. + + + gepubliceerd schema {[Name]}. + + + geconfigureerd script van schema {[Name]}. + + + niet-gepubliceerd schema {[Name]}. + + + bijgewerkt schema {[Name]}. + + + veranderde status van {[Schema]} inhoud in {[Status]}. + + + Jouw e-mailadres is ingesteld op privé in Github. Stel het in op openbaar om Github-login te gebruiken. + + + Er wordt al een andere regel uitgevoerd. + + + Berekende standaardwaarde en standaardwaarde kunnen niet samen worden gebruikt. + + + Veld '{field}' is twee keer toegevoegd. + + + Veld mag geen UI-veld zijn. + + + Schemaveld is vergrendeld. + + + Er bestaat al een veld met dezelfde naam. + + + Veld maakt geen deel uit van het schema. + + + Veld-id's dekken niet alle velden. + + + Er bestaat al een schema met dezelfde naam. + + + Je hebt geen toestemming voor dit schema. + + + Schema {id} bestaat niet. + + + Inline bewerken is niet toegestaan ​​voor Radio-editor. + + + Alleen matrixvelden kunnen geneste velden bevatten. + + + Genest veld mag geen matrixvelden zijn. + + + Kan alleen verwijzingen omzetten als MaxItems 1 is. + + + Inline bewerken is alleen toegestaan ​​voor dropdowns, slugs en invoervelden. + + + Radioknoppen of vervolgkeuzelijsten hebben toegestane waarden nodig. + + + Selectievakjes of vervolgkeuzelijsten hebben toegestane waarden nodig. + + + UI-veld kan niet worden uitgeschakeld. + + + UI-veld kan niet worden ingeschakeld. + + + UI-veld kan niet worden verborgen. + + + UI-veld kan niet worden weergegeven. + + + {name} Inhoud + + + {name} Inhoud + + + {name} Schema + + + Dit wachtwoord is eerder verschenen bij een datalek en mag nooit worden gebruikt. Als je het ooit eerder hebt gebruikt, verander het dan! + + + Create User + + + Confirm + + + Neither password authentication nor an external authentication provider such as Google is configured. Please check your settings and logs. + + + Admin User + + + Create Admin User + + + You have configured at least one external authentication provider such as Google. Just go to the login page and login to become administrator. + + + Go to Login Page. + + + OR + + + Installation + + + You see this screen because no user exists yet. After a user is created, you are not able to use this setup page again. + + + Proudly made by + + + Sebastian Stehle and Contributors, 2016-2021 + + + With your setup, only admins can create new apps. If you want to change this set <code>UI__ONLYADMINSCANCREATEAPPS=false</code> as environment variable. + + + With your setup, every user can create new apps. If you want to change this set <code>UI__ONLYADMINSCANCREATEAPPS=true</code> as environment variable. + + + You are using the <strong>folder asset store</strong> where all assets are stored in the file system. Please remember to include the asset folder into your backup strategy and map it to a volume, if you are using docker. + + + You are using the <strong>FTP asset store</strong>. It is not recommended to use this storage type because of bad performance. + + + You are not accessing the site over https. If this warning is not correct then Squidex cannot detect https mode, because your instance is behind a reverse proxy such as nginx. Ensure that http headers are forwarded properly, via the <code>X-Forwarded-*</code> headers. + + + Congratulations, you are accessing your Squidex installation over a secure connection (https). + + + System Checklist + + + You should access Squidex only over one canonical URL and configure this URL with the <code>URLS__BASEURL</code> environment variable. The current base URL <code>{actual}</code> does not match to the base url <code>{configured}</code>. This variable must point to the public URL under which your Squidex instance is available. + + + Congratulations, the <code>URLS__BASEURL</code> environment variable is configured properly. This variable must point to the public URL under which your Squidex instance is available. + + + Installation + + + webpack error + + + You have to run webpack dev server to run the frontend in Development mode. More information in the documenation: + + + webpack error + + + Deze bewerking is niet toegestaan, je account is mogelijk vergrendeld. + + + Toegang geweigerd + + + Akkoord! + + + Cookies en analyse + + + <p> Ik begrijp en ga ermee akkoord dat Squidex cookies gebruikt om ervoor te zorgen dat je de beste ervaring op ons platform krijgt en om jouw inlogstatus op te slaan. </p> <p> Ik begrijp en ga ermee akkoord dat Squidex heeft Google Analytics geïntegreerd (met de anonimiseringsfunctie). Google Analytics is een webanalysedienst om gegevens over het gedrag van gebruikers te verzamelen en te analyseren. </p> <p> Ik accepteer de <a href = "{privacyUrl} " target = "_blank " rel = "noopener "> privacybeleid </a>. </p> + + + Geautomatiseerde e-mailberichten (optioneel) + + + Ik begrijp en ga ermee akkoord dat Squidex e-mails verzendt om mij te informeren over nieuwe functies, verbroken veranderingen en downtime. + + + We hebben je toestemming nodig + + + Je moet toestemming geven. + + + Persoonlijke informatie + + + Ik begrijp en ga ermee akkoord dat Squidex de volgende privégegevens verzamelt die worden opgehaald van externe authenticatieleveranciers zoals Google, Microsoft of Github. <ul class = " personal-information "> <li> Persoonlijke basisinformatie (voornaam, achternaam en foto) wordt aan alle andere gebruikers verstrekt, zodat ze je aan hun werkruimte kunnen toevoegen. </li> <li> Je hebt op elk moment de mogelijkheid om deze informatie te wijzigen om jouw account te anonimiseren . </li> <li> Jouw gebruikersaccount heeft een unieke identificatie en voor al jouw wijzigingen volgen we, dat je deze wijzigingen hebt aangebracht en deze informatie aan andere gebruikers verstrekt. </li> </ul> + + + Toestemming + + + Je kan jezelf niet verwijderen. + + + Bewerking mislukt + + + Het spijt ons echt dat er iets mis is gegaan. + + + Fout + + + Er is een onverwachte uitzondering opgetreden. + + + Jouw account is vergrendeld, neem contact op met de beheerder. + + + Account vergrendeld + + + Je kunt jezelf niet vergrendelen. + + + + + + E-mailadres invoeren + + + E-mailadres of wachtwoord niet correct + + + {action} met <strong> {provider} </strong> + + + Klik hier om in te loggen + + + Al geregistreerd? + + + Klik hier om in te schrijven + + + Nog geen account? + + + Voer wachtwoord in + + + OF + + + Uitgelogd! + + + ! Sluit deze pop-up. + + + Uitloggen + + + We cannot get the email address from authentication provider. + + + Login succesvol toegevoegd. + + + Wachtwoord wijzigen + + + Wachtwoord succesvol gewijzigd. + + + Gebruik de clientreferenties om toegang te krijgen tot de API met jouw profielinformatie en machtigingen + + + Client + + + Bevestigen + + + Genereer + + + Clientgeheim succesvol gegenereerd. + + + Profiel bewerken + + + Mijn profiel niet aan andere gebruikers tonen + + + Logins + + + Wachtwoord + + + Persoonlijke informatie + + + Gebruik aangepaste eigenschappen voor regels en scripts. + + + Eigenschappen + + + Eigenschap toevoegen + + + Login provider succesvol verwijderd. + + + Wachtwoord instellen + + + Wachtwoord succesvol ingesteld. + + + Profiel + + + Account succesvol bijgewerkt. + + + Account succesvol bijgewerkt. + + + Afbeelding uploaden + + + Foto succesvol geüpload. + + + Je kunt jezelf niet ontgrendelen. + + + Gebruiker mag niet inloggen. + + + Kan gebruiker niet vinden. + + + {property|upper} moet tussen {min} en {max} liggen. + + + {property|upper} moet groter zijn dan of gelijk zijn aan {other|lower}. + + + {property|upper} moet groter zijn dan {other|lower}. + + + {property|upper} is geen Javascript-eigenschapsnaam. + + + {property|upper} moet kleiner zijn dan of gelijk zijn aan {other|lower}. + + + {property|upper} moet kleiner zijn dan {other|lower}. + + + Afbeelding is geen geldige afbeelding. + + + De SVG is schadelijk en bevat scripttags. + + + Kan slechts één bestand uploaden. + + + {property|upper} is vereist. + + + Als {property1|lower} of {property2|lower} wordt gebruikt, moeten beide worden gedefinieerd. + + + Waarde moet worden gedefinieerd. + + + {property|upper} is geen geldige slug. + + + {property|upper} is geen geldige waarde. + + + Meerdere workflows omvatten alle schema's. + + + Eerste stap kan niet worden gepubliceerd stap. + + + Workflow moet een gepubliceerde stap hebben. + + + Overgang heeft een ongeldig doel. + + + Het schema '{schema}' wordt gedekt door meerdere workflows. + \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fe826d851..8fc6038fa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -44,6 +44,7 @@ "mousetrap": "1.6.5", "ngx-color-picker": "11.0.0", "ngx-doc-viewer": "2.0.5", + "ngx-virtual-scroller": "^4.0.3", "oidc-client": "1.11.5", "pikaday": "1.8.2", "progressbar.js": "1.1.0", @@ -3573,6 +3574,11 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "node_modules/@tweenjs/tween.js": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-17.4.0.tgz", + "integrity": "sha512-J3fzl1F6wvh8KXVVcIuHN12xi1ZDcPA/0Vix+ZcJYwZWVHUwfIqfvzYXXEw7ybeev6477KCTt9fKydU+ajUqcg==" + }, "node_modules/@types/codemirror": { "version": "0.0.108", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz", @@ -3885,6 +3891,11 @@ "@types/jquery": "*" } }, + "node_modules/@types/tween.js": { + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/@types/tween.js/-/tween.js-17.2.0.tgz", + "integrity": "sha512-mOsqurEtFEzwgkVc/jDVE2XrjZBYTbrmDUyCr9GXmnfc6q5otokxFtKvSY/B21zgz9LVRIvRTawKczjKi57wrA==" + }, "node_modules/@types/uglify-js": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", @@ -11592,6 +11603,19 @@ "@angular/platform-browser": ">= 10.0.0" } }, + "node_modules/ngx-virtual-scroller": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ngx-virtual-scroller/-/ngx-virtual-scroller-4.0.3.tgz", + "integrity": "sha512-JBqUJ/f7GRCZDnI/JeiFoTmYR8rC/Hyv8L5I7ImePM6f/hwiFNRsrK8Abdd0E3TwklwgmZAK875te9XQJrgsyQ==", + "dependencies": { + "@tweenjs/tween.js": "17.4.0", + "@types/tween.js": "17.2.0" + }, + "peerDependencies": { + "@angular/core": ">=6.0.0", + "tslib": "^1.10.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -20366,6 +20390,11 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "@tweenjs/tween.js": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-17.4.0.tgz", + "integrity": "sha512-J3fzl1F6wvh8KXVVcIuHN12xi1ZDcPA/0Vix+ZcJYwZWVHUwfIqfvzYXXEw7ybeev6477KCTt9fKydU+ajUqcg==" + }, "@types/codemirror": { "version": "0.0.108", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz", @@ -20658,6 +20687,11 @@ "@types/jquery": "*" } }, + "@types/tween.js": { + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/@types/tween.js/-/tween.js-17.2.0.tgz", + "integrity": "sha512-mOsqurEtFEzwgkVc/jDVE2XrjZBYTbrmDUyCr9GXmnfc6q5otokxFtKvSY/B21zgz9LVRIvRTawKczjKi57wrA==" + }, "@types/uglify-js": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", @@ -26509,6 +26543,15 @@ "tslib": "^2.1.0" } }, + "ngx-virtual-scroller": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ngx-virtual-scroller/-/ngx-virtual-scroller-4.0.3.tgz", + "integrity": "sha512-JBqUJ/f7GRCZDnI/JeiFoTmYR8rC/Hyv8L5I7ImePM6f/hwiFNRsrK8Abdd0E3TwklwgmZAK875te9XQJrgsyQ==", + "requires": { + "@tweenjs/tween.js": "17.4.0", + "@types/tween.js": "17.2.0" + } + }, "nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8d1934aa2..85b4546b6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -49,6 +49,7 @@ "mousetrap": "1.6.5", "ngx-color-picker": "11.0.0", "ngx-doc-viewer": "2.0.5", + "ngx-virtual-scroller": "^4.0.3", "oidc-client": "1.11.5", "pikaday": "1.8.2", "progressbar.js": "1.1.0", diff --git a/frontend/src/app/_theme.html b/frontend/src/app/_theme.html index 92f50143b..4c2ee6fca 100644 --- a/frontend/src/app/_theme.html +++ b/frontend/src/app/_theme.html @@ -1161,24 +1161,24 @@

Badges

- Primary - Secondary - Success - Danger - Warning - Info - Light - Dark + Primary + Secondary + Success + Danger + Warning + Info + Light + Dark
- Primary - Secondary - Success - Danger - Warning - Info - Light + Primary + Secondary + Success + Danger + Warning + Info + Light Dark
diff --git a/frontend/src/app/features/assets/pages/asset-tags.component.html b/frontend/src/app/features/assets/pages/asset-tags.component.html index c0eb26f46..d5a2f5f76 100644 --- a/frontend/src/app/features/assets/pages/asset-tags.component.html +++ b/frontend/src/app/features/assets/pages/asset-tags.component.html @@ -12,7 +12,7 @@ {{tag.name}}
-
{{tag.count}}
+
{{tag.count}}
diff --git a/frontend/src/app/features/content/module.ts b/frontend/src/app/features/content/module.ts index be42655f5..4a776dd58 100644 --- a/frontend/src/app/features/content/module.ts +++ b/frontend/src/app/features/content/module.ts @@ -5,10 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ScrollingModule as ScrollingModuleExperimental } from '@angular/cdk-experimental/scrolling'; -import { ScrollingModule } from '@angular/cdk/scrolling'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { VirtualScrollerModule } from 'ngx-virtual-scroller'; import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, LoadSchemasGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; import { ArrayEditorComponent, ArrayItemComponent, AssetsEditorComponent, CalendarPageComponent, CommentsPageComponent, ComponentComponent, ComponentSectionComponent, ContentComponent, ContentCreatorComponent, ContentEditorComponent, ContentEventComponent, ContentExtensionComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentInspectionComponent, ContentPageComponent, ContentReferencesComponent, ContentSectionComponent, ContentsFiltersPageComponent, ContentsPageComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldCopyButtonComponent, FieldEditorComponent, FieldLanguagesComponent, IFrameEditorComponent, PreviewButtonComponent, ReferenceDropdownComponent, ReferenceItemComponent, ReferencesCheckboxesComponent, ReferencesEditorComponent, ReferencesTagsComponent, SchemasPageComponent, SidebarPageComponent, StockPhotoEditorComponent } from './declarations'; @@ -88,10 +87,9 @@ const routes: Routes = [ @NgModule({ imports: [ RouterModule.forChild(routes), - ScrollingModule, - ScrollingModuleExperimental, SqxFrameworkModule, SqxSharedModule, + VirtualScrollerModule, ], declarations: [ ArrayEditorComponent, diff --git a/frontend/src/app/features/content/pages/content/content-event.component.ts b/frontend/src/app/features/content/pages/content/content-event.component.ts index 6de3b26d6..651e3231c 100644 --- a/frontend/src/app/features/content/pages/content/content-event.component.ts +++ b/frontend/src/app/features/content/pages/content/content-event.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { ContentDto, HistoryEventDto } from '@app/shared'; @Component({ @@ -29,10 +29,12 @@ export class ContentEventComponent implements OnChanges { public canLoadOrCompare = false; - public ngOnChanges() { - this.canLoadOrCompare = - (this.event.eventType === 'ContentUpdatedEvent' || - this.event.eventType === 'ContentCreatedEventV2') && - !this.event.version.eq(this.content.version); + public ngOnChanges(changes: SimpleChanges) { + if (changes['event']) { + this.canLoadOrCompare = + (this.event.eventType === 'ContentUpdatedEvent' || + this.event.eventType === 'ContentCreatedEventV2') && + !this.event.version.eq(this.content.version); + } } } diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts b/frontend/src/app/features/content/pages/content/editor/content-field.component.ts index 50f561051..e20b0a263 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts +++ b/frontend/src/app/features/content/pages/content/editor/content-field.component.ts @@ -48,6 +48,11 @@ export class ContentFieldComponent implements OnChanges { @Input() public languages!: ReadonlyArray; + public showAllControls = false; + + public isDifferent?: Observable; + public isInvalid?: Observable; + @HostBinding('class') public get class() { return this.isHalfWidth ? 'col-6 half-field' : 'col-12'; @@ -61,11 +66,6 @@ export class ContentFieldComponent implements OnChanges { return this.formModel.field.properties.fieldType === 'String' && this.formModel.field.isLocalizable && this.languages.length > 1; } - public showAllControls = false; - - public isDifferent?: Observable; - public isInvalid?: Observable; - constructor( private readonly appsState: AppsState, private readonly localStore: LocalStoreService, diff --git a/frontend/src/app/features/content/pages/content/editor/content-section.component.ts b/frontend/src/app/features/content/pages/content/editor/content-section.component.ts index 7e187ef0f..5195926df 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-section.component.ts +++ b/frontend/src/app/features/content/pages/content/editor/content-section.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { AppLanguageDto, EditContentForm, FieldForm, FieldSection, LocalStoreService, RootFieldDto, SchemaDto, Settings, StatefulComponent } from '@app/shared'; interface State { @@ -64,11 +64,13 @@ export class ContentSectionComponent extends StatefulComponent implements }); } - public ngOnChanges() { - if (this.formSection?.separator && this.schema) { - const isCollapsed = this.localStore.getBoolean(this.expandedKey()); + public ngOnChanges(changes: SimpleChanges) { + if (changes['formSection' || changes['schema']]) { + if (this.formSection?.separator && this.schema) { + const isCollapsed = this.localStore.getBoolean(this.expandedKey()); - this.next({ isCollapsed }); + this.next({ isCollapsed }); + } } } diff --git a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts index c58870333..87a2322fe 100644 --- a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts +++ b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts @@ -9,7 +9,7 @@ import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppsState, getCategoryTree, LocalStoreService, SchemaCategory, SchemasState, Settings, UIOptions, value$ } from '@app/shared'; +import { AppsState, getCategoryTree, SchemaCategory, SchemasState, Settings, UIOptions, value$ } from '@app/shared'; @Component({ selector: 'sqx-schemas-page', @@ -43,27 +43,13 @@ export class SchemasPageComponent { return getCategoryTree(schemas, categories, filter); }); - public isCollapsed = false; - - public get width() { - return this.isCollapsed ? '4rem' : '16rem'; - } - constructor(uiOptions: UIOptions, public readonly schemasState: SchemasState, private readonly appsState: AppsState, - private readonly localStore: LocalStoreService, ) { - this.isCollapsed = localStore.getBoolean(Settings.Local.SCHEMAS_COLLAPSED); this.isEmbedded = uiOptions.get('embedded'); } - public toggle() { - this.isCollapsed = !this.isCollapsed; - - this.localStore.setBoolean(Settings.Local.SCHEMAS_COLLAPSED, this.isCollapsed); - } - public trackByCategory(_index: number, category: SchemaCategory) { return category.name; } diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.html b/frontend/src/app/features/content/shared/forms/array-editor.component.html index 20fcbb6ec..8b3c67b12 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.html +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.html @@ -1,36 +1,40 @@ -
-
-
- - - -
+
+
+ + +
+
- -
+
+ +
- +
diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.scss b/frontend/src/app/features/content/shared/forms/array-editor.component.scss index 7f76cd8e9..d9546e750 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.scss +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.scss @@ -1,9 +1,21 @@ @import 'mixins'; @import 'vars'; -:host ::ng-deep { - .cdk-virtual-scroll-content-wrapper { - right: 0; +/* stylelint-disable no-descending-specificity */ + +virtual-scroller { + height: 700px; +} + +.item { + padding: .25rem 1rem; + + &.first { + padding-top: .75rem; + } + + &.last { + padding-bottom: .75rem; } } @@ -11,17 +23,8 @@ background: $color-border-lighter; margin: 0; margin-bottom: 1rem; - padding: .5rem 0; } .drag-container { position: relative; -} - -cdk-virtual-scroll-viewport { - height: 1000px; -} - -.item { - margin: .25rem 1rem; } \ No newline at end of file diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.ts b/frontend/src/app/features/content/shared/forms/array-editor.component.ts index a5c4679ba..9a5661dad 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.ts @@ -6,8 +6,8 @@ */ import { CdkDragDrop } from '@angular/cdk/drag-drop'; -import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, QueryList, SimpleChanges, ViewChildren } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnChanges, QueryList, SimpleChanges, ViewChildren } from '@angular/core'; +import { VirtualScrollerComponent } from 'ngx-virtual-scroller'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { AppLanguageDto, ComponentsFieldPropertiesDto, disabled$, EditContentForm, FieldArrayForm, LocalStoreService, ModalModel, ObjectFormBase, SchemaDto, Settings, sorted, Types } from '@app/shared'; @@ -19,7 +19,7 @@ import { ArrayItemComponent } from './array-item.component'; templateUrl: './array-editor.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ArrayEditorComponent implements OnChanges, OnInit { +export class ArrayEditorComponent implements OnChanges { @Input() public form!: EditContentForm; @@ -43,9 +43,9 @@ export class ArrayEditorComponent implements OnChanges, OnInit { @ViewChildren(ArrayItemComponent) public children!: QueryList; - - @ViewChildren(CdkVirtualScrollViewport) - public viewport?: QueryList; + + @ViewChildren(VirtualScrollerComponent) + public scroller?: QueryList; public isArray = false; @@ -66,10 +66,6 @@ export class ArrayEditorComponent implements OnChanges, OnInit { ) { } - public ngOnInit() { - this.isCollapsedInitial = this.formLevel > 0; - } - public ngOnChanges(changes: SimpleChanges) { if (changes['formModel']) { const maxItems = this.formModel.field.properties['maxItems'] || Number.MAX_VALUE; @@ -88,10 +84,10 @@ export class ArrayEditorComponent implements OnChanges, OnInit { ]).pipe(map(([disabled, items]) => { return disabled || items.length >= maxItems; })); + } - if (this.formLevel === 0) { - this.isCollapsedInitial = this.localStore.getBoolean(this.expandedKey()); - } + if (changes['formModel'] || changes['formLevel']) { + this.isCollapsedInitial = this.localStore.getBoolean(this.expandedKey()) || this.formLevel > 0; } } @@ -127,32 +123,28 @@ export class ArrayEditorComponent implements OnChanges, OnInit { this.reset(); } - public onExpanded() { - this.viewport?.first?.checkViewportSize(); - } - public collapseAll() { - this.children.forEach(child => { - child.collapse(); - }); + for (const item of this.formModel.items) { + item.collapse(); + } if (this.formLevel === 0) { this.localStore.setBoolean(this.expandedKey(), true); } - this.onExpanded(); + this.scroller?.first?.invalidateAllCachedMeasurements(); } public expandAll() { - this.children.forEach(child => { - child.expand(); - }); + for (const item of this.formModel.items) { + item.expand(); + } if (this.formLevel === 0) { this.localStore.setBoolean(this.expandedKey(), false); } - this.onExpanded(); + this.scroller?.first?.invalidateAllCachedMeasurements(); } private reset() { diff --git a/frontend/src/app/features/content/shared/forms/array-item.component.html b/frontend/src/app/features/content/shared/forms/array-item.component.html index e5154cf48..e9da04aca 100644 --- a/frontend/src/app/features/content/shared/forms/array-item.component.html +++ b/frontend/src/app/features/content/shared/forms/array-item.component.html @@ -23,10 +23,10 @@ - -
@@ -42,7 +42,7 @@
-
+
implements OnChanges, OnInit { +export class ArrayItemComponent implements OnChanges { @Output() public itemRemove = new EventEmitter(); @@ -77,24 +69,13 @@ export class ArrayItemComponent extends StatefulComponent implements OnCh @ViewChildren(ComponentSectionComponent) public sections!: QueryList; - public isCollapsed = false; public isInvalid?: Observable; public isInvalidComponent?: Observable; public title?: Observable; - constructor(changeDetector: ChangeDetectorRef, - ) { - super(changeDetector, { - isExpanded: false, - isExpandedOnce: false, - }); - } - - public ngOnInit() { - if (!this.isCollapsedInitial) { - this.expand(); - } + public get isCollapsed() { + return this.formModel.collapsedChanges; } public ngOnChanges(changes: SimpleChanges) { @@ -109,16 +90,22 @@ export class ArrayItemComponent extends StatefulComponent implements OnCh this.title = valueProjection$(this.formModel.form, () => getTitle(this.formModel)); } + + if (changes['formModel'] || changes['isCollapsedInitial']) { + if (this.isCollapsedInitial && this.formModel.collapsed === null) { + this.collapse(); + } + } } public collapse() { - this.next({ isExpanded: false }); + this.formModel.collapse(); this.itemExpanded.emit(); } public expand() { - this.next({ isExpanded: true, isExpandedOnce: true }); + this.formModel.expand(); this.itemExpanded.emit(); } diff --git a/frontend/src/app/features/content/shared/list/content.component.ts b/frontend/src/app/features/content/shared/list/content.component.ts index 9c8f80bc7..1094a2b3b 100644 --- a/frontend/src/app/features/content/shared/list/content.component.ts +++ b/frontend/src/app/features/content/shared/list/content.component.ts @@ -59,7 +59,7 @@ export class ContentComponent implements OnChanges { public dropdown = new ModalModel(); public get isDirty() { - return this.patchForm && this.patchForm.form.dirty; + return this.patchForm?.form.dirty === true; } constructor( diff --git a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts index c895ae429..487c2f20e 100644 --- a/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts +++ b/frontend/src/app/features/content/shared/references/reference-dropdown.component.ts @@ -58,12 +58,12 @@ export class ReferenceDropdownComponent extends StatefulControlComponent - VALID - INVALID + VALID + INVALID @@ -22,7 +22,7 @@
- {{content.schemaDisplayName}} + {{content.schemaDisplayName}}
diff --git a/frontend/src/app/features/content/shared/references/reference-item.component.ts b/frontend/src/app/features/content/shared/references/reference-item.component.ts index 76a5bbdd4..cea5b83ec 100644 --- a/frontend/src/app/features/content/shared/references/reference-item.component.ts +++ b/frontend/src/app/features/content/shared/references/reference-item.component.ts @@ -47,12 +47,12 @@ export class ReferenceItemComponent implements OnChanges { @Input('sqxReferenceItem') public content!: ContentDto; - public get valid() { + public values: ReadonlyArray = []; + + public get isValid() { return !this.validations ? undefined : this.validations[this.content.id]; } - public values: ReadonlyArray = []; - public ngOnChanges() { const values = []; diff --git a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts index ebcde256c..81a1dd5ad 100644 --- a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts +++ b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts @@ -45,12 +45,12 @@ export class ReferencesCheckboxesComponent extends StatefulControlComponent(); + public chartData: any; + public chartSummary = 0; + public get chartOptions() { return this.isStacked ? ChartOptions.Stacked : ChartOptions.Default; } - public chartData: any; - public chartSummary = 0; - public ngOnChanges() { if (this.usage) { const labels = ChartHelpers.createLabelsFromSet(this.usage.details); diff --git a/frontend/src/app/features/dashboard/pages/cards/api-traffic-card.component.ts b/frontend/src/app/features/dashboard/pages/cards/api-traffic-card.component.ts index b5e7cea03..0f67830cb 100644 --- a/frontend/src/app/features/dashboard/pages/cards/api-traffic-card.component.ts +++ b/frontend/src/app/features/dashboard/pages/cards/api-traffic-card.component.ts @@ -28,13 +28,13 @@ export class ApiTrafficCardComponent implements OnChanges { @Output() public isStackedChange = new EventEmitter(); + public chartData: any; + public chartSummary = 0; + public get chartOptions() { return this.isStacked ? ChartOptions.Stacked : ChartOptions.Default; } - public chartData: any; - public chartSummary = 0; - public ngOnChanges(changes: SimpleChanges) { if (this.usage && changes['usage']) { const labels = ChartHelpers.createLabelsFromSet(this.usage.details); diff --git a/frontend/src/app/features/rules/declarations.ts b/frontend/src/app/features/rules/declarations.ts index 78bfff2ef..f8db251d2 100644 --- a/frontend/src/app/features/rules/declarations.ts +++ b/frontend/src/app/features/rules/declarations.ts @@ -17,6 +17,7 @@ export * from './shared/actions/formattable-input.component'; export * from './shared/actions/generic-action.component'; export * from './shared/rule-element.component'; export * from './shared/rule-icon.component'; +export * from './shared/pipes'; export * from './shared/triggers/asset-changed-trigger.component'; export * from './shared/triggers/comment-trigger.component'; export * from './shared/triggers/content-changed-trigger.component'; diff --git a/frontend/src/app/features/rules/module.ts b/frontend/src/app/features/rules/module.ts index 5974b764f..121b6ee7e 100644 --- a/frontend/src/app/features/rules/module.ts +++ b/frontend/src/app/features/rules/module.ts @@ -8,7 +8,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HelpComponent, RuleMustExistGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { AssetChangedTriggerComponent, CommentTriggerComponent, ContentChangedTriggerComponent, GenericActionComponent, RuleComponent, RuleElementComponent, RuleEventsPageComponent, RuleIconComponent, RuleSimulatorPageComponent, RulesPageComponent, RuleTransitionComponent, SchemaChangedTriggerComponent, UsageTriggerComponent } from './declarations'; +import { AssetChangedTriggerComponent, CommentTriggerComponent, ContentChangedTriggerComponent, GenericActionComponent, RuleClassPipe, RuleComponent, RuleElementComponent, RuleEventsPageComponent, RuleIconComponent, RuleSimulatorPageComponent, RulesPageComponent, RuleTransitionComponent, SchemaChangedTriggerComponent, SimulatedRuleEventStatusPipe, UsageTriggerComponent } from './declarations'; import { RuleEventComponent } from './pages/events/rule-event.component'; import { RulePageComponent } from './pages/rule/rule-page.component'; import { SimulatedRuleEventComponent } from './pages/simulator/simulated-rule-event.component'; @@ -71,6 +71,7 @@ const routes: Routes = [ ContentChangedTriggerComponent, FormattableInputComponent, GenericActionComponent, + RuleClassPipe, RuleComponent, RuleElementComponent, RuleEventComponent, @@ -82,6 +83,7 @@ const routes: Routes = [ RulesPageComponent, SchemaChangedTriggerComponent, SimulatedRuleEventComponent, + SimulatedRuleEventStatusPipe, UsageTriggerComponent, ], }) diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.html b/frontend/src/app/features/rules/pages/events/rule-event.component.html index 587de3423..308db60fb 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.html +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.html @@ -1,6 +1,6 @@ - {{event.jobResult}} + {{event.jobResult}} {{event.eventName}} @@ -25,7 +25,7 @@
- {{event.result}} + {{event.result}}
{{ 'rules.ruleEvents.numAttemptsLabel' | sqxTranslate }}: {{event.numCalls}} diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.ts b/frontend/src/app/features/rules/pages/events/rule-event.component.ts index c0fca2d7b..c93b045c7 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.ts +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.ts @@ -29,24 +29,4 @@ export class RuleEventComponent { @Output() public cancel = new EventEmitter(); - - public get jobResultClass() { - return getClass(this.event.jobResult); - } - - public get resultClass() { - return getClass(this.event.result); - } -} - -function getClass(result: string) { - if (result === 'Retry') { - return 'warning'; - } else if (result === 'Failed' || result === 'Cancelled') { - return 'danger'; - } else if (result === 'Pending') { - return 'secondary'; - } else { - return result.toLowerCase(); - } } diff --git a/frontend/src/app/features/rules/pages/rules/rule.component.html b/frontend/src/app/features/rules/pages/rules/rule.component.html index 7f8d31018..7da500096 100644 --- a/frontend/src/app/features/rules/pages/rules/rule.component.html +++ b/frontend/src/app/features/rules/pages/rules/rule.component.html @@ -99,12 +99,12 @@
{{ 'common.succeeded' | sqxTranslate }} - {{rule.numSucceeded}} + {{rule.numSucceeded}}
{{ 'common.failed' | sqxTranslate }} - {{rule.numFailed}} + {{rule.numFailed}}
{{ 'common.lastExecuted' | sqxTranslate }}: {{rule.lastExecuted | sqxFromNow:'-'}} diff --git a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts index 7278682ca..1227f3588 100644 --- a/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts +++ b/frontend/src/app/features/rules/pages/simulator/rule-transition.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { SimulatedRuleEventDto } from '@app/shared'; @Component({ @@ -14,7 +14,7 @@ import { SimulatedRuleEventDto } from '@app/shared'; templateUrl: './rule-transition.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RuleTransitionComponent { +export class RuleTransitionComponent implements OnChanges { @Input() public event: SimulatedRuleEventDto | undefined | null; @@ -24,19 +24,25 @@ export class RuleTransitionComponent { @Input() public text: string | undefined | null; - public get filteredErrors() { - const errors = this.errors; + public filteredErrors?: string[] | null; - if (!errors) { - return null; - } + public ngOnChanges(changes: SimpleChanges) { + if (changes['event'] || changes['errors']) { + const errors = this.errors; - const result = this.event?.skipReasons.filter(x => errors.includes(x)).map(x => `rules.simulation.error${x}`); + if (!errors) { + this.filteredErrors = null; + return; + } - if (result?.length === 0) { - return null; - } + const result = this.event?.skipReasons.filter(x => errors.includes(x)).map(x => `rules.simulation.error${x}`); - return result; + if (result?.length === 0) { + this.filteredErrors = null; + return; + } + + this.filteredErrors = result; + } } } diff --git a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html index fef7ad512..bc0456d88 100644 --- a/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html +++ b/frontend/src/app/features/rules/pages/simulator/simulated-rule-event.component.html @@ -1,12 +1,12 @@ - {{status}} + {{event | sqxSimulatedRuleEventStatus}} {{event.eventName}} - {{event.skipReasons.join(', ')}} + {{event.skipReasons | sqxJoin}}
- + {{ 'schemas.field.lockedMarker' | sqxTranslate }} - + {{ 'schemas.field.enabledMarker' | sqxTranslate }} - + {{ 'schemas.field.disabledMarker' | sqxTranslate }}
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts index e19fc4f17..06dce86e0 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts @@ -30,10 +30,6 @@ export class FieldComponent implements OnChanges { @Input() public settings!: AppSettingsDto; - public get isLocalizable() { - return (this.parent && this.parent.isLocalizable) || this.field['isLocalizable']; - } - public dropdown = new ModalModel(); public trackByFieldFn: (_index: number, field: NestedFieldDto) => any; @@ -45,6 +41,10 @@ export class FieldComponent implements OnChanges { public addFieldDialog = new DialogModel(); + public get isLocalizable() { + return (this.parent && this.parent.isLocalizable) || this.field['isLocalizable']; + } + constructor( private readonly schemasState: SchemasState, ) { diff --git a/frontend/src/app/features/settings/pages/backups/backup.component.ts b/frontend/src/app/features/settings/pages/backups/backup.component.ts index e5e898661..3e035c1d4 100644 --- a/frontend/src/app/features/settings/pages/backups/backup.component.ts +++ b/frontend/src/app/features/settings/pages/backups/backup.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ApiUrlConfig, BackupDto, BackupsState, Duration } from '@app/shared'; @Component({ @@ -14,19 +14,24 @@ import { ApiUrlConfig, BackupDto, BackupsState, Duration } from '@app/shared'; templateUrl: './backup.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class BackupComponent { +export class BackupComponent implements OnChanges { @Input() public backup!: BackupDto; - public get duration() { - return Duration.create(this.backup.started, this.backup.stopped!).toString(); - } + public duration = ''; constructor( - public readonly apiUrl: ApiUrlConfig, private readonly backupsState: BackupsState, + public readonly apiUrl: ApiUrlConfig, + private readonly backupsState: BackupsState, ) { } + public ngOnChanges(changes: SimpleChanges) { + if (changes['backup']) { + this.duration = Duration.create(this.backup.started, this.backup.stopped!).toString(); + } + } + public delete() { this.backupsState.delete(this.backup); } diff --git a/frontend/src/app/features/settings/pages/roles/role.component.html b/frontend/src/app/features/settings/pages/roles/role.component.html index dfe628263..b0f631bc9 100644 --- a/frontend/src/app/features/settings/pages/roles/role.component.html +++ b/frontend/src/app/features/settings/pages/roles/role.component.html @@ -6,12 +6,12 @@
{{ 'common.clients' | sqxTranslate }} - {{role.numClients}} + {{role.numClients}}
{{ 'common.users' | sqxTranslate }} - {{role.numContributors}} + {{role.numContributors}}
diff --git a/frontend/src/app/features/settings/pages/roles/role.component.ts b/frontend/src/app/features/settings/pages/roles/role.component.ts index a4bf3c8f0..f33711cef 100644 --- a/frontend/src/app/features/settings/pages/roles/role.component.ts +++ b/frontend/src/app/features/settings/pages/roles/role.component.ts @@ -50,10 +50,6 @@ export class RoleComponent implements OnChanges { @ViewChild('addInput', { static: false }) public addPermissionInput!: AutocompleteComponent; - public get halfSchemas() { - return Math.ceil(this.schemas.length / 2); - } - public descriptions = DESCRIPTIONS; public propertiesList = Settings.AppProperties; @@ -67,6 +63,10 @@ export class RoleComponent implements OnChanges { public editForm = new EditRoleForm(); + public get halfSchemas() { + return Math.ceil(this.schemas.length / 2); + } + constructor( private readonly rolesState: RolesState, ) { diff --git a/frontend/src/app/framework/angular/if-once.directive.ts b/frontend/src/app/framework/angular/if-once.directive.ts new file mode 100644 index 000000000..5659a048e --- /dev/null +++ b/frontend/src/app/framework/angular/if-once.directive.ts @@ -0,0 +1,30 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector: '[sqxIfOnce]', +}) +export class IfOnceDirective { + private hasView = false; + + @Input('sqxIfOnce') + public set condition(value: boolean) { + if (value && !this.hasView) { + this.viewContainer.createEmbeddedView(this.templateRef); + + this.hasView = true; + } + } + + constructor( + private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + ) { + } +} \ No newline at end of file diff --git a/frontend/src/app/framework/angular/layout.component.ts b/frontend/src/app/framework/angular/layout.component.ts index 75fc0fcb6..be8166e8b 100644 --- a/frontend/src/app/framework/angular/layout.component.ts +++ b/frontend/src/app/framework/angular/layout.component.ts @@ -68,6 +68,8 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { @ViewChild('panel', { static: false }) public panel!: ElementRef; + public isCollapsed = false; + public get desiredWidth() { return this.isCollapsed ? 3 : this.width; } @@ -84,8 +86,6 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { return this.widthToRender; } - public isCollapsed = false; - public firstChild = this.router.events.pipe( filter(event => event instanceof NavigationEnd), diff --git a/frontend/src/app/framework/angular/pipes/strings.pipes.ts b/frontend/src/app/framework/angular/pipes/strings.pipes.ts new file mode 100644 index 000000000..aed890512 --- /dev/null +++ b/frontend/src/app/framework/angular/pipes/strings.pipes.ts @@ -0,0 +1,18 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'sqxJoin', + pure: true, +}) +export class JoinPipe implements PipeTransform { + public transform(value: ReadonlyArray) { + return value?.join(', ') || ''; + } +} \ No newline at end of file diff --git a/frontend/src/app/framework/declarations.ts b/frontend/src/app/framework/declarations.ts index 9a12ab68b..2ab22f58c 100644 --- a/frontend/src/app/framework/declarations.ts +++ b/frontend/src/app/framework/declarations.ts @@ -45,6 +45,7 @@ export * from './angular/http/http-extensions'; export * from './angular/http/loading.interceptor'; export * from './angular/image-source.directive'; export * from './angular/image-url.directive'; +export * from './angular/if-once.directive'; export * from './angular/language-selector.component'; export * from './angular/layout-container.directive'; export * from './angular/layout.component'; @@ -65,6 +66,7 @@ export * from './angular/pipes/keys.pipe'; export * from './angular/pipes/markdown.pipe'; export * from './angular/pipes/name.pipe'; export * from './angular/pipes/numbers.pipes'; +export * from './angular/pipes/strings.pipes'; export * from './angular/pipes/translate.pipe'; export * from './angular/resized.directive'; export * from './angular/routers/can-deactivate.guard'; diff --git a/frontend/src/app/framework/module.ts b/frontend/src/app/framework/module.ts index d7679e4ed..aee7b74e7 100644 --- a/frontend/src/app/framework/module.ts +++ b/frontend/src/app/framework/module.ts @@ -11,7 +11,7 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { ColorPickerModule } from 'ngx-color-picker'; -import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; +import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; @NgModule({ imports: [ @@ -55,10 +55,12 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, + IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, + JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, @@ -140,10 +142,12 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, + IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, + JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, diff --git a/frontend/src/app/shared/components/assets/asset-dialog.component.ts b/frontend/src/app/shared/components/assets/asset-dialog.component.ts index 985b44775..fd4eb9858 100644 --- a/frontend/src/app/shared/components/assets/asset-dialog.component.ts +++ b/frontend/src/app/shared/components/assets/asset-dialog.component.ts @@ -44,14 +44,13 @@ export class AssetDialogComponent implements OnChanges { public path!: Observable>; + public selectedTab = 0; public isEditable = false; public isEditableAny = false; public isUploadable = false; public progress = 0; - public selectedTab = 0; - public annotateForm = new AnnotateAssetForm(); public get isImage() { diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html index cadceb56c..dff3f339d 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html @@ -1,4 +1,4 @@ -
+
- {{schemaCategory.countSchemasInSubtreeFiltered}} + {{schemaCategory.countSchemasInSubtreeFiltered}}
-
{{result.label}}
+
{{result.label}}