diff --git a/docs/cs/Best-Practices/PostgreSQL-Integration.md b/docs/cs/Best-Practices/PostgreSQL-Integration.md new file mode 100644 index 0000000000..6bf8224f1e --- /dev/null +++ b/docs/cs/Best-Practices/PostgreSQL-Integration.md @@ -0,0 +1,35 @@ +## Entity Framework Core PostgreSQL integrace + +> Podívejte se na [Entity Framework Core integrační dokument](../Entity-Framework-Core.md) pro základy integrace EF Core. + +### Aktualizace projektu EntityFrameworkCore + +- V projektu `Acme.BookStore.EntityFrameworkCore` nahraďte balík `Volo.Abp.EntityFrameworkCore.SqlServer` za `Volo.Abp.EntityFrameworkCore.PostgreSql` +- Aktualizace pro použití PostgreSQL v `BookStoreEntityFrameworkCoreModule` + - Nahraďte `AbpEntityFrameworkCoreSqlServerModule` za `AbpEntityFrameworkCorePostgreSqlModule` + - Nahraďte `options.UseSqlServer()` za `options.UsePostgreSql()` +- V jiných projektech aktualizujte PostgreSQL connection string v nezbytných `appsettings.json` souborech + +#### Odstranění stávajících migrací + +Smažte všechny stavající migrační soubory (včetně `DbContextModelSnapshot`) + +![postgresql-delete-initial-migrations](images/postgresql-delete-initial-migrations.png) + +#### Znovu vygenerujte počáteční migraci & aktualizujte databázi + +Nastavte správný spouštěcí projekt (obvykle web projekt), +Otevřete **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console), zvolte `Acme.BookStore.EntityFrameworkCore.DbMigrations` jako **Default project** a proveďte následující příkaz: + +Proveďte příkaz `Add-Migration`: +```` +PM> Add-Migration Initial +```` + +Poté proveďte příkaz `Update-Database` k aktualizaci schématu databáze: + +```` +PM> Update-Database +```` + +![postgresql-update-database](images/postgresql-update-database.png) diff --git a/docs/cs/Best-Practices/images/postgresql-delete-initial-migrations.png b/docs/cs/Best-Practices/images/postgresql-delete-initial-migrations.png new file mode 100644 index 0000000000..39d9e6dedf Binary files /dev/null and b/docs/cs/Best-Practices/images/postgresql-delete-initial-migrations.png differ diff --git a/docs/cs/Best-Practices/images/postgresql-update-database.png b/docs/cs/Best-Practices/images/postgresql-update-database.png new file mode 100644 index 0000000000..02b61ef6a3 Binary files /dev/null and b/docs/cs/Best-Practices/images/postgresql-update-database.png differ diff --git a/docs/cs/Dapper.md b/docs/cs/Dapper.md new file mode 100644 index 0000000000..94e40347f0 --- /dev/null +++ b/docs/cs/Dapper.md @@ -0,0 +1,61 @@ +# Dapper integrace + +Jelikož myšlenka Dapper je taková, že sql příkaz má přednost, tak hlavně poskytuje metody rozšíření pro `IDbConnection` rozhraní. + +Abp nezapouzdřuje přílíš mnoho funkcí pro Dapper. Abp Dapper poskytuje základní třídu `DapperRepository` založenou na Abp EntityFrameworkCore, který poskytuje vlastnosti `IDbConnection` a `IDbTransaction` vyžadované v Dapper. + +Tyto dvě vlastnosti fungují dobře s [jednotkou práce](Unit-Of-Work.md). + +## Instalace + +Nainstalujte a nakonfigurujte EF Core podle [EF Core integrační dokumentace](Entity-Framework-Core.md). + +`Volo.Abp.Dapper` je hlavní NuGet balík pro Dapper integraci. Nainstalujte jej proto do vašeho projektu (pro strukturovanou aplikaci do datové/infrastrukturní vrstvy): + +```shell +Install-Package Volo.Abp.Dapper +``` + +Poté přidejte závislost na `AbpDapperModule` modulu (atribut `DependsOn`) do Vašeho [modulu](Module-Development-Basics.md): + +````C# +using Volo.Abp.Dapper; +using Volo.Abp.Modularity; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpDapperModule))] + public class MyModule : AbpModule + { + //... + } +} +```` + +## Implementace Dapper repozitáře + +Následující kód implementuje repozitář `Person`, který vyžaduje `DbContext` z EF Core (MyAppDbContext). Můžete vložit `PersonDapperRepository` k volání jeho metod. + +`DbConnection` a `DbTransaction` jsou ze základní třídy `DapperRepository`. + +```C# +public class PersonDapperRepository : DapperRepository, ITransientDependency +{ + public PersonDapperRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task> GetAllPersonNames() + { + return (await DbConnection.QueryAsync("select Name from People", transaction: DbTransaction)) + .ToList(); + } + + public virtual async Task UpdatePersonNames(string name) + { + return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name }, + DbTransaction); + } +} +``` diff --git a/docs/cs/Getting-Started-Angular-Template.md b/docs/cs/Getting-Started-Angular-Template.md new file mode 100644 index 0000000000..6accc637e8 --- /dev/null +++ b/docs/cs/Getting-Started-Angular-Template.md @@ -0,0 +1,126 @@ +## Začínáme s Angular aplikační šablonou + +Tento tutoriál vysvětluje, jak vytvořit novou Angular aplikaci pomocí spouštěcí šablony, jak ji nakonfigurovat a spustit. + +### Tvorba nového projektu + +Tento tutorial používá k vytvoření nového projektu **ABP CLI**. Podívejte se na stránku [začínáme](https://abp.io/get-started) pro více možností. + +Pokud jste tak dosud neučinili, nainstalujte ABP CLI pomocí okna příkazového řádku: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +Použíjte příkaz `abp new` v prázdné složce k vytvoření Vašeho projektu: + +````bash +abp new Acme.BookStore -u angular +```` + +> Můžete použít různé úrovně jmenných prostorů; např. BookStore, Acme.BookStore nebo Acme.Retail.BookStore. + +`-u angular` volba specifikuje Angular jako UI framework. Výchozí poskytovatel databáze je EF Core. Podívejte se na [CLI dokumentaci](CLI.md) pro všechny dostupné možnosti. + +#### Předběžné požadavky + +Vytvořené řešení vyžaduje; + +* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/) +* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/) +* [Node v10.16+](https://nodejs.org) +* [Yarn v1.17+](https://yarnpkg.com/) + +### Struktura řešení + +Otevřete řešení ve **Visual Studio**: + +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-for-spa.png) + +Řešení má vrstvenou strukturu (založenou na [domain driven designu](Domain-Driven-Design.md)) a obsahuje projekty testů jednotek a integrace správně nakonfigurované pro práci s **EF Core** & **SQLite in-memory** databází. + +> Podívejte se na [dokument šablony aplikace](Startup-Templates/Application.md) k detailnímu pochopení struktury řešení. + +### Databázový connection string + +Zkontrolujte **connection string** v souboru `appsettings.json` u projektu `.HttpApi.Host`: + +````json +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" + } +} +```` + +Řešení je nakonfigurováno pro použití **Entity Framework Core** s **MS SQL Server**. EF Core podporuje [různé](https://docs.microsoft.com/en-us/ef/core/providers/) poskytovatele databáze, takže pokud chcete můžete použít jiný DBMS. V případě potřeby změňte connection string. + +### Tvorba databáze & aplikace migrací databáze + +K vytvoření databáze máte dvě možnosti. + +#### Použití aplikace DbMigrator + +Řešení obsahuje konzolovou aplikaci (v tomto příkladu nazvanou `Acme.BookStore.DbMigrator`), která dokáže vytvořit databázi, aplikovat migrace a vložit počáteční data. Ta je užitečná jak pro vývojové tak pro produkční prostředí. + +> `.DbMigrator` má vlastní `appsettings.json`. Pokud jste změnili connection string výše, měli byste změnit i tento. + +Klikněte pravým na projekt `.DbMigrator` zvolte **Set as StartUp Project**: + +![set-as-startup-project](images/set-as-startup-project.png) + +Zmáčkněte F5 (nebo Ctrl+F5) ke spuštění aplikace. Výstup by měl být podobný vyobrazení níže: + +![set-as-startup-project](images/db-migrator-app.png) + +#### Použití příkazu EF Core Update-Database + +Ef Core máš příkaz `Update-Database`, který v případě potřeby vytvoří databázi a aplikuje čekající migrace. Klikněte pravým na projekt `.Web` a zvolte **Set as StartUp Project**: + +![set-as-startup-project](images/set-as-startup-project.png) + +Otevřete **Package Manager Console**, zvolte `.EntityFrameworkCore.DbMigrations` jako **Default Project** a proveďte příkaz `Update-Database`: + +![pcm-update-database](images/pcm-update-database-v2.png) + +Tímto vytvoříte novou databáze podle nakonfigurovaného connection string. + +> Je doporučeno užití nástroje `.DbMigrator`, protože zároveň vloží i počáteční data ke správnému běhu webové aplikace. + +### Spuštění aplikace + +#### Spuštění API Host (na straně serveru) + +Ujistěte se že je projekt `.HttpApi.Host` nastaven jako startovací a spusťte aplikaci což otevře Swagger UI: + +![bookstore-homepage](images/bookstore-swagger-ui-host.png) + +Tady můžete vidět API aplikace a zároveň je i otestovat. Získejte [více informací](https://swagger.io/tools/swagger-ui/) o Swagger UI. + +##### Autorizace pro Swagger UI + +Vetšina API aplikace vyžaduje autentizaci & autorizaci. Pokud chcete otestovat autorizované API, manuálně přejděte na stránku `/Account/Login`, vložte `admin` jako uživatelské jméno a `1q2w3E*` jako heslo k příhlášení do aplikace. Poté budete moci provádět autorizované požadavky API. + +#### Spuštění Angular aplikace (na straně klienta) + +Přejděte do složky `angular`, otevřete terminál příkazového řádku, proveďte příkaz `yarn` (doporučujeme používat správce balíků [yarn](https://yarnpkg.com), npm install bude v mnoha případech také fungovat): + +````bash +yarn +```` + +Jakmile jsou načteny všechny node moduly, proveďte příkaz `yarn start` nebo `npm start`: + +````bash +yarn start +```` + +Otevřete Váš oblíbený prohlížeč a přejděte na adresu `localhost:4200`. Počáteční uživatelské jméno je `admin` a heslo `1q2w3E*`. + +Startovací šablona obsahuje moduly **správa identit** a **správa tenantů**. Jakmile se přihlásíte, zprístupní se administrační menu kde můžete spravovat **tenanty**, **role**, **uživatele** a jejich **oprávnění**. + +> Doporučujeme [Visual Studio Code](https://code.visualstudio.com/) jako editor pro Angular projekt, ale klidně použijte Váš oblíbený editor. + +### Co dále? + +* [Tutoriál vývoje aplikace](Tutorials/Angular/Part-I.md) diff --git a/docs/cs/docs-nav.json b/docs/cs/docs-nav.json index 8560190492..91badc73e9 100644 --- a/docs/cs/docs-nav.json +++ b/docs/cs/docs-nav.json @@ -202,7 +202,7 @@ "items": [ { "text": "API", - "items": [ + "items": [ { "text": "Automatické API řadiče", "path": "AspNetCore/Auto-API-Controllers.md" @@ -245,11 +245,21 @@ "items": [ { "text": "Entity Framework Core integrace", - "path": "Entity-Framework-Core.md" + "path": "Entity-Framework-Core.md", + "items": [ + { + "text": "PostgreSQL integrace", + "path": "Best-Practices/PostgreSQL-Integration.md" + } + ] }, { "text": "MongoDB integrace", "path": "MongoDB.md" + }, + { + "text": "Dapper integrace", + "path": "Dapper.md" } ] }, diff --git a/docs/en/Getting-Started-Angular-Template.md b/docs/en/Getting-Started-Angular-Template.md index 1bd605cd65..31186eaa35 100644 --- a/docs/en/Getting-Started-Angular-Template.md +++ b/docs/en/Getting-Started-Angular-Template.md @@ -85,13 +85,13 @@ Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` This will create a new database based on the configured connection string. -> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application. +> Using the `.DbMigrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application. ### Running the Application #### Run the API Host (Server Side) -Ensure that the `.HttpApi.Host` project is the startup project and un the application which will open a Swagger UI: +Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI: ![bookstore-homepage](images/bookstore-swagger-ui-host.png) diff --git a/docs/es/Index.md b/docs/es/Index.md new file mode 100644 index 0000000000..772a0afc13 --- /dev/null +++ b/docs/es/Index.md @@ -0,0 +1,31 @@ +# Documentación de ABP + +ABP es un **marco de desarrollo de código abierto** enfocado en el desarrollo de aplicaciones web basadas en ASP.NET Core, pero tambien soporta el desarrollo de otro tipo de aplicaciones. + +Explora el menú de navegación de la izquierda para profundizar en la documentación. + +## Estado del proyecto + +ABP es la **próxima generación** del marco de desarrollo de código abierto [ASP.NET Boilerplate](https://aspnetboilerplate.com/). Actualmente se encuentra en una fase preliminar y no está listo para su uso en producción. La documentación todavía está en progreso y se encuentra lejos de estar completa. + +Para aplicaciones en fase de producción o que lo vayan a estar en el corto plazo, se sugiere usar el marco de desarrollo [ASP.NET Boilerplate](https://aspnetboilerplate.com/) el cual tiene un completo conjunto de funciones, es maduro, mantenido y actualizado activamente. + +## Primeros pasos + +La manera más facil para iniciar un proyecto con ABP es usar las plantillas de inicio: + +* [Plantilla ASP.NET Core MVC (Razor Pages) UI](Getting-Started-AspNetCore-MVC-Template.md) +* [Plantilla Angular UI](Getting-Started-Angular-Template.md) + +Si quieres iniciar desde cero (con un proyecto vacío), entonces instala manualmente el marco de desarrollo ABP y usa los siguientes tutoriales: + +* [Aplicación de Consola](Getting-Started-Console-Application.md) +* [Aplicación web con ASP.NET Core](Getting-Started-AspNetCore-Application.md) + +## Código fuente + +ABP está hospedado en GitHub. Mira el [código fuente](https://github.com/abpframework/abp). + +## ¿Quieres contribuir? + +ABP es un proyecto de código abierto impulsado por la comunidad. Mira la [guía de contribución](Contribution/Index.md) si quieres formar parte de este proyecto. \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index fb549d3067..75fe4f23e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -227,17 +227,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form if (string.IsNullOrEmpty(TagHelper.Label)) { - return await GetLabelAsHtmlUsingTagHelperAsync(context, output, isCheckbox) + GetRequiredSymbol(context, output, inputTag); + return await GetLabelAsHtmlUsingTagHelperAsync(context, output, isCheckbox) + GetRequiredSymbol(context, output); } var checkboxClass = isCheckbox ? "class=\"custom-control-label\" " : ""; return "" + GetRequiredSymbol(context, output, inputTag); + "" + GetRequiredSymbol(context, output); } - protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) + protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output) { if (!TagHelper.DisplayRequiredSymbol) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs index d7dbb70d14..f02f03d502 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs @@ -169,8 +169,16 @@ namespace Volo.Abp.Cli } } + private static bool IsGlobalTool(string toolPath) + { + var globalPaths = new[] { @"%USERPROFILE%\.dotnet\tools\", "%HOME%/.dotnet/tools/", }; + return globalPaths.Select(Environment.ExpandEnvironmentVariables).Contains(toolPath); + } + private void LogNewVersionInfo(UpdateChannel updateChannel, SemanticVersion latestVersion, string toolPath) { + var toolPathArg = IsGlobalTool(toolPath) ? "-g" : $"--tool-path {toolPath}"; + Logger.LogWarning($"ABP CLI has a newer {updateChannel.ToString().ToLowerInvariant()} version {latestVersion}, please update to get the latest features and fixes."); Logger.LogWarning(string.Empty); Logger.LogWarning("Update Command: "); @@ -179,18 +187,18 @@ namespace Volo.Abp.Cli switch (updateChannel) { case UpdateChannel.Stable: - Logger.LogWarning("dotnet tool update -g Volo.Abp.Cli"); + Logger.LogWarning($"dotnet tool update {toolPathArg} Volo.Abp.Cli"); break; case UpdateChannel.Prerelease: - Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli"); - Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --version {latestVersion}"); + Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli"); + Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --version {latestVersion}"); break; case UpdateChannel.Nightly: case UpdateChannel.Development: - Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli"); - Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}"); + Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli"); + Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}"); break; default: throw new ArgumentOutOfRangeException(nameof(updateChannel), updateChannel, null); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs index db39a660ce..4cf3209955 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs @@ -29,8 +29,7 @@ namespace Volo.Abp.Cli.ProjectModification var csFiles = new DirectoryInfo(csprojFileDirectory) .GetFiles("*.cs", SearchOption.AllDirectories) - .Where(f => !f.DirectoryName.StartsWith(binFile)) - .Where(f => !f.DirectoryName.StartsWith(objFile)) + .Where(f => f.DirectoryName != null && (!f.DirectoryName.StartsWith(binFile) || !f.DirectoryName.StartsWith(objFile))) .Select(f => f.FullName) .ToList(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs index 548290df5c..4a313e784d 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs @@ -83,8 +83,11 @@ namespace Volo.Abp.Cli.ProjectModification public static string[] GetProjectFiles(string solutionFile) { - var baseProjectFolder = GetBaseProjectFolder(solutionFile); - return Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories); + return GetBaseProjectFolders(solutionFile) + .Select(baseProjectFolder => + Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories)) + .SelectMany(files => files) + .ToArray(); } public static string[] GetAssemblyNames(string[] projectFiles) @@ -118,16 +121,33 @@ namespace Volo.Abp.Cli.ProjectModification return null; } - private static string GetBaseProjectFolder(string solutionFile) + private static string[] GetBaseProjectFolders(string solutionFile) { + var projectFolders = new List(); var baseFolder = Path.GetDirectoryName(solutionFile); + if (baseFolder == null) + { + return projectFolders.ToArray(); + } + var srcFolder = Path.Combine(baseFolder, "src"); if (Directory.Exists(srcFolder)) { - baseFolder = srcFolder; + projectFolders.Add(srcFolder); + } + + var testFolder = Path.Combine(baseFolder, "test"); + if (Directory.Exists(testFolder)) + { + projectFolders.Add(testFolder); + } + + if (!projectFolders.Any()) + { + projectFolders.Add(baseFolder); } - return baseFolder; + return projectFolders.ToArray(); } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs index c5cbb16a1f..188cc4d48c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs @@ -8,23 +8,22 @@ namespace Volo.Abp.Identity public override void Define(ISettingDefinitionContext context) { context.Add( + new SettingDefinition(IdentitySettingNames.Password.RequiredLength, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Password.RequiredUniqueChars, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Password.RequireNonAlphanumeric, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Password.RequireLowercase, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Password.RequireUppercase, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Password.RequireDigit, null, null, null, true), - new SettingDefinition(IdentitySettingNames.Password.RequiredLength), - new SettingDefinition(IdentitySettingNames.Password.RequiredUniqueChars), - new SettingDefinition(IdentitySettingNames.Password.RequireNonAlphanumeric), - new SettingDefinition(IdentitySettingNames.Password.RequireLowercase), - new SettingDefinition(IdentitySettingNames.Password.RequireUppercase), - new SettingDefinition(IdentitySettingNames.Password.RequireDigit), + new SettingDefinition(IdentitySettingNames.Lockout.AllowedForNewUsers, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Lockout.LockoutDuration, null, null, null, true), + new SettingDefinition(IdentitySettingNames.Lockout.MaxFailedAccessAttempts, null, null, null, true), - new SettingDefinition(IdentitySettingNames.Lockout.AllowedForNewUsers), - new SettingDefinition(IdentitySettingNames.Lockout.LockoutDuration), - new SettingDefinition(IdentitySettingNames.Lockout.MaxFailedAccessAttempts), + new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedEmail, null, null, null, true), + new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber, null, null, null, true), - new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedEmail), - new SettingDefinition(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber), - - new SettingDefinition(IdentitySettingNames.User.IsUserNameUpdateEnabled, "true"), - new SettingDefinition(IdentitySettingNames.User.IsEmailUpdateEnabled, "true") + new SettingDefinition(IdentitySettingNames.User.IsUserNameUpdateEnabled, "true", null, null, true), + new SettingDefinition(IdentitySettingNames.User.IsEmailUpdateEnabled, "true", null, null, true) ); } diff --git a/npm/ng-packs/.vscode/settings.json b/npm/ng-packs/.vscode/settings.json index a768ac15eb..ba819b4f57 100644 --- a/npm/ng-packs/.vscode/settings.json +++ b/npm/ng-packs/.vscode/settings.json @@ -1,5 +1,22 @@ { "search.exclude": { "**/dist": true - } -} \ No newline at end of file + }, + "tslint.rulesDirectory": "../node_modules/codelyzer", + "typescript.tsdk": "../node_modules/typescript/lib", + "workbench.colorCustomizations": { + "activityBar.background": "#df7fa2", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#c7ecb3", + "activityBarBadge.foreground": "#15202b", + "titleBar.activeBackground": "#d55685", + "titleBar.inactiveBackground": "#d5568599", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveForeground": "#15202b99", + "statusBar.background": "#d55685", + "statusBarItem.hoverBackground": "#c73169", + "statusBar.foreground": "#15202b" + }, + "peacock.color": "#d55685" +} diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index 617e907f3f..fb32a2d757 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -282,9 +282,257 @@ "packages/setting-management/tsconfig.lib.json", "packages/setting-management/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] + } + } + } + }, + "setting-management-config": { + "projectType": "library", + "root": "packages/setting-management-config", + "sourceRoot": "packages/setting-management-config/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/setting-management-config/tsconfig.lib.json", + "project": "packages/setting-management-config/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "packages/setting-management-config/src/test.ts", + "tsConfig": "packages/setting-management-config/tsconfig.spec.json", + "karmaConfig": "packages/setting-management-config/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "packages/setting-management-config/tsconfig.lib.json", + "packages/setting-management-config/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "identity-config": { + "projectType": "library", + "root": "packages/identity-config", + "sourceRoot": "packages/identity-config/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/identity-config/tsconfig.lib.json", + "project": "packages/identity-config/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "packages/identity-config/src/test.ts", + "tsConfig": "packages/identity-config/tsconfig.spec.json", + "karmaConfig": "packages/identity-config/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["packages/identity-config/tsconfig.lib.json", "packages/identity-config/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "account-config": { + "projectType": "library", + "root": "packages/account-config", + "sourceRoot": "packages/account-config/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/account-config/tsconfig.lib.json", + "project": "packages/account-config/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "packages/account-config/src/test.ts", + "tsConfig": "packages/account-config/tsconfig.spec.json", + "karmaConfig": "packages/account-config/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["packages/account-config/tsconfig.lib.json", "packages/account-config/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "tenant-management-config": { + "projectType": "library", + "root": "packages/tenant-management-config", + "sourceRoot": "packages/tenant-management-config/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "packages/tenant-management-config/tsconfig.lib.json", + "project": "packages/tenant-management-config/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "packages/tenant-management-config/src/test.ts", + "tsConfig": "packages/tenant-management-config/tsconfig.spec.json", + "karmaConfig": "packages/tenant-management-config/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "packages/tenant-management-config/tsconfig.lib.json", + "packages/tenant-management-config/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "dev-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "apps/dev-app", + "sourceRoot": "apps/dev-app/src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/dev-app", + "index": "apps/dev-app/src/index.html", + "main": "apps/dev-app/src/main.ts", + "polyfills": "apps/dev-app/src/polyfills.ts", + "tsConfig": "apps/dev-app/tsconfig.app.json", + "aot": false, + "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], + "styles": [ + "apps/dev-app/src/styles.scss", + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/font-awesome/css/font-awesome.min.css", + "node_modules/primeng/resources/themes/nova-light/theme.css", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/primeng.min.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "apps/dev-app/src/environments/environment.ts", + "with": "apps/dev-app/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "dev-app:build" + }, + "configurations": { + "production": { + "browserTarget": "dev-app:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "dev-app:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/dev-app/src/test.ts", + "polyfills": "apps/dev-app/src/polyfills.ts", + "tsConfig": "apps/dev-app/tsconfig.spec.json", + "karmaConfig": "apps/dev-app/karma.conf.js", + "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], + "styles": [ + "apps/dev-app/src/styles.scss", + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/font-awesome/css/font-awesome.min.css", + "node_modules/primeng/resources/themes/nova-light/theme.css", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/primeng.min.css" + ], + "scripts": [] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "apps/dev-app/tsconfig.app.json", + "apps/dev-app/tsconfig.spec.json", + "apps/dev-app/e2e/tsconfig.json" + ], + "exclude": ["**/node_modules/**"] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "apps/dev-app/e2e/protractor.conf.js", + "devServerTarget": "dev-app:serve" + }, + "configurations": { + "production": { + "devServerTarget": "dev-app:serve:production" + } } } } diff --git a/npm/ng-packs/apps/dev-app/browserslist b/npm/ng-packs/apps/dev-app/browserslist new file mode 100644 index 0000000000..80848532e4 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/browserslist @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/npm/ng-packs/apps/dev-app/e2e/protractor.conf.js b/npm/ng-packs/apps/dev-app/e2e/protractor.conf.js new file mode 100644 index 0000000000..5c23d8477d --- /dev/null +++ b/npm/ng-packs/apps/dev-app/e2e/protractor.conf.js @@ -0,0 +1,30 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: ['./src/**/*.e2e-spec.ts'], + capabilities: { + browserName: 'chrome', + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {}, + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json'), + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + }, +}; diff --git a/npm/ng-packs/apps/dev-app/e2e/src/app.e2e-spec.ts b/npm/ng-packs/apps/dev-app/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..96edba0042 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/e2e/src/app.e2e-spec.ts @@ -0,0 +1,28 @@ +import { AppPage } from './app.po'; +import { browser, logging } from 'protractor'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getTitleText()).toEqual('dev-app app is running!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser + .manage() + .logs() + .get(logging.Type.BROWSER); + expect(logs).not.toContain( + jasmine.objectContaining({ + level: logging.Level.SEVERE, + } as logging.Entry), + ); + }); +}); diff --git a/npm/ng-packs/apps/dev-app/e2e/src/app.po.ts b/npm/ng-packs/apps/dev-app/e2e/src/app.po.ts new file mode 100644 index 0000000000..b8498c26f2 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get(browser.baseUrl) as Promise; + } + + getTitleText() { + return element(by.css('app-root .content span')).getText() as Promise; + } +} diff --git a/npm/ng-packs/apps/dev-app/e2e/tsconfig.json b/npm/ng-packs/apps/dev-app/e2e/tsconfig.json new file mode 100644 index 0000000000..201351434e --- /dev/null +++ b/npm/ng-packs/apps/dev-app/e2e/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/e2e", + "module": "commonjs", + "target": "es5", + "types": ["jasmine", "jasminewd2", "node"] + } +} diff --git a/npm/ng-packs/apps/dev-app/karma.conf.js b/npm/ng-packs/apps/dev-app/karma.conf.js new file mode 100644 index 0000000000..54492af159 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/dev-app'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true, + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts new file mode 100644 index 0000000000..d6fa06f853 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts @@ -0,0 +1,34 @@ +import { ABP } from '@abp/ng.core'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +const routes: Routes = [ + { + path: '', + loadChildren: () => import('./home/home.module').then(m => m.HomeModule), + data: { + routes: { + name: '::Menu:Home', + } as ABP.Route, + }, + }, + { + path: 'account', + loadChildren: () => import('./lazy-libs/account-wrapper.module').then(m => m.AccountWrapperModule), + }, + { + path: 'identity', + loadChildren: () => import('./lazy-libs/identity-wrapper.module').then(m => m.IdentityWrapperModule), + }, + { + path: 'tenant-management', + loadChildren: () => + import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule), + }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/app.component.ts b/npm/ng-packs/apps/dev-app/src/app/app.component.ts new file mode 100644 index 0000000000..bf2a27962a --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + + + `, +}) +export class AppComponent {} diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts new file mode 100644 index 0000000000..14c9a7bb4f --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -0,0 +1,43 @@ +import { CoreModule } from '@abp/ng.core'; +import { LAYOUTS } from '@abp/ng.theme.basic'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin'; +import { NgxsModule } from '@ngxs/store'; +import { OAuthModule } from 'angular-oauth2-oidc'; +import { environment } from '../environments/environment'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { SharedModule } from './shared/shared.module'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { AccountConfigModule } from '@abp/ng.account.config'; +import { IdentityConfigModule } from '@abp/ng.identity.config'; +import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + CoreModule.forRoot({ + environment, + requirements: { + layouts: LAYOUTS, + }, + }), + ThemeSharedModule.forRoot(), + AccountConfigModule.forRoot({ redirectUrl: '/' }), + IdentityConfigModule, + TenantManagementConfigModule, + BrowserModule, + BrowserAnimationsModule, + AppRoutingModule, + SharedModule, + + OAuthModule.forRoot(), + NgxsModule.forRoot([]), + + NgxsReduxDevtoolsPluginModule.forRoot(), + ], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts new file mode 100644 index 0000000000..367affb583 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { HomeComponent } from './home.component'; +import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; + +const routes: Routes = [ + { + path: '', + component: ApplicationLayoutComponent, + children: [{ path: '', component: HomeComponent }], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class HomeRoutingModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.html b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html new file mode 100644 index 0000000000..4fafc67398 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html @@ -0,0 +1,15 @@ +
+
{{ '::Welcome' | abpLocalization }}
+
+

+ {{ '::LongWelcomeMessage' | abpLocalization }} +

+

+ {{ 'AbpIdentity::Login' | abpLocalization }} +

+
+

abp.io

+
+
diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts new file mode 100644 index 0000000000..a42b960493 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { OAuthService } from 'angular-oauth2-oidc'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', +}) +export class HomeComponent { + get hasLoggedIn(): boolean { + return this.oAuthService.hasValidAccessToken(); + } + + constructor(private oAuthService: OAuthService) {} +} diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.module.ts b/npm/ng-packs/apps/dev-app/src/app/home/home.module.ts new file mode 100644 index 0000000000..72d20ccc65 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { HomeRoutingModule } from './home-routing.module'; +import { HomeComponent } from './home.component'; + +@NgModule({ + declarations: [HomeComponent], + imports: [SharedModule, HomeRoutingModule], +}) +export class HomeModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts new file mode 100644 index 0000000000..7130bb1a1a --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { AccountModule } from '@abp/ng.account'; + +@NgModule({ + imports: [AccountModule], +}) +export class AccountWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts new file mode 100644 index 0000000000..fed1dcd1ec --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { IdentityModule } from '@abp/ng.identity'; + +@NgModule({ + imports: [IdentityModule], +}) +export class IdentityWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts new file mode 100644 index 0000000000..63eabda505 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { TenantManagementModule } from '@abp/ng.tenant-management'; + +@NgModule({ + imports: [TenantManagementModule], +}) +export class TenantManagementWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts b/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts new file mode 100644 index 0000000000..6bae4ea332 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/shared/shared.module.ts @@ -0,0 +1,14 @@ +import { CoreModule } from '@abp/ng.core'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgModule } from '@angular/core'; +import { ThemeBasicModule } from '@abp/ng.theme.basic'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { TableModule } from 'primeng/table'; + +@NgModule({ + declarations: [], + imports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule], + exports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule], + providers: [], +}) +export class SharedModule {} diff --git a/npm/ng-packs/apps/dev-app/src/assets/.gitkeep b/npm/ng-packs/apps/dev-app/src/assets/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts b/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts new file mode 100644 index 0000000000..5b42ebace3 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts @@ -0,0 +1,25 @@ +export const environment = { + production: true, + hmr: false, + application: { + name: 'MyProjectName', + logoUrl: '', + }, + oAuthConfig: { + issuer: 'https://localhost:44305', + clientId: 'MyProjectName_App', + dummyClientSecret: '1q2w3e*', + scope: 'MyProjectName', + showDebugInformation: true, + oidc: false, + requireHttps: true, + }, + apis: { + default: { + url: 'https://localhost:44305', + }, + }, + localization: { + defaultResourceName: 'MyProjectName', + }, +}; diff --git a/npm/ng-packs/apps/dev-app/src/environments/environment.ts b/npm/ng-packs/apps/dev-app/src/environments/environment.ts new file mode 100644 index 0000000000..ca462ff043 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/environments/environment.ts @@ -0,0 +1,25 @@ +export const environment = { + production: false, + hmr: false, + application: { + name: 'MyProjectName', + logoUrl: '', + }, + oAuthConfig: { + issuer: 'https://localhost:44305', + clientId: 'MyProjectName_App', + dummyClientSecret: '1q2w3e*', + scope: 'MyProjectName', + showDebugInformation: true, + oidc: false, + requireHttps: true, + }, + apis: { + default: { + url: 'https://localhost:44305', + }, + }, + localization: { + defaultResourceName: 'MyProjectName', + }, +}; diff --git a/npm/ng-packs/apps/dev-app/src/favicon.ico b/npm/ng-packs/apps/dev-app/src/favicon.ico new file mode 100644 index 0000000000..997406ad22 Binary files /dev/null and b/npm/ng-packs/apps/dev-app/src/favicon.ico differ diff --git a/npm/ng-packs/apps/dev-app/src/index.html b/npm/ng-packs/apps/dev-app/src/index.html new file mode 100644 index 0000000000..b0c15da536 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/index.html @@ -0,0 +1,13 @@ + + + + + DevApp + + + + + + + + diff --git a/npm/ng-packs/apps/dev-app/src/main.ts b/npm/ng-packs/apps/dev-app/src/main.ts new file mode 100644 index 0000000000..fa4e0aef33 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/main.ts @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/npm/ng-packs/apps/dev-app/src/polyfills.ts b/npm/ng-packs/apps/dev-app/src/polyfills.ts new file mode 100644 index 0000000000..2f258e56c6 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/polyfills.ts @@ -0,0 +1,62 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/npm/ng-packs/apps/dev-app/src/styles.scss b/npm/ng-packs/apps/dev-app/src/styles.scss new file mode 100644 index 0000000000..90d4ee0072 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/npm/ng-packs/apps/dev-app/src/test.ts b/npm/ng-packs/apps/dev-app/src/test.ts new file mode 100644 index 0000000000..da9049dc63 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/test.ts @@ -0,0 +1,14 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/apps/dev-app/tsconfig.app.json b/npm/ng-packs/apps/dev-app/tsconfig.app.json new file mode 100644 index 0000000000..cc22d70d1c --- /dev/null +++ b/npm/ng-packs/apps/dev-app/tsconfig.app.json @@ -0,0 +1,38 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [], + "paths": { + "@abp/ng.core": ["packages/core/src/public-api.ts"], + "@abp/ng.core/*": ["packages/core/src/lib/*"], + // "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"], + // "@abp/ng.theme.shared/*": ["packages/theme-shared/src/lib/*"], + "@abp/ng.theme.basic": ["packages/theme-basic/src/public-api.ts"], + "@abp/ng.theme.basic/*": ["packages/theme-basic/src/lib/*"], + "@abp/ng.account": ["packages/account/src/public-api.ts"], + "@abp/ng.account/*": ["packages/account/src/lib/*"], + "@abp/ng.account.config": ["packages/account-config/src/public-api.ts"], + "@abp/ng.account.config/*": ["packages/account-config/src/lib/*"], + "@abp/ng.identity": ["packages/identity/src/public-api.ts"], + "@abp/ng.identity/*": ["packages/identity/src/lib/*"], + "@abp/ng.identity.config": ["packages/identity-config/src/public-api.ts"], + "@abp/ng.identity.config/*": ["packages/identity-config/src/lib/*"], + "@abp/ng.tenant-management": ["packages/tenant-management/src/public-api.ts"], + "@abp/ng.tenant-management/*": ["packages/tenant-management/src/lib/*"], + "@abp/ng.tenant-management.config": ["packages/tenant-management-config/src/public-api.ts"], + "@abp/ng.tenant-management.config/*": ["packages/tenant-management-config/src/lib/*"], + "@abp/ng.setting-management": ["packages/setting-management/src/public-api.ts"], + "@abp/ng.setting-management/*": ["packages/setting-management/src/lib/*"], + "@abp/ng.setting-management.config": ["packages/setting-management-config/src/public-api.ts"], + "@abp/ng.setting-management.config/*": ["packages/setting-management-config/src/lib/*"], + "@abp/ng.permission-management": ["packages/permission-management/src/public-api.ts"], + "@abp/ng.permission-management/*": ["packages/permission-management/src/lib/*"], + "@abp/ng.feature-management": ["packages/feature-management/src/public-api.ts"], + "@abp/ng.feature-management/*": ["packages/feature-management/src/lib/*"] + } + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.ts"], + "exclude": ["src/test.ts", "src/**/*.spec.ts"] +} diff --git a/npm/ng-packs/apps/dev-app/tsconfig.spec.json b/npm/ng-packs/apps/dev-app/tsconfig.spec.json new file mode 100644 index 0000000000..8eec07d40a --- /dev/null +++ b/npm/ng-packs/apps/dev-app/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": ["jasmine", "node"] + }, + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/npm/ng-packs/apps/dev-app/tslint.json b/npm/ng-packs/apps/dev-app/tslint.json new file mode 100644 index 0000000000..8006e74e96 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [true, "attribute", "app", "camelCase"], + "component-selector": [true, "element", "app", "kebab-case"] + } +} diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index b647954502..5e4a254863 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -1,41 +1,48 @@ { "scripts": { + "start": "ng serve dev-app", "ng": "ng", "symlink": "symlink", - "abpng": "abpng" + "abpng": "abpng", + "commit": "git-cz && node scripts/push.js", + "lint": "ng lint --fix" }, "devDependencies": { - "@abp/ng.core": "^0.8.3", - "@abp/ng.feature-management": "^0.8.3", - "@abp/ng.permission-management": "^0.8.3", - "@abp/ng.theme.shared": "^0.8.3", - "@angular-devkit/build-angular": "~0.803.5", - "@angular-devkit/build-ng-packagr": "~0.803.5", - "@angular/animations": "~8.2.7", - "@angular/cdk": "^8.0.1", - "@angular/cli": "~8.3.5", - "@angular/common": "~8.1.2", - "@angular/compiler": "~8.2.7", - "@angular/compiler-cli": "~8.2.7", - "@angular/core": "~8.1.2", - "@angular/forms": "~8.1.2", - "@angular/language-service": "~8.2.7", - "@angular/platform-browser": "~8.2.7", - "@angular/platform-browser-dynamic": "~8.2.7", - "@angular/router": "~8.1.2", + "@abp/ng.core": "^0.9.0", + "@abp/ng.feature-management": "^0.9.0", + "@abp/ng.permission-management": "^0.9.0", + "@abp/ng.theme.shared": "^0.9.0", + "@angular-devkit/build-angular": "~0.803.6", + "@angular-devkit/build-ng-packagr": "~0.803.6", + "@angular/animations": "~8.2.8", + "@angular/cdk": "^8.2.2", + "@angular/cli": "~8.3.6", + "@angular/common": "~8.2.8", + "@angular/compiler": "~8.2.8", + "@angular/compiler-cli": "~8.2.8", + "@angular/core": "~8.2.8", + "@angular/forms": "~8.2.8", + "@angular/language-service": "~8.2.8", + "@angular/platform-browser": "~8.2.8", + "@angular/platform-browser-dynamic": "~8.2.8", + "@angular/router": "~8.2.8", "@ng-bootstrap/ng-bootstrap": "^5.1.0", + "@ngneat/spectator": "^4.3.2", "@ngx-validate/core": "^0.0.5", - "@ngxs/router-plugin": "^3.5.0", - "@ngxs/storage-plugin": "^3.5.0", - "@ngxs/store": "^3.5.0", + "@ngxs/devtools-plugin": "^3.5.1", + "@ngxs/router-plugin": "^3.5.1", + "@ngxs/storage-plugin": "^3.5.1", + "@ngxs/store": "^3.5.1", "@types/jasmine": "~3.3.8", "@types/jasminewd2": "~2.0.3", "@types/node": "~8.9.4", "angular-oauth2-oidc": "^8.0.1", "bootstrap": "^4.3.1", "chart.js": "^2.8.0", - "codelyzer": "^5.0.0", + "codelyzer": "^5.1.2", + "cz-conventional-changelog": "3.0.2", "font-awesome": "^4.7.0", + "husky": "^3.0.7", "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", "just-clone": "3.1.0", @@ -45,19 +52,45 @@ "karma-coverage-istanbul-reporter": "~2.0.1", "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.4.0", + "lint-staged": "^9.4.0", "ng-packagr": "^5.5.0", "ngx-perfect-scrollbar": "^8.0.0", - "primeicons": "^1.0.0", + "ngxs-reset-plugin": "^1.2.0", + "prettier": "^1.18.2", + "primeicons": "^2.0.0", "primeng": "^8.0.0", "protractor": "~5.4.0", "rxjs": "~6.4.0", "snq": "^1.0.3", "symlink-manager": "^1.3.0", "ts-node": "~7.0.0", - "tsickle": "^0.35.0", - "tslib": "^1.9.0", - "tslint": "~5.15.0", + "tsickle": "^0.37.0", + "tslint": "~5.20.0", "typescript": "~3.5.3", "zone.js": "~0.9.1" + }, + "dependencies": { + "tslib": "^1.10.0" + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{ts,js,html,json,md,yaml,yml}": [ + "prettier --write", + "git add" + ], + "*.ts": [ + "prettier --write", + "tslint --fix", + "git add" + ] } } diff --git a/npm/ng-packs/packages/account-config/README.md b/npm/ng-packs/packages/account-config/README.md new file mode 100644 index 0000000000..9ace70206e --- /dev/null +++ b/npm/ng-packs/packages/account-config/README.md @@ -0,0 +1 @@ +# @abp/ng.account.config \ No newline at end of file diff --git a/npm/ng-packs/packages/account-config/karma.conf.js b/npm/ng-packs/packages/account-config/karma.conf.js new file mode 100644 index 0000000000..e74978b4ca --- /dev/null +++ b/npm/ng-packs/packages/account-config/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/account-config'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/account-config/ng-package.json b/npm/ng-packs/packages/account-config/ng-package.json new file mode 100644 index 0000000000..de9885883c --- /dev/null +++ b/npm/ng-packs/packages/account-config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/account-config", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/npm/ng-packs/packages/account-config/package.json b/npm/ng-packs/packages/account-config/package.json new file mode 100644 index 0000000000..4113d6e984 --- /dev/null +++ b/npm/ng-packs/packages/account-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "@abp/ng.account.config", + "version": "0.0.1", + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts b/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts new file mode 100644 index 0000000000..a94997e846 --- /dev/null +++ b/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts @@ -0,0 +1,37 @@ +import { CoreModule, noop } from '@abp/ng.core'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { APP_INITIALIZER, InjectionToken, ModuleWithProviders, NgModule } from '@angular/core'; +import { AccountConfigService } from './services/account-config.service'; + +export interface AccountConfigOptions { + redirectUrl?: string; +} + +export function accountOptionsFactory(options: AccountConfigOptions) { + return { + redirectUrl: '/', + ...options, + }; +} + +export const ACCOUNT_OPTIONS = new InjectionToken('ACCOUNT_OPTIONS'); + +@NgModule({ + imports: [CoreModule, ThemeSharedModule], + providers: [{ provide: APP_INITIALIZER, multi: true, deps: [AccountConfigService], useFactory: noop }], +}) +export class AccountConfigModule { + static forRoot(options = {} as AccountConfigOptions): ModuleWithProviders { + return { + ngModule: AccountConfigModule, + providers: [ + { provide: ACCOUNT_OPTIONS, useValue: options }, + { + provide: 'ACCOUNT_OPTIONS', + useFactory: accountOptionsFactory, + deps: [ACCOUNT_OPTIONS], + }, + ], + }; + } +} diff --git a/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts b/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts new file mode 100644 index 0000000000..7af86a49c9 --- /dev/null +++ b/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts @@ -0,0 +1,21 @@ +import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root', +}) +export class AccountConfigService { + constructor(private router: Router, private restService: RestService) { + ABP_ROUTES.push({ + name: 'AbpAccount::Menu:Account', + path: 'account', + invisible: true, + layout: eLayoutType.application, + children: [ + { path: 'login', name: 'AbpAccount::Login', order: 1 }, + { path: 'register', name: 'AbpAccount::Register', order: 2 }, + ], + }); + } +} diff --git a/npm/ng-packs/packages/account-config/src/public-api.ts b/npm/ng-packs/packages/account-config/src/public-api.ts new file mode 100644 index 0000000000..83270614c0 --- /dev/null +++ b/npm/ng-packs/packages/account-config/src/public-api.ts @@ -0,0 +1,2 @@ +export * from './lib/services/account-config.service'; +export * from './lib/account-config.module'; diff --git a/npm/ng-packs/packages/account-config/src/test.ts b/npm/ng-packs/packages/account-config/src/test.ts new file mode 100644 index 0000000000..978c64fb83 --- /dev/null +++ b/npm/ng-packs/packages/account-config/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/account-config/tsconfig.lib.json b/npm/ng-packs/packages/account-config/tsconfig.lib.json new file mode 100644 index 0000000000..bd23948e59 --- /dev/null +++ b/npm/ng-packs/packages/account-config/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/account-config/tsconfig.spec.json b/npm/ng-packs/packages/account-config/tsconfig.spec.json new file mode 100644 index 0000000000..16da33db07 --- /dev/null +++ b/npm/ng-packs/packages/account-config/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/account-config/tslint.json b/npm/ng-packs/packages/account-config/tslint.json new file mode 100644 index 0000000000..8c3919ea62 --- /dev/null +++ b/npm/ng-packs/packages/account-config/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "abp", + "camelCase" + ], + "component-selector": [ + true, + "element", + "abp", + "kebab-case" + ] + } +} diff --git a/npm/ng-packs/packages/account/src/lib/account.module.ts b/npm/ng-packs/packages/account/src/lib/account.module.ts index 8a790a21c1..d6b97fec84 100644 --- a/npm/ng-packs/packages/account/src/lib/account.module.ts +++ b/npm/ng-packs/packages/account/src/lib/account.module.ts @@ -18,6 +18,10 @@ import { ACCOUNT_OPTIONS, optionsFactory } from './tokens/options.token'; }) export class AccountModule {} +/** + * + * @deprecated since version 0.9 + */ export function AccountProviders(options = {} as Options): Provider[] { return [ { provide: ACCOUNT_OPTIONS, useValue: options }, diff --git a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts index a1ba6004c5..c114095adc 100644 --- a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts @@ -9,10 +9,14 @@ import { AccountService } from '../../services/account.service'; @Component({ selector: 'abp-tenant-box', - templateUrl: './tenant-box.component.html', + templateUrl: './tenant-box.component.html' }) export class TenantBoxComponent implements OnInit { - constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {} + constructor( + private store: Store, + private toasterService: ToasterService, + private accountService: AccountService + ) {} tenant = {} as ABP.BasicItem; @@ -21,7 +25,9 @@ export class TenantBoxComponent implements OnInit { isModalVisible: boolean; ngOnInit() { - this.tenant = this.store.selectSnapshot(SessionState.getTenant) || ({} as ABP.BasicItem); + this.tenant = + this.store.selectSnapshot(SessionState.getTenant) || + ({} as ABP.BasicItem); this.tenantName = this.tenant.name || ''; } @@ -37,24 +43,31 @@ export class TenantBoxComponent implements OnInit { take(1), catchError(err => { this.toasterService.error( - snq(() => err.error.error_description, 'AbpUi::DefaultErrorMessage'), - 'AbpUi::Error', + snq( + () => err.error.error_description, + 'AbpUi::DefaultErrorMessage' + ), + 'AbpUi::Error' ); return throwError(err); - }), + }) ) .subscribe(({ success, tenantId }) => { if (success) { this.tenant = { id: tenantId, - name: this.tenant.name, + name: this.tenant.name }; this.tenantName = this.tenant.name; this.isModalVisible = false; } else { - this.toasterService.error(`AbpUiMultiTenancy::GivenTenantIsNotAvailable`, 'AbpUi::Error', { - messageLocalizationParams: [this.tenant.name], - }); + this.toasterService.error( + 'AbpUiMultiTenancy::GivenTenantIsNotAvailable', + 'AbpUi::Error', + { + messageLocalizationParams: [this.tenant.name] + } + ); this.tenant = {} as ABP.BasicItem; } this.store.dispatch(new SetTenant(success ? this.tenant : null)); diff --git a/npm/ng-packs/packages/account/src/lib/constants/routes.ts b/npm/ng-packs/packages/account/src/lib/constants/routes.ts index 762d10c313..119bde1e7f 100644 --- a/npm/ng-packs/packages/account/src/lib/constants/routes.ts +++ b/npm/ng-packs/packages/account/src/lib/constants/routes.ts @@ -1,5 +1,9 @@ import { ABP, eLayoutType } from '@abp/ng.core'; +/** + * + * @deprecated since version 0.9 + */ export const ACCOUNT_ROUTES = { routes: [ { diff --git a/npm/ng-packs/packages/account/src/lib/services/account.service.ts b/npm/ng-packs/packages/account/src/lib/services/account.service.ts index ce90f7c6e0..5061cfeea1 100644 --- a/npm/ng-packs/packages/account/src/lib/services/account.service.ts +++ b/npm/ng-packs/packages/account/src/lib/services/account.service.ts @@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core'; import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class AccountService { constructor(private rest: RestService) {} @@ -12,7 +12,7 @@ export class AccountService { findTenant(tenantName: string): Observable { const request: Rest.Request = { method: 'GET', - url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`, + url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}` }; return this.rest.request(request); @@ -21,8 +21,8 @@ export class AccountService { register(body: RegisterRequest): Observable { const request: Rest.Request = { method: 'POST', - url: `/api/account/register`, - body, + url: '/api/account/register', + body }; return this.rest.request(request, { skipHandleError: true }); diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts b/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts index 6cc720b011..4614011b92 100644 --- a/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts +++ b/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts @@ -1,13 +1,11 @@ import { ControlValueAccessor } from '@angular/forms'; import { ChangeDetectorRef, Component, Injector, Input, Type } from '@angular/core'; -@Component({ template: '' }) +@Component({ selector: 'abp-abstract-ng-model', template: '' }) export class AbstractNgModelComponent implements ControlValueAccessor { - @Input() - disabled: boolean; + @Input() disabled: boolean; - @Input() - set value(value: T) { + @Input() set value(value: T) { this._value = value; this.notifyValueChange(); } diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 876c476d5f..40954268e8 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -13,21 +13,19 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; selector: 'abp-dynamic-layout', template: ` - - `, + ` }) export class DynamicLayoutComponent implements OnDestroy { - @Select(ConfigState.getOne('requirements')) - requirements$: Observable; + @Select(ConfigState.getOne('requirements')) requirements$: Observable; layout: Type; constructor(private router: Router, private route: ActivatedRoute, private store: Store) { const { requirements: { layouts }, - routes, + routes } = this.store.selectSnapshot(ConfigState.getAll); if ((this.route.snapshot.data || {}).layout) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts index edd7c8497a..7fb7de34d1 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts @@ -1,11 +1,12 @@ import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core'; @Directive({ - selector: '[autofocus]', + // tslint:disable-next-line: directive-selector + selector: '[autofocus]' }) export class AutofocusDirective implements AfterViewInit { @Input('autofocus') - delay: number = 0; + delay = 0; constructor(private elRef: ElementRef) {} diff --git a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts index 144c43d4e2..fc29b5fd65 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts @@ -4,12 +4,13 @@ import { debounceTime } from 'rxjs/operators'; import { takeUntilDestroy } from '@ngx-validate/core'; @Directive({ - selector: '[input.debounce]', + // tslint:disable-next-line: directive-selector + selector: '[input.debounce]' }) export class InputEventDebounceDirective implements OnInit { - @Input() debounce: number = 300; + @Input() debounce = 300; - @Output('input.debounce') debounceEvent = new EventEmitter(); + @Output('input.debounce') readonly debounceEvent = new EventEmitter(); constructor(private renderer: Renderer2, private el: ElementRef) {} @@ -17,7 +18,7 @@ export class InputEventDebounceDirective implements OnInit { fromEvent(this.el.nativeElement, 'input') .pipe( debounceTime(this.debounce), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe((event: Event) => { this.debounceEvent.emit(event); diff --git a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts index 604f7a8660..932ff11b8e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts @@ -9,7 +9,7 @@ import { OnChanges, TemplateRef, TrackByFunction, - ViewContainerRef, + ViewContainerRef } from '@angular/core'; import compare from 'just-compare'; import clone from 'just-clone'; @@ -25,7 +25,7 @@ class RecordView { } @Directive({ - selector: '[abpFor]', + selector: '[abpFor]' }) export class ForDirective implements OnChanges { @Input('abpForOf') @@ -67,7 +67,7 @@ export class ForDirective implements OnChanges { constructor( private tempRef: TemplateRef, private vcRef: ViewContainerRef, - private differs: IterableDiffers, + private differs: IterableDiffers ) {} private iterateOverAppliedOperations(changes: IterableChanges) { @@ -78,7 +78,7 @@ export class ForDirective implements OnChanges { const view = this.vcRef.createEmbeddedView( this.tempRef, new AbpForContext(null, -1, -1, this.items), - currentIndex, + currentIndex ); rw.push(new RecordView(record, view)); @@ -113,8 +113,11 @@ export class ForDirective implements OnChanges { private projectItems(items: any[]): void { if (!items.length && this.emptyRef) { + this.vcRef.clear(); + // tslint:disable-next-line: no-unused-expression this.vcRef.createEmbeddedView(this.emptyRef).rootNodes; this.isShowEmptyRef = true; + this.differ = null; return; } diff --git a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts index c77016ce7f..00982bbbb9 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts @@ -7,7 +7,7 @@ import { OnDestroy, OnInit, Output, - Self, + Self } from '@angular/core'; import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms'; import { fromEvent } from 'rxjs'; @@ -17,21 +17,21 @@ import { takeUntilDestroy } from '../utils'; type Controls = { [key: string]: FormControl } | FormGroup[]; @Directive({ - selector: 'form[ngSubmit][formGroup]', + // tslint:disable-next-line: directive-selector + selector: 'form[ngSubmit][formGroup]' }) export class FormSubmitDirective implements OnInit, OnDestroy { @Input() notValidateOnSubmit: string | boolean; - @Output() - ngSubmit = new EventEmitter(); + @Output() readonly ngSubmit = new EventEmitter(); - executedNgSubmit: boolean = false; + executedNgSubmit = false; constructor( @Self() private formGroupDirective: FormGroupDirective, private host: ElementRef, - private cdRef: ChangeDetectorRef, + private cdRef: ChangeDetectorRef ) {} ngOnInit() { @@ -44,7 +44,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy { .pipe( debounceTime(200), filter((key: KeyboardEvent) => key && key.key === 'Enter'), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(() => { if (!this.executedNgSubmit) { @@ -57,7 +57,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy { fromEvent(this.host.nativeElement, 'submit') .pipe( takeUntilDestroy(this), - filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string'), + filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string') ) .subscribe(() => { if (!this.executedNgSubmit) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts index ffa2e9e10b..7b9ae46e9c 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts @@ -3,10 +3,11 @@ import { fromEvent } from 'rxjs'; import { takeUntilDestroy } from '@ngx-validate/core'; @Directive({ - selector: '[click.stop]', + // tslint:disable-next-line: directive-selector + selector: '[click.stop]' }) export class ClickEventStopPropagationDirective implements OnInit { - @Output('click.stop') stopPropEvent = new EventEmitter(); + @Output('click.stop') readonly stopPropEvent = new EventEmitter(); constructor(private renderer: Renderer2, private el: ElementRef) {} diff --git a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts index 56b49f23ee..2428b736a3 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts @@ -3,14 +3,14 @@ import { Subject } from 'rxjs'; import snq from 'snq'; @Directive({ - selector: '[abpVisibility]', + selector: '[abpVisibility]' }) export class VisibilityDirective implements AfterViewInit { @Input('abpVisibility') focusedElement: HTMLElement; @Input() - mutationObserverEnabled: boolean = true; + mutationObserverEnabled = true; completed$ = new Subject(); @@ -25,7 +25,7 @@ export class VisibilityDirective implements AfterViewInit { const htmlNodes = snq( () => Array.from(mutation.target.childNodes).filter(node => node instanceof HTMLElement), - [], + [] ); if (!htmlNodes.length) { @@ -40,13 +40,13 @@ export class VisibilityDirective implements AfterViewInit { }); observer.observe(this.focusedElement, { - childList: true, + childList: true }); } else { setTimeout(() => { const htmlNodes = snq( () => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), - [], + [] ); if (!htmlNodes.length) this.removeFromDOM(); diff --git a/npm/ng-packs/packages/core/src/lib/enums/common.ts b/npm/ng-packs/packages/core/src/lib/enums/common.ts index 3b953b8ac7..1ecc29406c 100644 --- a/npm/ng-packs/packages/core/src/lib/enums/common.ts +++ b/npm/ng-packs/packages/core/src/lib/enums/common.ts @@ -2,5 +2,8 @@ export const enum eLayoutType { account = 'account', application = 'application', empty = 'empty', + /** + * @deprecated since version 0.9.0 + */ setting = 'setting', } diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index a5089760f0..95bf490e91 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -43,4 +43,8 @@ export namespace ABP { id: string; name: string; } + + export interface Dictionary { + [key: string]: T; + } } diff --git a/npm/ng-packs/packages/core/src/lib/models/config.ts b/npm/ng-packs/packages/core/src/lib/models/config.ts index 8238864b2f..e6c9a05f35 100644 --- a/npm/ng-packs/packages/core/src/lib/models/config.ts +++ b/npm/ng-packs/packages/core/src/lib/models/config.ts @@ -30,4 +30,9 @@ export namespace Config { export interface Requirements { layouts: Type[]; } + + export interface LocalizationWithDefault { + key: string; + defaultValue: string; + } } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/index.ts b/npm/ng-packs/packages/core/src/lib/pipes/index.ts index 4802a25ca4..eb028eee4f 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/index.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/index.ts @@ -1,2 +1,2 @@ export * from './localization.pipe'; -export * from "./sort.pipe"; +export * from './sort.pipe'; diff --git a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts index 0766a50091..b18a12946f 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts @@ -1,45 +1,20 @@ -import { Pipe, PipeTransform, OnDestroy } from '@angular/core'; +import { Pipe, PipeTransform } from '@angular/core'; import { Store } from '@ngxs/store'; +import { Config } from '../models'; import { ConfigState } from '../states'; -import { takeUntilDestroy } from '../utils'; -import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; @Pipe({ name: 'abpLocalization', - pure: false, // required to update the value }) -export class LocalizationPipe implements PipeTransform, OnDestroy { - initialValue: string = ''; - - value: string; - - destroy$ = new Subject(); - +export class LocalizationPipe implements PipeTransform { constructor(private store: Store) {} - transform(value: string = '', ...interpolateParams: string[]): string { - if (this.initialValue !== value) { - this.initialValue = value; - this.destroy$.next(); - - this.store - .select( - ConfigState.getCopy( - value, - ...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []), - ), - ) - .pipe( - takeUntil(this.destroy$), - takeUntilDestroy(this), - distinctUntilChanged(), - ) - .subscribe(copy => (this.value = copy)); - } - - return this.value; + transform(value: string | Config.LocalizationWithDefault = '', ...interpolateParams: string[]): string { + return this.store.selectSnapshot( + ConfigState.getLocalization( + value, + ...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []), + ), + ); } - - ngOnDestroy() {} } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts index b44de74193..09cf1d7147 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts @@ -1,13 +1,14 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'abpSort', - pure: false + name: 'abpSort', + // tslint:disable-next-line: no-pipe-impure + pure: false }) export class SortPipe implements PipeTransform { - transform(value: any[], sortOrder: string): any { - sortOrder = sortOrder.toLowerCase(); - if(sortOrder === "desc") return value.reverse(); - else return value; - } -} \ No newline at end of file + transform(value: any[], sortOrder: string): any { + sortOrder = sortOrder.toLowerCase(); + if (sortOrder === 'desc') return value.reverse(); + else return value; + } +} diff --git a/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts b/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts index 5add16c31e..2c15787f84 100644 --- a/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts +++ b/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts @@ -8,9 +8,11 @@ import clone from 'just-clone'; export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS'); +export let ABP_ROUTES = [] as ABP.FullRoute[]; + @Injectable() export class ConfigPlugin implements NgxsPlugin { - private initialized: boolean = false; + private initialized = false; constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, private router: Router) {} @@ -18,9 +20,11 @@ export class ConfigPlugin implements NgxsPlugin { const matches = actionMatcher(event); const isInitAction = matches(InitState) || matches(UpdateState); - // const layouts = snq(() => this.options.requirements.layouts.filter(layout => layout instanceof Type), []); if (isInitAction && !this.initialized) { - let { routes, wrappers } = transformRoutes(this.router.config); + const transformedRoutes = transformRoutes(this.router.config); + let { routes } = transformedRoutes; + const { wrappers } = transformedRoutes; + routes = organizeRoutes(routes, wrappers); const flattedRoutes = flatRoutes(clone(routes)); state = setValue(state, 'ConfigState', { @@ -38,27 +42,32 @@ export class ConfigPlugin implements NgxsPlugin { } function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): any { + /** + * + * @deprecated since version 0.9.0 + */ const abpRoutes: ABP.FullRoute[] = routes .filter(route => { return snq(() => route.data.routes.routes.find(r => r.path === route.path), false); }) .reduce((acc, val) => [...acc, ...val.data.routes.routes], []); + // tslint:disable-next-line: deprecation + ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes]; - wrappers = abpRoutes.filter(ar => ar.wrapper); + wrappers = ABP_ROUTES.filter(ar => ar.wrapper); const transformed = [] as ABP.FullRoute[]; routes - .filter(route => (route.data || {}).routes && (route.component || route.loadChildren)) + .filter(route => route.component || route.loadChildren) .forEach(route => { - const abpPackage = abpRoutes.find( - abp => abp.path.toLowerCase() === route.path.toLowerCase() && snq(() => route.data.routes.routes.length, false), - ); + const abpPackage = ABP_ROUTES.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper); + const { length } = transformed; if (abpPackage) { transformed.push(abpPackage); } - if (transformed.length === length) { + if (transformed.length === length && (route.data || {}).routes) { transformed.push({ ...route.data.routes, path: route.path, diff --git a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts index d0a653e1e0..5081f94068 100644 --- a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { ApplicationConfiguration, Rest } from '../models'; +import { Rest } from '../models/rest'; +import { ApplicationConfiguration } from '../models/application-configuration'; import { RestService } from './rest.service'; @Injectable({ diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 2af99ee227..d3dd746fa6 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -1,9 +1,8 @@ -import { Injectable, Optional, SkipSelf, NgZone } from '@angular/core'; +import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Actions, Store } from '@ngxs/store'; +import { Store } from '@ngxs/store'; import { noop, Observable } from 'rxjs'; import { ConfigState } from '../states/config.state'; -import { SessionState } from '../states/session.state'; import { registerLocale } from '../utils/initial-utils'; type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; @@ -11,14 +10,13 @@ type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSna @Injectable({ providedIn: 'root' }) export class LocalizationService { get currentLang(): string { - return this.store.selectSnapshot(SessionState.getLanguage); + return this.store.selectSnapshot(state => state.SessionState.language); } constructor( private store: Store, private router: Router, private ngZone: NgZone, - private actions: Actions, @Optional() @SkipSelf() otherInstance: LocalizationService, @@ -26,13 +24,12 @@ export class LocalizationService { if (otherInstance) throw new Error('LocaleService should have only one instance.'); } - private setRouteReuse(reuse: ShouldReuseRoute) { + setRouteReuse(reuse: ShouldReuseRoute) { this.router.routeReuseStrategy.shouldReuseRoute = reuse; } registerLocale(locale: string) { const { shouldReuseRoute } = this.router.routeReuseStrategy; - this.setRouteReuse(() => false); this.router.navigated = false; @@ -44,11 +41,11 @@ export class LocalizationService { }); } - get(keys: string, ...interpolateParams: string[]): Observable { - return this.store.select(ConfigState.getCopy(keys, ...interpolateParams)); + get(key: string, ...interpolateParams: string[]): Observable { + return this.store.select(ConfigState.getLocalization(key, ...interpolateParams)); } - instant(keys: string, ...interpolateParams: string[]): string { - return this.store.selectSnapshot(ConfigState.getCopy(keys, ...interpolateParams)); + instant(key: string, ...interpolateParams: string[]): string { + return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams)); } } diff --git a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts index 7ff1a42af0..611b208b35 100644 --- a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts @@ -1,11 +1,11 @@ import { HttpClient, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Store } from '@ngxs/store'; -import { NEVER, Observable, throwError } from 'rxjs'; +import { Observable, throwError } from 'rxjs'; import { catchError, take } from 'rxjs/operators'; +import { RestOccurError } from '../actions/rest.actions'; import { Rest } from '../models/rest'; -import { ConfigState } from '../states'; -import { RestOccurError } from '../actions'; +import { ConfigState } from '../states/config.state'; @Injectable({ providedIn: 'root', diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 8225aaea2a..f454be2872 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -1,10 +1,11 @@ import { State, Selector, createSelector, Action, StateContext, Store } from '@ngxs/store'; -import { Config, ABP } from '../models'; +import { Config } from '../models/config'; +import { ABP } from '../models/common'; import { GetAppConfiguration, PatchRouteByName } from '../actions/config.actions'; import { ApplicationConfigurationService } from '../services/application-configuration.service'; import { tap, switchMap } from 'rxjs/operators'; import snq from 'snq'; -import { SetLanguage } from '../actions'; +import { SetLanguage } from '../actions/session.actions'; import { SessionState } from './session.state'; import { of } from 'rxjs'; import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils'; @@ -27,7 +28,7 @@ export class ConfigState { static getOne(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return state[key]; }, ); @@ -46,7 +47,7 @@ export class ConfigState { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return (keys as string[]).reduce((acc, val) => { if (acc) { return acc[val]; @@ -63,7 +64,7 @@ export class ConfigState { static getRoute(path?: string, name?: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { const { flattedRoutes } = state; return (flattedRoutes as ABP.FullRoute[]).find(route => { if (path && route.path === path) { @@ -81,7 +82,7 @@ export class ConfigState { static getApiUrl(key?: string) { const selector = createSelector( [ConfigState], - function(state: Config.State): string { + (state: Config.State): string => { return state.environment.apis[key || 'default'].url; }, ); @@ -92,7 +93,7 @@ export class ConfigState { static getSetting(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return snq(() => state.setting.values[key]); }, ); @@ -103,7 +104,7 @@ export class ConfigState { static getGrantedPolicy(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State): boolean { + (state: Config.State): boolean => { if (!key) return true; return snq(() => state.auth.grantedPolicies[key], false); }, @@ -112,20 +113,24 @@ export class ConfigState { return selector; } + /** + * + * @deprecated, Use getLocalization instead. To be delete in v1 + */ static getCopy(key: string, ...interpolateParams: string[]) { if (!key) key = ''; const keys = key.split('::') as string[]; const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { if (!state.localization) return key; const { defaultResourceName } = state.environment.localization; if (keys[0] === '') { if (!defaultResourceName) { throw new Error( - `Please check your environment. May you forget set defaultResourceName? + `Please check your environment. May you forget set defaultResourceName? Here is the example: { production: false, localization: { @@ -160,6 +165,61 @@ export class ConfigState { return selector; } + static getLocalization(key: string | Config.LocalizationWithDefault, ...interpolateParams: string[]) { + let defaultValue: string; + + if (typeof key !== 'string') { + defaultValue = key.defaultValue; + key = key.key; + } + + if (!key) key = ''; + + const keys = key.split('::') as string[]; + const selector = createSelector( + [ConfigState], + (state: Config.State) => { + if (!state.localization) return defaultValue || key; + + const { defaultResourceName } = state.environment.localization; + if (keys[0] === '') { + if (!defaultResourceName) { + throw new Error( + `Please check your environment. May you forget set defaultResourceName? + Here is the example: + { production: false, + localization: { + defaultResourceName: 'MyProjectName' + } + }`, + ); + } + + keys[0] = snq(() => defaultResourceName); + } + + let localization = (keys as any).reduce((acc, val) => { + if (acc) { + return acc[val]; + } + + return undefined; + }, state.localization.values); + + interpolateParams = interpolateParams.filter(params => params != null); + if (localization && interpolateParams && interpolateParams.length) { + interpolateParams.forEach(param => { + localization = localization.replace(/[\'\"]?\{[\d]+\}[\'\"]?/, param); + }); + } + + return localization || defaultValue || key; + }, + ); + + return selector; + } + constructor(private appConfigurationService: ApplicationConfigurationService, private store: Store) {} @Action(GetAppConfiguration) diff --git a/npm/ng-packs/packages/core/src/lib/states/session.state.ts b/npm/ng-packs/packages/core/src/lib/states/session.state.ts index 0754b0d62b..544bd42b2b 100644 --- a/npm/ng-packs/packages/core/src/lib/states/session.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/session.state.ts @@ -4,6 +4,7 @@ import { ABP, Session } from '../models'; import { GetAppConfiguration } from '../actions/config.actions'; import { LocalizationService } from '../services/localization.service'; import { from, combineLatest } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; @State({ name: 'SessionState', @@ -28,7 +29,9 @@ export class SessionState { language: payload, }); - return combineLatest([dispatch(new GetAppConfiguration()), from(this.localizationService.registerLocale(payload))]); + return dispatch(new GetAppConfiguration()).pipe( + switchMap(() => from(this.localizationService.registerLocale(payload))), + ); } @Action(SetTenant) diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts new file mode 100644 index 0000000000..1cd4266924 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts @@ -0,0 +1,343 @@ +import { RouterTestingModule } from '@angular/router/testing'; +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { NGXS_PLUGINS, NgxsModule, InitState, Store } from '@ngxs/store'; +import { environment } from '../../../../../apps/dev-app/src/environments/environment'; +import { LAYOUTS } from '../../../../theme-basic/src/public-api'; +import { ABP } from '../models'; +import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, ABP_ROUTES } from '../plugins'; +import { RouterOutletComponent } from '../components'; +import { ConfigState } from '../states'; +import { CoreModule } from '../core.module'; +import { eLayoutType } from '../enums/common'; + +ABP_ROUTES.push( + { + name: 'AbpUiNavigation::Menu:Administration', + path: '', + order: 1, + wrapper: true, + }, + { + name: 'AbpIdentity::Menu:IdentityManagement', + path: 'identity', + order: 1, + parentName: 'AbpUiNavigation::Menu:Administration', + layout: eLayoutType.application, + iconClass: 'fa fa-id-card-o', + children: [ + { path: 'roles', name: 'AbpIdentity::Roles', order: 2, requiredPolicy: 'AbpIdentity.Roles' }, + { path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' }, + ], + }, + { + name: 'AbpAccount::Menu:Account', + path: 'account', + invisible: true, + layout: eLayoutType.application, + children: [ + { path: 'login', name: 'AbpAccount::Login', order: 1 }, + { path: 'register', name: 'AbpAccount::Register', order: 2 }, + ], + }, + { + name: 'AbpTenantManagement::Menu:TenantManagement', + path: 'tenant-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: eLayoutType.application, + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + }, + ], + }, +); + +const expectedState = { + environment, + requirements: { + layouts: LAYOUTS, + }, + routes: [ + { + name: '::Menu:Home', + path: '', + children: [], + url: '/', + }, + { + name: 'AbpAccount::Menu:Account', + path: 'account', + invisible: true, + layout: 'application', + children: [ + { + path: 'login', + name: 'AbpAccount::Login', + order: 1, + url: '/account/login', + }, + { + path: 'register', + name: 'AbpAccount::Register', + order: 2, + url: '/account/register', + }, + ], + url: '/account', + }, + { + name: 'AbpUiNavigation::Menu:Administration', + path: '', + order: 1, + wrapper: true, + children: [ + { + name: 'AbpIdentity::Menu:IdentityManagement', + path: 'identity', + order: 1, + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-id-card-o', + children: [ + { + path: 'users', + name: 'AbpIdentity::Users', + order: 1, + requiredPolicy: 'AbpIdentity.Users', + url: '/identity/users', + }, + { + path: 'roles', + name: 'AbpIdentity::Roles', + order: 2, + requiredPolicy: 'AbpIdentity.Roles', + url: '/identity/roles', + }, + ], + url: '/identity', + }, + { + name: 'AbpTenantManagement::Menu:TenantManagement', + path: 'tenant-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + url: '/tenant-management/tenants', + }, + ], + url: '/tenant-management', + }, + ], + }, + ], + flattedRoutes: [ + { + name: '::Menu:Home', + path: '', + children: [], + url: '/', + }, + { + name: 'AbpAccount::Menu:Account', + path: 'account', + invisible: true, + layout: 'application', + children: [ + { + path: 'login', + name: 'AbpAccount::Login', + order: 1, + url: '/account/login', + }, + { + path: 'register', + name: 'AbpAccount::Register', + order: 2, + url: '/account/register', + }, + ], + url: '/account', + }, + { + path: 'login', + name: 'AbpAccount::Login', + order: 1, + url: '/account/login', + }, + { + path: 'register', + name: 'AbpAccount::Register', + order: 2, + url: '/account/register', + }, + { + name: 'AbpUiNavigation::Menu:Administration', + path: '', + order: 1, + wrapper: true, + children: [ + { + name: 'AbpIdentity::Menu:IdentityManagement', + path: 'identity', + order: 1, + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-id-card-o', + children: [ + { + path: 'users', + name: 'AbpIdentity::Users', + order: 1, + requiredPolicy: 'AbpIdentity.Users', + url: '/identity/users', + }, + { + path: 'roles', + name: 'AbpIdentity::Roles', + order: 2, + requiredPolicy: 'AbpIdentity.Roles', + url: '/identity/roles', + }, + ], + url: '/identity', + }, + { + name: 'AbpTenantManagement::Menu:TenantManagement', + path: 'tenant-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + url: '/tenant-management/tenants', + }, + ], + url: '/tenant-management', + }, + ], + }, + { + name: 'AbpIdentity::Menu:IdentityManagement', + path: 'identity', + order: 1, + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-id-card-o', + children: [ + { + path: 'users', + name: 'AbpIdentity::Users', + order: 1, + requiredPolicy: 'AbpIdentity.Users', + url: '/identity/users', + }, + { + path: 'roles', + name: 'AbpIdentity::Roles', + order: 2, + requiredPolicy: 'AbpIdentity.Roles', + url: '/identity/roles', + }, + ], + url: '/identity', + }, + { + path: 'users', + name: 'AbpIdentity::Users', + order: 1, + requiredPolicy: 'AbpIdentity.Users', + url: '/identity/users', + }, + { + path: 'roles', + name: 'AbpIdentity::Roles', + order: 2, + requiredPolicy: 'AbpIdentity.Roles', + url: '/identity/roles', + }, + { + name: 'AbpTenantManagement::Menu:TenantManagement', + path: 'tenant-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: 'application', + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + url: '/tenant-management/tenants', + }, + ], + url: '/tenant-management', + }, + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + url: '/tenant-management/tenants', + }, + ], +}; + +describe('ConfigPlugin', () => { + let spectator: SpectatorService; + const createService = createServiceFactory({ + service: ConfigPlugin, + imports: [ + CoreModule, + NgxsModule.forRoot([]), + RouterTestingModule.withRoutes([ + { + path: '', + component: RouterOutletComponent, + data: { + routes: { + name: '::Menu:Home', + } as ABP.Route, + }, + }, + { path: 'identity', component: RouterOutletComponent }, + { path: 'account', component: RouterOutletComponent }, + { path: 'tenant-management', component: RouterOutletComponent }, + ]), + ], + providers: [ + { + provide: NGXS_PLUGINS, + useClass: ConfigPlugin, + multi: true, + }, + { + provide: NGXS_CONFIG_PLUGIN_OPTIONS, + useValue: { environment, requirements: { layouts: LAYOUTS } } as ABP.Root, + }, + ], + }); + + beforeEach(() => { + spectator = createService(); + }); + + it('should ConfigState must be create with correct datas', () => { + const store = spectator.get(Store); + const state = store.selectSnapshot(ConfigState); + expect(state).toEqual(expectedState); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts new file mode 100644 index 0000000000..bfd2762760 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -0,0 +1,157 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator'; +import { NgxsModule, Store } from '@ngxs/store'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin'; +import { LAYOUTS, ThemeBasicModule } from '../../../../theme-basic/src/public-api'; +import { eLayoutType } from '../enums'; +import { ABP } from '../models'; +import { RouterOutletComponent, CoreModule, DynamicLayoutComponent, ConfigState } from '@abp/ng.core'; +import { ThemeSharedModule } from '../../../../theme-shared/src/public-api'; +import { MessageService } from 'primeng/components/common/messageservice'; + +@Component({ + selector: 'abp-dummy', + template: '{{route.snapshot.data?.name}} works!', +}) +class DummyComponent { + constructor(public route: ActivatedRoute) {} +} + +describe('DynamicLayoutComponent', () => { + const createComponent = createRoutingFactory({ + component: RouterOutletComponent, + declareComponent: false, + imports: [ + CoreModule, + NgxsModule.forRoot([ConfigState]), + NgxsResetPluginModule.forRoot(), + ThemeSharedModule, + ThemeBasicModule, + ], + declarations: [DummyComponent], + entryComponents: [], + stubsEnabled: false, + providers: [MessageService, { provide: OAuthService, useValue: { getAccessToken: () => true } }], + routes: [ + { path: '', component: RouterOutletComponent }, + { + path: 'parentWithLayout', + component: DynamicLayoutComponent, + children: [ + { + path: 'childWithoutLayout', + component: DummyComponent, + data: { name: 'childWithoutLayout' }, + }, + { + path: 'childWithLayout', + component: DummyComponent, + data: { name: 'childWithLayout' }, + }, + ], + }, + { + path: 'withData', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withData' }, + }, + ], + data: { layout: eLayoutType.empty }, + }, + { + path: 'withoutLayout', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withoutLayout' }, + }, + ], + data: { layout: null }, + }, + ], + }); + + let spectator: SpectatorRouting; + let store: SpyObject; + + beforeEach(async () => { + spectator = createComponent(); + store = spectator.get(Store); + store.dispatch( + new StateOverwrite([ + ConfigState, + { + requirements: { layouts: LAYOUTS }, + routes: [ + { + path: '', + wrapper: true, + children: [ + { + path: 'parentWithLayout', + layout: eLayoutType.application, + children: [{ path: 'childWithoutLayout' }, { path: 'childWithLayout', layout: eLayoutType.account }], + }, + ], + }, + { path: 'withData', layout: eLayoutType.application }, + , + ] as ABP.FullRoute[], + environment: { application: {} }, + }, + ]), + ); + }); + + it('should handle application layout from parent abp route and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-dynamic-layout')).toBeTruthy(); + expect(spectator.query('abp-layout-application')).toBeTruthy(); + }); + + it('should handle account layout from own property and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-account')).toBeTruthy(); + }); + + it('should handle empty layout from route data and display it', async () => { + spectator.router.navigateByUrl('/withData'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should display empty layout when layout is null', async () => { + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should not display any layout when layouts are empty', async () => { + store.dispatch( + new StateOverwrite([ConfigState, { ...store.selectSnapshot(ConfigState), requirements: { layouts: [] } }]), + ); + + spectator.detectChanges(); + + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + + expect(spectator.query('abp-layout-empty')).toBeFalsy(); + expect(spectator.query('abp-dynamic-layout').children[0].tagName).toEqual('ROUTER-OUTLET'); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts new file mode 100644 index 0000000000..3fbcab3493 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts @@ -0,0 +1,37 @@ +import { Component, LOCALE_ID } from '@angular/core'; +import { createRoutingFactory, SpectatorHost, SpectatorRouting } from '@ngneat/spectator'; +import { LocalizationService } from '../services'; +import { LocaleProvider, LocaleId } from '../providers'; +import localesMapping from '../constants/different-locales'; + +@Component({ selector: 'abp-dummy', template: '' }) +export class DummyComponent {} + +describe('LocaleProvider', () => { + let spectator: SpectatorRouting; + + const createComponent = createRoutingFactory({ + component: DummyComponent, + stubsEnabled: false, + providers: [ + { provide: LocalizationService, useValue: { currentLang: 'en-US' } }, + { + provide: LOCALE_ID, + useClass: LocaleId, + deps: [LocalizationService], + }, + ], + }); + + describe('#LOCALE_ID', () => { + it('should equal to localizationService.currentLang', async () => { + spectator = createComponent(); + const localizationService = spectator.get(LocalizationService); + + expect(spectator.get(LOCALE_ID).valueOf()).toBe(localesMapping['en-US'] || 'en-US'); + + localizationService.currentLang = 'tr'; + expect(spectator.get(LOCALE_ID).valueOf()).toBe(localesMapping['tr'] || 'tr'); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts new file mode 100644 index 0000000000..c279fb6da2 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts @@ -0,0 +1,81 @@ +import { Router } from '@angular/router'; +import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator'; +import { Store } from '@ngxs/store'; +import { Observable, of } from 'rxjs'; +import { LocalizationService } from '../services/localization.service'; + +describe('LocalizationService', () => { + let spectator: SpectatorService; + let store: SpyObject; + let service: LocalizationService; + + const createService = createServiceFactory({ + service: LocalizationService, + entryComponents: [], + mocks: [Store, Router], + }); + + beforeEach(() => { + spectator = createService(); + store = spectator.get(Store); + service = spectator.service; + }); + + describe('#currentLang', () => { + it('should be tr', () => { + store.selectSnapshot.andCallFake((selector: (state: any, ...states: any[]) => string) => { + return selector({ SessionState: { getLanguage: 'tr' } }); + }); + + expect(service.currentLang).toBe('tr'); + }); + }); + + describe('#get', () => { + it('should be return an observable localization', async () => { + store.select.andCallFake((selector: (state: any, ...states: any[]) => Observable) => { + return selector({ ConfigState: { getLocalization: (keys, ...interpolateParams) => of(keys) } }); + }); + + const localization = await service.get('AbpTest').toPromise(); + + expect(localization).toBe('AbpTest'); + }); + }); + + describe('#instant', () => { + it('should be return a localization', () => { + store.selectSnapshot.andCallFake((selector: (state: any, ...states: any[]) => Observable) => { + return selector({ ConfigState: { getLocalization: (keys, ...interpolateParams) => keys } }); + }); + + expect(service.instant('AbpTest')).toBe('AbpTest'); + }); + }); + + describe('#registerLocale', () => { + it('should return registerLocale and then call setRouteReuse', () => { + const router = spectator.get(Router); + + const shouldReuseRoute = () => true; + router.routeReuseStrategy = { shouldReuseRoute } as any; + + router.navigateByUrl.andCallFake(url => { + return new Promise(resolve => resolve({ catch: () => null })); + }); + + service.registerLocale('tr'); + + expect(router.navigated).toBe(false); + expect(router.routeReuseStrategy.shouldReuseRoute).not.toEqual(shouldReuseRoute); + }); + + it('should throw an error message when service have an otherInstance', async () => { + try { + const instance = new LocalizationService(null, null, null, {} as any); + } catch (error) { + expect((error as Error).message).toBe('LocaleService should have only one instance.'); + } + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts new file mode 100644 index 0000000000..6707432c03 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts @@ -0,0 +1,15 @@ +import { Spectator, createComponentFactory, createHostFactory } from '@ngneat/spectator'; +import { RouterTestingModule } from '@angular/router/testing'; +import { RouterOutletComponent } from '../components/router-outlet.component'; + +describe('RouterOutletComponent', () => { + let spectator: Spectator; + const createHost = createHostFactory({ component: RouterOutletComponent, imports: [RouterTestingModule] }); + + it('should have a router-outlet element', () => { + spectator = createHost(''); + console.log((spectator.debugElement.nativeElement as HTMLElement).children); + expect((spectator.debugElement.nativeElement as HTMLElement).children.length).toBe(1); + expect((spectator.debugElement.nativeElement as HTMLElement).children[0].tagName).toBe('ROUTER-OUTLET'); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/state-overwrite.ts b/npm/ng-packs/packages/core/src/lib/tests/state-overwrite.ts new file mode 100644 index 0000000000..31eaa0560f --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/state-overwrite.ts @@ -0,0 +1,45 @@ +import { Inject, Injectable, InjectionToken, Provider } from '@angular/core'; +import { getActionTypeFromInstance, InitState, NgxsPlugin, NGXS_PLUGINS, setValue } from '@ngxs/store'; + +export const NGXS_OVERWRITE_PLUGIN_VALUE = new InjectionToken('NGXS_OVERWRITE_PLUGIN_VALUE'); + +export class StateOverwrite { + static readonly type = '[StateOverwrite] Patch'; + constructor(public payload: { stateName: string; value: any }) {} +} + +@Injectable() +export class OverwritePlugin implements NgxsPlugin { + initialized: boolean; + + constructor(@Inject(NGXS_OVERWRITE_PLUGIN_VALUE) private options: any) {} + + handle(state, action, next) { + const type = getActionTypeFromInstance(action); + + if (action instanceof InitState && !this.initialized) { + state = { ...state, ...this.options }; + this.initialized = true; + } + + if (type === StateOverwrite.type) { + state = setValue(state, action.payload.stateName, action.payload.value); + } + + return next(state, action); + } +} + +export function stateOverwriteProviders(value = {}): Provider[] { + return [ + { + provide: NGXS_PLUGINS, + useClass: OverwritePlugin, + multi: true, + }, + { + provide: NGXS_OVERWRITE_PLUGIN_VALUE, + useValue: value, + }, + ]; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts index ba76b0f97f..756d3a34dd 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts @@ -1,4 +1,5 @@ export function noop() { + // tslint:disable-next-line: only-arrow-functions const fn = function() {}; return fn; } diff --git a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts index 4e6bf5e09c..4256489efa 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts @@ -1,5 +1,6 @@ export function uuid(a?: any): string { return a - ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) + ? // tslint:disable-next-line: no-bitwise + (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid); } diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index 7cf870667b..7cea3905dc 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -3,10 +3,9 @@ import { Injector } from '@angular/core'; import { Store } from '@ngxs/store'; import { GetAppConfiguration } from '../actions/config.actions'; import differentLocales from '../constants/different-locales'; -import { SessionState } from '../states/session.state'; export function getInitialData(injector: Injector) { - const fn = function() { + const fn = () => { const store: Store = injector.get(Store); return store.dispatch(new GetAppConfiguration()).toPromise(); @@ -16,10 +15,10 @@ export function getInitialData(injector: Injector) { } export function localeInitializer(injector: Injector) { - const fn = function() { + const fn = () => { const store: Store = injector.get(Store); - const lang = store.selectSnapshot(SessionState.getLanguage) || 'en'; + const lang = store.selectSnapshot(state => state.SessionState.language) || 'en'; return new Promise((resolve, reject) => { registerLocale(lang).then(() => resolve(), reject); diff --git a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts index 533e4cc30d..ef6a75fe65 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts @@ -6,18 +6,19 @@ function isFunction(value) { } export const takeUntilDestroy = (componentInstance, destroyMethodName = 'ngOnDestroy') => ( - source: Observable, + source: Observable ) => { const originalDestroy = componentInstance[destroyMethodName]; if (isFunction(originalDestroy) === false) { throw new Error( - `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`, + `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}` ); } if (!componentInstance['__takeUntilDestroy']) { componentInstance['__takeUntilDestroy'] = new Subject(); componentInstance[destroyMethodName] = function() { + // tslint:disable-next-line: no-unused-expression isFunction(originalDestroy) && originalDestroy.apply(this, arguments); componentInstance['__takeUntilDestroy'].next(true); componentInstance['__takeUntilDestroy'].complete(); diff --git a/npm/ng-packs/packages/core/tsconfig.spec.json b/npm/ng-packs/packages/core/tsconfig.spec.json index 16da33db07..b86d4b4cce 100644 --- a/npm/ng-packs/packages/core/tsconfig.spec.json +++ b/npm/ng-packs/packages/core/tsconfig.spec.json @@ -2,16 +2,12 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"], + "paths": { + "@abp/ng.core": ["packages/core/src/public-api.ts"], + "@abp/ng.core/*": ["packages/core/src/lib/*"] + } }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index 38219b2b08..bf8e5b2e5e 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -9,7 +9,7 @@ import { pluck, tap } from 'rxjs/operators'; @Component({ selector: 'abp-feature-management', - templateUrl: './feature-management.component.html', + templateUrl: './feature-management.component.html' }) export class FeatureManagementComponent { @Input() @@ -32,13 +32,12 @@ export class FeatureManagementComponent { if (value) this.openModal(); } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @Select(FeatureManagementState.getFeatures) features$: Observable; - modalBusy: boolean = false; + modalBusy = false; form: FormGroup; @@ -54,7 +53,12 @@ export class FeatureManagementComponent { getFeatures() { this.store - .dispatch(new GetFeatures({ providerKey: this.providerKey, providerName: this.providerName })) + .dispatch( + new GetFeatures({ + providerKey: this.providerKey, + providerName: this.providerName + }) + ) .pipe(pluck('FeatureManagementState', 'features')) .subscribe(features => { this.buildForm(features); @@ -78,7 +82,7 @@ export class FeatureManagementComponent { features = features.map((feature, i) => ({ name: feature.name, - value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i], + value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i] })); this.store @@ -86,8 +90,8 @@ export class FeatureManagementComponent { new UpdateFeatures({ providerKey: this.providerKey, providerName: this.providerName, - features, - }), + features + }) ) .subscribe(() => { this.modalBusy = false; diff --git a/npm/ng-packs/packages/feature-management/src/lib/states/index.ts b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts index 5c32d87889..3c9c6e26d6 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/states/index.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./feature-management.state"; +export * from './feature-management.state'; diff --git a/npm/ng-packs/packages/identity-config/README.md b/npm/ng-packs/packages/identity-config/README.md new file mode 100644 index 0000000000..8754748f6a --- /dev/null +++ b/npm/ng-packs/packages/identity-config/README.md @@ -0,0 +1 @@ +# @abp/ng.identity.config \ No newline at end of file diff --git a/npm/ng-packs/packages/identity-config/karma.conf.js b/npm/ng-packs/packages/identity-config/karma.conf.js new file mode 100644 index 0000000000..47f6e498d2 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/identity-config'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/identity-config/ng-package.json b/npm/ng-packs/packages/identity-config/ng-package.json new file mode 100644 index 0000000000..d72870cd39 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/identity-config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/identity-config/package.json b/npm/ng-packs/packages/identity-config/package.json new file mode 100644 index 0000000000..e7989e56de --- /dev/null +++ b/npm/ng-packs/packages/identity-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "@abp/ng.identity.config", + "version": "0.0.1", + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts b/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts new file mode 100644 index 0000000000..a354cd6a46 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts @@ -0,0 +1,8 @@ +import { noop } from '@abp/ng.core'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { IdentityConfigService } from './services/identity-config.service'; + +@NgModule({ + providers: [{ provide: APP_INITIALIZER, deps: [IdentityConfigService], useFactory: noop, multi: true }], +}) +export class IdentityConfigModule {} diff --git a/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts b/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts new file mode 100644 index 0000000000..fe78f8e810 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts @@ -0,0 +1,32 @@ +import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class IdentityConfigService { + constructor(private router: Router, private restService: RestService) { + ABP_ROUTES.push( + { + name: 'AbpUiNavigation::Menu:Administration', + path: '', + order: 1, + wrapper: true, + }, + { + name: 'AbpIdentity::Menu:IdentityManagement', + path: 'identity', + order: 1, + parentName: 'AbpUiNavigation::Menu:Administration', + layout: eLayoutType.application, + iconClass: 'fa fa-id-card-o', + children: [ + { path: 'roles', name: 'AbpIdentity::Roles', order: 2, requiredPolicy: 'AbpIdentity.Roles' }, + { path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' }, + ], + }, + ); + } +} diff --git a/npm/ng-packs/packages/identity-config/src/public-api.ts b/npm/ng-packs/packages/identity-config/src/public-api.ts new file mode 100644 index 0000000000..e702653211 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/src/public-api.ts @@ -0,0 +1,2 @@ +export * from './lib/services/identity-config.service'; +export * from './lib/identity-config.module'; diff --git a/npm/ng-packs/packages/identity-config/src/test.ts b/npm/ng-packs/packages/identity-config/src/test.ts new file mode 100644 index 0000000000..978c64fb83 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/identity-config/tsconfig.lib.json b/npm/ng-packs/packages/identity-config/tsconfig.lib.json new file mode 100644 index 0000000000..bd23948e59 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/identity-config/tsconfig.spec.json b/npm/ng-packs/packages/identity-config/tsconfig.spec.json new file mode 100644 index 0000000000..16da33db07 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/identity-config/tslint.json b/npm/ng-packs/packages/identity-config/tslint.json new file mode 100644 index 0000000000..8c3919ea62 --- /dev/null +++ b/npm/ng-packs/packages/identity-config/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "abp", + "camelCase" + ], + "component-selector": [ + true, + "element", + "abp", + "kebab-case" + ] + } +} diff --git a/npm/ng-packs/packages/identity/ng-package.json b/npm/ng-packs/packages/identity/ng-package.json index aa178ed0d5..5f547dee2b 100644 --- a/npm/ng-packs/packages/identity/ng-package.json +++ b/npm/ng-packs/packages/identity/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.permission-management"] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.permission-management", "@abp/ng.identity.config"] } diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index f139753679..7f0c47ef61 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -2,7 +2,9 @@ "name": "@abp/ng.identity", "version": "0.9.0", "dependencies": { - "@abp/ng.permission-management": "^0.9.0" + "@abp/ng.theme.shared": "^0.9.0", + "@abp/ng.permission-management": "^0.9.0", + "@abp/ng.identity.config": "0.0.1" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts index a13ffb7489..7ebda6be18 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts @@ -11,7 +11,7 @@ import { IdentityState } from '../../states/identity.state'; @Component({ selector: 'abp-roles', - templateUrl: './roles.component.html', + templateUrl: './roles.component.html' }) export class RolesComponent { @Select(IdentityState.getRoles) @@ -26,19 +26,19 @@ export class RolesComponent { isModalVisible: boolean; - visiblePermissions: boolean = false; + visiblePermissions = false; providerKey: string; pageQuery: ABP.PageQueryParams = { - sorting: 'name', + sorting: 'name' }; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; @ViewChild('modalContent', { static: false }) modalContent: TemplateRef; @@ -54,10 +54,10 @@ export class RolesComponent { this.form = this.fb.group({ name: new FormControl({ value: this.selected.name || '', disabled: this.selected.isStatic }, [ Validators.required, - Validators.maxLength(256), + Validators.maxLength(256) ]), isDefault: [this.selected.isDefault || false], - isPublic: [this.selected.isPublic || false], + isPublic: [this.selected.isPublic || false] }); } @@ -89,7 +89,7 @@ export class RolesComponent { .dispatch( this.selected.id ? new UpdateRole({ ...this.form.value, id: this.selected.id }) - : new CreateRole(this.form.value), + : new CreateRole(this.form.value) ) .subscribe(() => { this.modalBusy = false; @@ -100,7 +100,7 @@ export class RolesComponent { delete(id: string, name: string) { this.confirmationService .warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { - messageLocalizationParams: [name], + messageLocalizationParams: [name] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts index f3a1891018..18f7507f29 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts @@ -12,13 +12,13 @@ import { GetUserById, GetUserRoles, GetUsers, - UpdateUser, + UpdateUser } from '../../actions/identity.actions'; import { Identity } from '../../models/identity'; import { IdentityState } from '../../states/identity.state'; @Component({ selector: 'abp-users', - templateUrl: './users.component.html', + templateUrl: './users.component.html' }) export class UsersComponent { @Select(IdentityState.getUsers) @@ -38,21 +38,21 @@ export class UsersComponent { roles: Identity.RoleItem[]; - visiblePermissions: boolean = false; + visiblePermissions = false; providerKey: string; pageQuery: ABP.PageQueryParams = { - sorting: 'userName', + sorting: 'userName' }; isModalVisible: boolean; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; @@ -80,10 +80,10 @@ export class UsersComponent { roleNames: this.fb.array( this.roles.map(role => this.fb.group({ - [role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))], - }), - ), - ), + [role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))] + }) + ) + ) }); if (!this.selected.userName) { this.form.addControl('password', new FormControl('', [Validators.required, Validators.maxLength(32)])); @@ -107,7 +107,7 @@ export class UsersComponent { .pipe( switchMap(() => this.store.dispatch(new GetUserRoles(id))), pluck('IdentityState'), - take(1), + take(1) ) .subscribe((state: Identity.State) => { this.selected = state.selectedUser; @@ -123,7 +123,7 @@ export class UsersComponent { const { roleNames } = this.form.value; const mappedRoleNames = snq( () => roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]), - [], + [] ); this.store @@ -132,12 +132,12 @@ export class UsersComponent { ? new UpdateUser({ ...this.form.value, id: this.selected.id, - roleNames: mappedRoleNames, + roleNames: mappedRoleNames }) : new CreateUser({ ...this.form.value, - roleNames: mappedRoleNames, - }), + roleNames: mappedRoleNames + }) ) .subscribe(() => { this.modalBusy = false; @@ -148,7 +148,7 @@ export class UsersComponent { delete(id: string, userName: string) { this.confirmationService .warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { - messageLocalizationParams: [userName], + messageLocalizationParams: [userName] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { diff --git a/npm/ng-packs/packages/identity/src/lib/constants/routes.ts b/npm/ng-packs/packages/identity/src/lib/constants/routes.ts index cece75ad63..1dfc53245e 100644 --- a/npm/ng-packs/packages/identity/src/lib/constants/routes.ts +++ b/npm/ng-packs/packages/identity/src/lib/constants/routes.ts @@ -1,5 +1,9 @@ import { eLayoutType, ABP } from '@abp/ng.core'; +/** + * + * @deprecated + */ export const IDENTITY_ROUTES = { routes: [ { diff --git a/npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts b/npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts index fbbecffc9b..ffe155a94f 100644 --- a/npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts +++ b/npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts @@ -9,21 +9,20 @@ import { UserResolver } from './resolvers/users.resolver'; const routes: Routes = [ { path: '', redirectTo: 'roles', pathMatch: 'full' }, { - path: 'roles', + path: '', component: DynamicLayoutComponent, canActivate: [AuthGuard, PermissionGuard], - data: { requiredPolicy: 'AbpIdentity.Roles' }, - children: [{ path: '', component: RolesComponent, resolve: [RoleResolver] }], - }, - { - path: 'users', - component: DynamicLayoutComponent, - canActivate: [AuthGuard, PermissionGuard], - data: { requiredPolicy: 'AbpIdentity.Users' }, children: [ { - path: '', + path: 'roles', + component: RolesComponent, + resolve: [RoleResolver], + data: { requiredPolicy: 'AbpIdentity.Roles' }, + }, + { + path: 'users', component: UsersComponent, + data: { requiredPolicy: 'AbpIdentity.Users' }, resolve: [RoleResolver, UserResolver], }, ], diff --git a/npm/ng-packs/packages/identity/src/lib/identity.module.ts b/npm/ng-packs/packages/identity/src/lib/identity.module.ts index d94ba410d9..15b1b1c7cb 100644 --- a/npm/ng-packs/packages/identity/src/lib/identity.module.ts +++ b/npm/ng-packs/packages/identity/src/lib/identity.module.ts @@ -27,6 +27,10 @@ import { NgxValidateCoreModule } from '@ngx-validate/core'; }) export class IdentityModule {} +/** + * + * @deprecated + */ export function IdentityProviders(): Provider[] { return []; } diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts index 4b83c5a5ab..c968df1e3d 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts @@ -7,7 +7,7 @@ import { Output, Renderer2, SimpleChanges, - TrackByFunction, + TrackByFunction } from '@angular/core'; import { Select, Store } from '@ngxs/store'; import { from, Observable } from 'rxjs'; @@ -16,11 +16,13 @@ import { GetPermissions, UpdatePermissions } from '../actions/permission-managem import { PermissionManagement } from '../models/permission-management'; import { PermissionManagementState } from '../states/permission-management.state'; -type PermissionWithMargin = PermissionManagement.Permission & { margin: number }; +type PermissionWithMargin = PermissionManagement.Permission & { + margin: number; +}; @Component({ selector: 'abp-permission-management', - templateUrl: './permission-management.component.html', + templateUrl: './permission-management.component.html' }) export class PermissionManagementComponent implements OnInit, OnChanges { @Input() @@ -47,8 +49,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @Select(PermissionManagementState.getPermissionGroups) groups$: Observable; @@ -60,18 +61,18 @@ export class PermissionManagementComponent implements OnInit, OnChanges { permissions: PermissionManagement.Permission[] = []; - selectThisTab: boolean = false; + selectThisTab = false; - selectAllTab: boolean = false; + selectAllTab = false; - modalBusy: boolean = false; + modalBusy = false; trackByFn: TrackByFunction = (_, item) => item.name; get selectedGroupPermissions$(): Observable { return this.groups$.pipe( map(groups => - this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : [], + this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : [] ), map(permissions => permissions.map( @@ -79,10 +80,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges { (({ ...permission, margin: findMargin(permissions, permission), - isGranted: this.permissions.find(per => per.name === permission.name).isGranted, - } as any) as PermissionWithMargin), - ), - ), + isGranted: this.permissions.find(per => per.name === permission.name).isGranted + } as any) as PermissionWithMargin) + ) + ) ); } @@ -164,7 +165,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges { this.permissions = [ ...this.permissions.slice(0, index), { ...this.permissions[index], isGranted: !this.selectThisTab }, - ...this.permissions.slice(index + 1), + ...this.permissions.slice(index + 1) ]; }); }); @@ -173,7 +174,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } onClickSelectAll() { - this.permissions = this.permissions.map(permission => ({ ...permission, isGranted: !this.selectAllTab })); + this.permissions = this.permissions.map(permission => ({ + ...permission, + isGranted: !this.selectAllTab + })); this.selectThisTab = !this.selectAllTab; } @@ -186,12 +190,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges { submit() { this.modalBusy = true; const unchangedPermissions = getPermissions( - this.store.selectSnapshot(PermissionManagementState.getPermissionGroups), + this.store.selectSnapshot(PermissionManagementState.getPermissionGroups) ); const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions .filter(per => - unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true, + unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true ) .map(({ name, isGranted }) => ({ name, isGranted })); @@ -201,8 +205,8 @@ export class PermissionManagementComponent implements OnInit, OnChanges { new UpdatePermissions({ providerKey: this.providerKey, providerName: this.providerName, - permissions: changedPermissions, - }), + permissions: changedPermissions + }) ) .subscribe(() => { this.modalBusy = false; @@ -220,7 +224,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } this.store - .dispatch(new GetPermissions({ providerKey: this.providerKey, providerName: this.providerName })) + .dispatch( + new GetPermissions({ + providerKey: this.providerKey, + providerName: this.providerName + }) + ) .pipe(pluck('PermissionManagementState', 'permissionRes')) .subscribe((permissionRes: PermissionManagement.Response) => { this.selectedGroup = permissionRes.groups[0]; diff --git a/npm/ng-packs/packages/permission-management/src/lib/states/index.ts b/npm/ng-packs/packages/permission-management/src/lib/states/index.ts index 11166b2e3b..1d8fcc0d5d 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/states/index.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./permission-management.state"; +export * from './permission-management.state'; diff --git a/npm/ng-packs/packages/setting-management-config/README.md b/npm/ng-packs/packages/setting-management-config/README.md new file mode 100644 index 0000000000..9e69e2dce1 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/README.md @@ -0,0 +1,24 @@ +# SettingManagementConfig + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.7. + +## Code scaffolding + +Run `ng generate component component-name --project setting-management-config` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project setting-management-config`. +> Note: Don't forget to add `--project setting-management-config` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build setting-management-config` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build setting-management-config`, go to the dist folder `cd dist/setting-management-config` and run `npm publish`. + +## Running unit tests + +Run `ng test setting-management-config` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/npm/ng-packs/packages/setting-management-config/karma.conf.js b/npm/ng-packs/packages/setting-management-config/karma.conf.js new file mode 100644 index 0000000000..7ff4863cb0 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/setting-management-config'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/setting-management-config/ng-package.json b/npm/ng-packs/packages/setting-management-config/ng-package.json new file mode 100644 index 0000000000..0d24be4f85 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/setting-management-config", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/npm/ng-packs/packages/setting-management-config/package.json b/npm/ng-packs/packages/setting-management-config/package.json new file mode 100644 index 0000000000..9a9f49aa0a --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "@abp/ng.setting-management.config", + "version": "0.0.1", + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts b/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts new file mode 100644 index 0000000000..851f435449 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { ABP_ROUTES, eLayoutType } from '@abp/ng.core'; + +@Injectable({ + providedIn: 'root', +}) +export class SettingManagementConfigService { + constructor() { + ABP_ROUTES.push({ + name: 'Settings', + path: 'setting-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: eLayoutType.application, + order: 6, + iconClass: 'fa fa-cog', + }); + } +} diff --git a/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts b/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts new file mode 100644 index 0000000000..c11e712fa4 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts @@ -0,0 +1,8 @@ +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { SettingManagementConfigService } from './services/setting-management-config.service'; +import { noop } from '@abp/ng.core'; + +@NgModule({ + providers: [{ provide: APP_INITIALIZER, deps: [SettingManagementConfigService], useFactory: noop, multi: true }], +}) +export class SettingManagementConfigModule {} diff --git a/npm/ng-packs/packages/setting-management-config/src/public-api.ts b/npm/ng-packs/packages/setting-management-config/src/public-api.ts new file mode 100644 index 0000000000..236ed82079 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/src/public-api.ts @@ -0,0 +1 @@ +export * from './lib/setting-management-config.module'; diff --git a/npm/ng-packs/packages/setting-management-config/src/test.ts b/npm/ng-packs/packages/setting-management-config/src/test.ts new file mode 100644 index 0000000000..978c64fb83 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json b/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json new file mode 100644 index 0000000000..bd23948e59 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json b/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json new file mode 100644 index 0000000000..16da33db07 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/setting-management-config/tslint.json b/npm/ng-packs/packages/setting-management-config/tslint.json new file mode 100644 index 0000000000..8c3919ea62 --- /dev/null +++ b/npm/ng-packs/packages/setting-management-config/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "abp", + "camelCase" + ], + "component-selector": [ + true, + "element", + "abp", + "kebab-case" + ] + } +} diff --git a/npm/ng-packs/packages/setting-management/ng-package.json b/npm/ng-packs/packages/setting-management/ng-package.json index e5ba9f97fc..7a08dda36b 100644 --- a/npm/ng-packs/packages/setting-management/ng-package.json +++ b/npm/ng-packs/packages/setting-management/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.setting-management.config"] } diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index f02d0f33b2..f0ebfe66c8 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -2,7 +2,8 @@ "name": "@abp/ng.setting-management", "version": "0.9.0", "dependencies": { - "@abp/ng.theme.shared": "^0.9.0" + "@abp/ng.theme.shared": "^0.9.0", + "@abp/ng.setting-management.config": "^0.0.1" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.ts b/npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.ts deleted file mode 100644 index 1c9d702a01..0000000000 --- a/npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { eLayoutType } from '@abp/ng.core'; -import { SettingTab } from '@abp/ng.theme.shared'; -import { Component, TrackByFunction } from '@angular/core'; -import { Router } from '@angular/router'; -import { timer } from 'rxjs'; -import { SettingManagementService } from '../services/setting-management.service'; - -@Component({ - selector: 'abp-setting-layout', - templateUrl: './setting-layout.component.html', -}) -export class SettingLayoutComponent { - // required for dynamic component - static type = eLayoutType.setting; - - trackByFn: TrackByFunction = (_, item) => item.name; - - constructor(public settingManagementService: SettingManagementService, private router: Router) { - if ( - settingManagementService.selected && - this.router.url !== settingManagementService.selected.url && - settingManagementService.settings.length - ) { - settingManagementService.setSelected(settingManagementService.settings[0]); - } - } - - ngOnDestroy() {} -} diff --git a/npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.html b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html similarity index 56% rename from npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.html rename to npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html index 379e368e9d..58659ff50b 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/components/setting-layout.component.html +++ b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html @@ -13,20 +13,12 @@
-
- -
-
-
- +
+
+
diff --git a/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts new file mode 100644 index 0000000000..17f0f93bb0 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts @@ -0,0 +1,28 @@ +import { Component, TrackByFunction, OnInit } from '@angular/core'; +import { SettingTab, SETTING_TABS } from '@abp/ng.theme.shared'; +import { Router } from '@angular/router'; +import { Store } from '@ngxs/store'; +import { ConfigState } from '@abp/ng.core'; + +@Component({ + selector: 'abp-setting-management', + templateUrl: './setting-management.component.html', +}) +export class SettingManagementComponent implements OnInit { + settings: SettingTab[] = []; + + selected = {} as SettingTab; + + trackByFn: TrackByFunction = (_, item) => item.name; + + constructor(private router: Router, private store: Store) {} + + ngOnInit() { + this.settings = SETTING_TABS.filter(setting => + this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)), + ).sort((a, b) => a.order - b.order); + if (this.settings.length) { + this.selected = this.settings[0]; + } + } +} diff --git a/npm/ng-packs/packages/setting-management/src/lib/constants/index.ts b/npm/ng-packs/packages/setting-management/src/lib/constants/index.ts deleted file mode 100644 index a3820983e2..0000000000 --- a/npm/ng-packs/packages/setting-management/src/lib/constants/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './routes'; diff --git a/npm/ng-packs/packages/setting-management/src/lib/constants/routes.ts b/npm/ng-packs/packages/setting-management/src/lib/constants/routes.ts deleted file mode 100644 index 3f47c95eb7..0000000000 --- a/npm/ng-packs/packages/setting-management/src/lib/constants/routes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ABP, eLayoutType } from '@abp/ng.core'; - -export const SETTING_MANAGEMENT_ROUTES = { - routes: [ - { - name: 'Settings', - path: 'setting-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: eLayoutType.application, - order: 6, - iconClass: 'fa fa-cog', - }, - ] as ABP.FullRoute[], -}; diff --git a/npm/ng-packs/packages/setting-management/src/lib/services/setting-management.service.ts b/npm/ng-packs/packages/setting-management/src/lib/services/setting-management.service.ts deleted file mode 100644 index 79878b1717..0000000000 --- a/npm/ng-packs/packages/setting-management/src/lib/services/setting-management.service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { SettingTab } from '@abp/ng.theme.shared'; -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { Navigate } from '@ngxs/router-plugin'; -import { Store, Actions, ofActionSuccessful } from '@ngxs/store'; -import { Subject } from 'rxjs'; -import { ConfigState, GetAppConfiguration } from '@abp/ng.core'; -import { OAuthService } from 'angular-oauth2-oidc'; -import { takeUntil } from 'rxjs/operators'; - -@Injectable({ providedIn: 'root' }) -export class SettingManagementService { - settings: SettingTab[] = []; - - selected = {} as SettingTab; - - private destroy$ = new Subject(); - - constructor( - private actions: Actions, - private router: Router, - private store: Store, - private oAuthService: OAuthService, - ) { - setTimeout(() => this.setSettings(), 0); - - this.actions - .pipe(ofActionSuccessful(GetAppConfiguration)) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - if (this.oAuthService.hasValidAccessToken()) { - this.setSettings(); - } - }); - } - - ngOnDestroy() { - this.destroy$.next(); - } - - setSettings() { - const route = this.router.config.find(r => r.path === 'setting-management'); - this.settings = (route.data.settings as SettingTab[]) - .filter(setting => this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy))) - .sort((a, b) => a.order - b.order); - this.checkSelected(); - } - - checkSelected() { - this.selected = this.settings.find(setting => setting.url === this.router.url) || ({} as SettingTab); - - if (!this.selected.name && this.settings.length) { - this.setSelected(this.settings[0]); - } - } - - setSelected(selected: SettingTab) { - this.selected = selected; - this.store.dispatch(new Navigate([selected.url])); - } -} diff --git a/npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts b/npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts index e401df16de..d1cb780777 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts @@ -1,15 +1,13 @@ -import { DynamicLayoutComponent } from '@abp/ng.core'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { SETTING_MANAGEMENT_ROUTES } from './constants/routes'; -import { SettingLayoutComponent } from './components/setting-layout.component'; +import { SettingManagementComponent } from './components/setting-management.component'; +import { DynamicLayoutComponent } from '@abp/ng.core'; const routes: Routes = [ { - path: 'setting-management', + path: '', component: DynamicLayoutComponent, - children: [{ path: '', component: SettingLayoutComponent }], - data: { routes: SETTING_MANAGEMENT_ROUTES, settings: [] }, + children: [{ path: '', component: SettingManagementComponent }], }, ]; diff --git a/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts b/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts index b3959e6650..397c581587 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts @@ -1,14 +1,11 @@ import { CoreModule } from '@abp/ng.core'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { NgModule } from '@angular/core'; -import { SettingLayoutComponent } from './components/setting-layout.component'; import { SettingManagementRoutingModule } from './setting-management-routing.module'; - -export const SETTING_LAYOUT = SettingLayoutComponent; +import { SettingManagementComponent } from './components/setting-management.component'; @NgModule({ - declarations: [SETTING_LAYOUT], + declarations: [SettingManagementComponent], imports: [SettingManagementRoutingModule, CoreModule, ThemeSharedModule], - entryComponents: [SETTING_LAYOUT], }) export class SettingManagementModule {} diff --git a/npm/ng-packs/packages/setting-management/src/public-api.ts b/npm/ng-packs/packages/setting-management/src/public-api.ts index 5343d9e457..8027d769f9 100644 --- a/npm/ng-packs/packages/setting-management/src/public-api.ts +++ b/npm/ng-packs/packages/setting-management/src/public-api.ts @@ -1,3 +1,2 @@ export * from './lib/setting-management.module'; -export * from './lib/constants'; -export * from './lib/components/setting-layout.component'; +export * from './lib/components/setting-management.component'; diff --git a/npm/ng-packs/packages/tenant-management-config/README.md b/npm/ng-packs/packages/tenant-management-config/README.md new file mode 100644 index 0000000000..77548feaf9 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/README.md @@ -0,0 +1 @@ +# @abp/ng.tenant-management.config \ No newline at end of file diff --git a/npm/ng-packs/packages/tenant-management-config/karma.conf.js b/npm/ng-packs/packages/tenant-management-config/karma.conf.js new file mode 100644 index 0000000000..850929cc06 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/tenant-management-config'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/npm/ng-packs/packages/tenant-management-config/ng-package.json b/npm/ng-packs/packages/tenant-management-config/ng-package.json new file mode 100644 index 0000000000..834556113c --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/tenant-management-config", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/npm/ng-packs/packages/tenant-management-config/package.json b/npm/ng-packs/packages/tenant-management-config/package.json new file mode 100644 index 0000000000..43b4392733 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/package.json @@ -0,0 +1,7 @@ +{ + "name": "@abp/ng.tenant-management.config", + "version": "0.0.1", + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts b/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts new file mode 100644 index 0000000000..8d77d1005f --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { ABP_ROUTES, eLayoutType } from '@abp/ng.core'; + +@Injectable({ + providedIn: 'root', +}) +export class TenantManagementConfigService { + constructor() { + ABP_ROUTES.push({ + name: 'AbpTenantManagement::Menu:TenantManagement', + path: 'tenant-management', + parentName: 'AbpUiNavigation::Menu:Administration', + layout: eLayoutType.application, + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: 'AbpTenantManagement::Tenants', + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + }, + ], + }); + } +} diff --git a/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts b/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts new file mode 100644 index 0000000000..7f59d28be4 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts @@ -0,0 +1,8 @@ +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { TenantManagementConfigService } from './services/tenant-management-config.service'; +import { noop } from '@abp/ng.core'; + +@NgModule({ + providers: [{ provide: APP_INITIALIZER, deps: [TenantManagementConfigService], useFactory: noop, multi: true }], +}) +export class TenantManagementConfigModule {} diff --git a/npm/ng-packs/packages/tenant-management-config/src/public-api.ts b/npm/ng-packs/packages/tenant-management-config/src/public-api.ts new file mode 100644 index 0000000000..a6d21694db --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/src/public-api.ts @@ -0,0 +1,2 @@ +export * from './lib/services/tenant-management-config.service'; +export * from './lib/tenant-management-config.module'; diff --git a/npm/ng-packs/packages/tenant-management-config/src/test.ts b/npm/ng-packs/packages/tenant-management-config/src/test.ts new file mode 100644 index 0000000000..978c64fb83 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json b/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json new file mode 100644 index 0000000000..bd23948e59 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json b/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json new file mode 100644 index 0000000000..16da33db07 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/npm/ng-packs/packages/tenant-management-config/tslint.json b/npm/ng-packs/packages/tenant-management-config/tslint.json new file mode 100644 index 0000000000..8c3919ea62 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management-config/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "abp", + "camelCase" + ], + "component-selector": [ + true, + "element", + "abp", + "kebab-case" + ] + } +} diff --git a/npm/ng-packs/packages/tenant-management/ng-package.json b/npm/ng-packs/packages/tenant-management/ng-package.json index 07fbab773b..959eae44ab 100644 --- a/npm/ng-packs/packages/tenant-management/ng-package.json +++ b/npm/ng-packs/packages/tenant-management/ng-package.json @@ -4,5 +4,9 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.feature-management"] + "whitelistedNonPeerDependencies": [ + "@abp/ng.theme.shared", + "@abp/ng.feature-management", + "@abp/ng.tenant-management.config" + ] } diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 8f59b9179d..c1b3909534 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -3,7 +3,8 @@ "version": "0.9.0", "dependencies": { "@abp/ng.theme.shared": "^0.9.0", - "@abp/ng.feature-management": "^0.9.0" + "@abp/ng.feature-management": "^0.9.0", + "@abp/ng.tenant-management.config": "^0.0.1" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts index a6040da45f..f9c90764c2 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts @@ -10,20 +10,20 @@ import { DeleteTenant, GetTenants, GetTenantById, - UpdateTenant, + UpdateTenant } from '../../actions/tenant-management.actions'; import { TenantManagementService } from '../../services/tenant-management.service'; import { TenantManagementState } from '../../states/tenant-management.state'; -type SelectedModalContent = { +interface SelectedModalContent { type: string; title: string; template: TemplateRef; -}; +} @Component({ selector: 'abp-tenants', - templateUrl: './tenants.component.html', + templateUrl: './tenants.component.html' }) export class TenantsComponent { @Select(TenantManagementState.get) @@ -44,21 +44,21 @@ export class TenantsComponent { selectedModalContent = {} as SelectedModalContent; - visibleFeatures: boolean = false; + visibleFeatures = false; providerKey: string; _useSharedDatabase: boolean; pageQuery: ABP.PageQueryParams = { - sorting: 'name', + sorting: 'name' }; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; get useSharedDatabase(): boolean { return this.defaultConnectionStringForm.get('useSharedDatabase').value; @@ -78,7 +78,7 @@ export class TenantsComponent { private confirmationService: ConfirmationService, private tenantService: TenantManagementService, private fb: FormBuilder, - private store: Store, + private store: Store ) {} onSearch(value) { @@ -88,14 +88,14 @@ export class TenantsComponent { private createTenantForm() { this.tenantForm = this.fb.group({ - name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]], + name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]] }); } private createDefaultConnectionStringForm() { this.defaultConnectionStringForm = this.fb.group({ useSharedDatabase: this._useSharedDatabase, - defaultConnectionString: [this.defaultConnectionString || ''], + defaultConnectionString: [this.defaultConnectionString || ''] }); } @@ -103,7 +103,7 @@ export class TenantsComponent { this.selectedModalContent = { title, template, - type, + type }; this.isModalVisible = true; @@ -117,7 +117,7 @@ export class TenantsComponent { switchMap(selected => { this.selected = selected; return this.tenantService.getDefaultConnectionString(id); - }), + }) ) .subscribe(fetchedConnectionString => { this._useSharedDatabase = fetchedConnectionString ? false : true; @@ -158,7 +158,7 @@ export class TenantsComponent { .deleteDefaultConnectionString(this.selected.id) .pipe( take(1), - finalize(() => (this.modalBusy = false)), + finalize(() => (this.modalBusy = false)) ) .subscribe(() => { this.isModalVisible = false; @@ -168,7 +168,7 @@ export class TenantsComponent { .updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString }) .pipe( take(1), - finalize(() => (this.modalBusy = false)), + finalize(() => (this.modalBusy = false)) ) .subscribe(() => { this.isModalVisible = false; @@ -184,9 +184,9 @@ export class TenantsComponent { .dispatch( this.selected.id ? new UpdateTenant({ ...this.tenantForm.value, id: this.selected.id }) - : new CreateTenant(this.tenantForm.value), + : new CreateTenant(this.tenantForm.value) ) - .pipe(finalize(()=> (this.modalBusy = false))) + .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; }); @@ -195,7 +195,7 @@ export class TenantsComponent { delete(id: string, name: string) { this.confirmationService .warn('AbpTenantManagement::TenantDeletionConfirmationMessage', 'AbpTenantManagement::AreYouSure', { - messageLocalizationParams: [name], + messageLocalizationParams: [name] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { @@ -220,6 +220,6 @@ export class TenantsComponent { } changeSortOrder() { - this.sortOrder = this.sortOrder.toLowerCase() === "asc" ? "desc" : "asc"; + this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc'; } } diff --git a/npm/ng-packs/packages/tenant-management/src/lib/constants/routes.ts b/npm/ng-packs/packages/tenant-management/src/lib/constants/routes.ts index 5aad33a3c4..ad919c8ee1 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/constants/routes.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/constants/routes.ts @@ -1,5 +1,9 @@ import { ABP, eLayoutType } from '@abp/ng.core'; +/** + * + * @deprecated since version 0.9.0 + */ export const TENANT_MANAGEMENT_ROUTES = { routes: [ { diff --git a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts index 5763efa4d2..bb043377e6 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts @@ -4,7 +4,7 @@ import { RestService, Rest, ABP } from '@abp/ng.core'; import { TenantManagement } from '../models/tenant-management'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class TenantManagementService { constructor(private rest: RestService) {} @@ -13,7 +13,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'GET', url: '/api/multi-tenancy/tenants', - params, + params }; return this.rest.request(request); @@ -22,7 +22,7 @@ export class TenantManagementService { getTenantById(id: string): Observable { const request: Rest.Request = { method: 'GET', - url: `/api/multi-tenancy/tenants/${id}`, + url: `/api/multi-tenancy/tenants/${id}` }; return this.rest.request(request); @@ -31,7 +31,7 @@ export class TenantManagementService { deleteTenant(id: string): Observable { const request: Rest.Request = { method: 'DELETE', - url: `/api/multi-tenancy/tenants/${id}`, + url: `/api/multi-tenancy/tenants/${id}` }; return this.rest.request(request); @@ -40,8 +40,8 @@ export class TenantManagementService { createTenant(body: TenantManagement.AddRequest): Observable { const request: Rest.Request = { method: 'POST', - url: `/api/multi-tenancy/tenants`, - body, + url: '/api/multi-tenancy/tenants', + body }; return this.rest.request(request); @@ -54,7 +54,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'PUT', url, - body, + body }; return this.rest.request(request); @@ -66,7 +66,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'GET', responseType: Rest.ResponseType.Text, - url, + url }; return this.rest.request(request); } @@ -77,7 +77,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'PUT', url, - params: { defaultConnectionString: payload.defaultConnectionString }, + params: { defaultConnectionString: payload.defaultConnectionString } }; return this.rest.request(request); } @@ -87,7 +87,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'DELETE', - url, + url }; return this.rest.request(request); } diff --git a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts index f8a258da3b..5ab4f20716 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts @@ -25,6 +25,10 @@ import { NgxValidateCoreModule } from '@ngx-validate/core'; }) export class TenantManagementModule {} +/** + * + * @deprecated since version 0.9.0 + */ export function TenantManagementProviders(): Provider[] { return []; } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts index c942aa7c92..6459515b23 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts @@ -6,7 +6,7 @@ import { eLayoutType, SetLanguage, SessionState, - takeUntilDestroy, + takeUntilDestroy } from '@abp/ng.core'; import { AfterViewInit, @@ -16,7 +16,7 @@ import { TemplateRef, TrackByFunction, ViewChild, - ViewChildren, + ViewChildren } from '@angular/core'; import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { Navigate, RouterState } from '@ngxs/router-plugin'; @@ -32,7 +32,7 @@ import { LayoutState } from '../../states'; @Component({ selector: 'abp-layout-application', - templateUrl: './application-layout.component.html', + templateUrl: './application-layout.component.html' }) export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { // required for dynamic component @@ -59,9 +59,9 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { @ViewChildren('navbarRootDropdown', { read: NgbDropdown }) navbarRootDropdowns: QueryList; - isOpenChangePassword: boolean = false; + isOpenChangePassword = false; - isOpenProfile: boolean = false; + isOpenProfile = false; isDropdownChildDynamic: boolean; @@ -71,16 +71,13 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { get defaultLanguage$(): Observable { return this.languages$.pipe( - map( - languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName), - '', - ), + map(languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName), '') ); } get dropdownLanguages$(): Observable { return this.languages$.pipe( - map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), []), + map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), []) ); } @@ -116,8 +113,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { this.store.dispatch( new AddNavigationElement([ { element: this.languageRef, order: 4, name: 'LanguageRef' }, - { element: this.currentUserRef, order: 5, name: 'CurrentUserRef' }, - ]), + { element: this.currentUserRef, order: 5, name: 'CurrentUserRef' } + ]) ); } @@ -125,7 +122,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { .pipe( map(elements => elements.map(({ element }) => element)), filter(elements => !compare(elements, this.rightPartElements)), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(elements => { setTimeout(() => (this.rightPartElements = elements), 0); @@ -136,7 +133,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { fromEvent(window, 'resize') .pipe( takeUntilDestroy(this), - debounceTime(250), + debounceTime(250) ) .subscribe(() => { this.checkWindowWidth(); @@ -153,8 +150,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { this.oauthService.logOut(); this.store.dispatch( new Navigate(['/'], null, { - state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, - }), + state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url } + }) ); this.store.dispatch(new GetAppConfiguration()); } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts index dce489a331..5566335671 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts @@ -6,10 +6,10 @@ import { Store } from '@ngxs/store'; @Component({ selector: ' abp-layout', templateUrl: './layout.component.html', - animations: [slideFromBottom], + animations: [slideFromBottom] }) export class LayoutComponent { - isCollapsed: boolean = true; + isCollapsed = true; get appInfo(): Config.Application { return this.store.selectSnapshot(ConfigState.getApplicationInfo); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts index 5b4e7f483a..3943859e84 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts @@ -6,4 +6,4 @@ export default ` .entry-row { margin-bottom: 15px; } -`; \ No newline at end of file +`; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts index 71bff26b8b..1e257fc64c 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./layout.state"; +export * from './layout.state'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts index 25cb7d0ebd..8d7a50df65 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts @@ -28,19 +28,19 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt NgxValidateCoreModule.forRoot({ targetSelector: '.form-group', blueprints: { - email: `AbpAccount::ThisFieldIsNotAValidEmailAddress.`, - max: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`, - maxlength: `AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]`, - min: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`, - minlength: `AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]`, - required: `AbpAccount::ThisFieldIsRequired.`, - passwordMismatch: `AbpIdentity::Identity.PasswordConfirmationFailed`, + email: 'AbpAccount::ThisFieldIsNotAValidEmailAddress.', + max: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]', + maxlength: 'AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]', + min: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]', + minlength: 'AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]', + required: 'AbpAccount::ThisFieldIsRequired.', + passwordMismatch: 'AbpIdentity::Identity.PasswordConfirmationFailed' }, - errorTemplate: ValidationErrorComponent, - }), + errorTemplate: ValidationErrorComponent + }) ], exports: [...LAYOUTS], - entryComponents: [...LAYOUTS, ValidationErrorComponent], + entryComponents: [...LAYOUTS, ValidationErrorComponent] }) export class ThemeBasicModule { constructor(private initialService: InitialService) {} diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 690ed974b0..8ac5a60827 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -5,13 +5,13 @@ "@abp/ng.core": "^0.9.0", "@angular/cdk": "^8.0.1", "@ng-bootstrap/ng-bootstrap": "^5.1.0", - "@ngx-validate/core": "^0.0.5", + "@ngx-validate/core": "^0.0.7", "bootstrap": "^4.3.1", "chart.js": "^2.8.0", "font-awesome": "^4.7.0", "ngx-perfect-scrollbar": "^8.0.0", - "primeicons": "^1.0.0", - "primeng": "^8.0.0" + "primeicons": "^2.0.0", + "primeng": "^8.0.3" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts b/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts index 5dbba869f1..c911be1e43 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts @@ -2,12 +2,12 @@ import { MessageService } from 'primeng/components/common/messageservice'; import { Observable, Subject } from 'rxjs'; import { Toaster } from '../models/toaster'; -export class AbstractToaster { +export abstract class AbstractToaster { status$: Subject; - key: string = 'abpToast'; + key = 'abpToast'; - sticky: boolean = false; + sticky = false; constructor(protected messageService: MessageService) {} @@ -36,7 +36,7 @@ export class AbstractToaster { summary: title || '', ...options, key: this.key, - ...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }), + ...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }) }); this.status$ = new Subject(); return this.status$; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/bounce.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/bounce.animations.ts new file mode 100644 index 0000000000..c539725a0a --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/bounce.animations.ts @@ -0,0 +1,23 @@ +import { animate, animation, keyframes, style } from '@angular/animations'; + +export const bounceIn = animation( + [ + style({ opacity: '0', display: '{{ display }}' }), + animate( + '{{ time}} {{ easing }}', + keyframes([ + style({ opacity: '0', transform: '{{ transform }} scale(0.0)', offset: 0 }), + style({ opacity: '0', transform: '{{ transform }} scale(0.8)', offset: 0.5 }), + style({ opacity: '1', transform: '{{ transform }} scale(1.0)', offset: 1 }) + ]) + ) + ], + { + params: { + time: '350ms', + easing: 'cubic-bezier(.7,.31,.72,1.47)', + display: 'block', + transform: 'translate(-50%, -50%)' + } + } +); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts index ecbec49e99..ad6cf81e70 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts @@ -1,19 +1,40 @@ -import { trigger, state, style, transition, animate } from '@angular/animations'; +import { animate, animation, trigger, state, style, transition, useAnimation } from '@angular/animations'; + +export const collapseY = animation( + [ + style({ height: '*', overflow: 'hidden', 'box-sizing': 'border-box' }), + animate('{{ time }} {{ easing }}', style({ height: '0', padding: '0px' })) + ], + { params: { time: '350ms', easing: 'ease' } } +); + +export const collapseX = animation( + [ + style({ width: '*', overflow: 'hidden', 'box-sizing': 'border-box' }), + animate('{{ time }} {{ easing }}', style({ width: '0', padding: '0px' })) + ], + { params: { time: '350ms', easing: 'ease' } } +); + +export const expandY = animation( + [ + style({ height: '0', overflow: 'hidden', 'box-sizing': 'border-box' }), + animate('{{ time }} {{ easing }}', style({ height: '*', padding: '*' })) + ], + { params: { time: '350ms', easing: 'ease' } } +); + +export const expandX = animation( + [ + style({ width: '0', overflow: 'hidden', 'box-sizing': 'border-box' }), + animate('{{ time }} {{ easing }}', style({ width: '*', padding: '*' })) + ], + { params: { time: '350ms', easing: 'ease' } } +); export const collapse = trigger('collapse', [ - state( - 'open', - style({ - height: '*', - overflow: 'hidden', - }), - ), - state( - 'close', - style({ - height: '0px', - overflow: 'hidden', - }), - ), - transition(`open <=> close`, animate('{{duration}}ms'), { params: { duration: '350' } }), + state('collapsed', style({ height: '0', overflow: 'hidden' })), + state('expanded', style({ height: '*', overflow: 'hidden' })), + transition('expanded => collapsed', useAnimation(collapseY)), + transition('collapsed => expanded', useAnimation(expandY)) ]); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts index fb523e0d56..3321974a77 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/fade.animations.ts @@ -1,19 +1,74 @@ -import { animate, state, style, transition, trigger } from '@angular/animations'; - -export const fade = trigger('fade', [ - state('void', style({ opacity: 1 })), - transition(':enter', [style({ opacity: 0 }), animate(250)]), - transition(':leave', animate(250, style({ opacity: 0 }))), -]); - -export const fadeWithStates = trigger('fadeInOut', [ - state('out', style({ opacity: 0 })), - state('in', style({ opacity: 1 })), - transition('in <=> out', [animate(250)]), -]); - -export const fadeIn = trigger('fadeIn', [ - state('*', style({ opacity: 1 })), - transition('* => *', [style({ opacity: 0 }), animate(250)]), - transition(':enter', [style({ opacity: 0 }), animate(250)]), -]); +import { animate, animation, style } from '@angular/animations'; + +export const fadeIn = animation([style({ opacity: '0' }), animate('{{ time}} {{ easing }}', style({ opacity: '1' }))], { + params: { time: '350ms', easing: 'ease' }, +}); + +export const fadeOut = animation( + [style({ opacity: '1' }), animate('{{ time}} {{ easing }}', style({ opacity: '0' }))], + { params: { time: '350ms', easing: 'ease' } }, +); + +export const fadeInDown = animation( + [ + style({ opacity: '0', transform: '{{ transform }} translateY(-20px)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateY(0)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeInUp = animation( + [ + style({ opacity: '0', transform: '{{ transform }} translateY(20px)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateY(0)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeInLeft = animation( + [ + style({ opacity: '0', transform: '{{ transform }} translateX(20px)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateX(0)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeInRight = animation( + [ + style({ opacity: '0', transform: '{{ transform }} translateX(-20px)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '1', transform: '{{ transform }} translateX(0)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeOutDown = animation( + [ + style({ opacity: '1', transform: '{{ transform }} translateY(0)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateY(20px)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeOutUp = animation( + [ + style({ opacity: '1', transform: '{{ transform }} translateY(0)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateY(-20px)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeOutLeft = animation( + [ + style({ opacity: '1', transform: '{{ transform }} translateX(0)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateX(20px)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); + +export const fadeOutRight = animation( + [ + style({ opacity: '1', transform: '{{ transform }} translateX(0)' }), + animate('{{ time }} {{ easing }}', style({ opacity: '0', transform: '{{ transform }} translateX(-20px)' })), + ], + { params: { time: '350ms', easing: 'ease', transform: '' } }, +); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts index 50772ccedc..5a7009be17 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts @@ -1,3 +1,4 @@ export * from './collapse.animations'; export * from './fade.animations'; export * from './slide.animations'; +export * from './bounce.animations'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html new file mode 100644 index 0000000000..fa8f7df3da --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html @@ -0,0 +1,13 @@ + diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts index 09e8e588a5..1b0d4a5a4c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts @@ -5,21 +5,7 @@ import { ConfigState, ABP } from '@abp/ng.core'; @Component({ selector: 'abp-breadcrumb', - template: ` - - `, + templateUrl: './breadcrumb.component.html' }) export class BreadcrumbComponent implements OnInit { show: boolean; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts index 7a915d667c..50dab84c0f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts @@ -6,23 +6,23 @@ import { Component, Input } from '@angular/core'; - `, + ` }) export class ButtonComponent { @Input() - buttonClass: string = 'btn btn-primary'; + buttonClass = 'btn btn-primary'; @Input() - type: string = 'button'; + type = 'button'; @Input() iconClass: string; @Input() - loading: boolean = false; + loading = false; @Input() - disabled: boolean = false; + disabled = false; get icon(): string { return `${this.loading ? 'fa fa-pulse fa-spinner' : this.iconClass || 'd-none'}`; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts index 1f7dcab4af..09c7a8da5d 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts @@ -38,17 +38,16 @@ export class ChangePasswordComponent implements OnInit, OnChanges { this.visibleChange.emit(value); } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @ViewChild('modalContent', { static: false }) modalContent: TemplateRef; form: FormGroup; - modalBusy: boolean = false; + modalBusy = false; - mapErrorsFn: Validation.MapErrorsFn = function(errors, groupErrors, control) { + mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => { if (PASSWORD_FIELDS.indexOf(control.name) < 0) return errors; return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch')); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts index a3ff6bbdaa..45829c8ee4 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts @@ -6,7 +6,7 @@ import { Input, OnDestroy, Output, - ChangeDetectorRef, + ChangeDetectorRef } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { chartJsLoaded$ } from '../../utils/widget-utils'; @@ -14,7 +14,7 @@ declare const Chart: any; @Component({ selector: 'abp-chart', - templateUrl: './chart.component.html', + templateUrl: './chart.component.html' }) export class ChartComponent implements AfterViewInit, OnDestroy { @Input() type: string; @@ -27,11 +27,12 @@ export class ChartComponent implements AfterViewInit, OnDestroy { @Input() height: string; - @Input() responsive: boolean = true; + @Input() responsive = true; - @Output() onDataSelect: EventEmitter = new EventEmitter(); + // tslint:disable-next-line: no-output-on-prefix + @Output() readonly onDataSelect: EventEmitter = new EventEmitter(); - @Output() initialized = new BehaviorSubject(this); + @Output() readonly initialized = new BehaviorSubject(this); private _initialized: boolean; @@ -61,6 +62,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy { ngAfterViewInit() { chartJsLoaded$.subscribe(() => { try { + // tslint:disable-next-line: no-unused-expression Chart; } catch (error) { console.error(`Chart is not found. Import the Chart from app.module like shown below: @@ -76,16 +78,20 @@ export class ChartComponent implements AfterViewInit, OnDestroy { onCanvasClick = event => { if (this.chart) { - let element = this.chart.getElementAtEvent(event); - let dataset = this.chart.getDatasetAtEvent(event); + const element = this.chart.getElementAtEvent(event); + const dataset = this.chart.getDatasetAtEvent(event); if (element && element[0] && dataset) { - this.onDataSelect.emit({ originalEvent: event, element: element[0], dataset: dataset }); + this.onDataSelect.emit({ + originalEvent: event, + element: element[0], + dataset + }); } } }; initChart = () => { - let opts = this.options || {}; + const opts = this.options || {}; opts.responsive = this.responsive; // allows chart to resize in responsive mode @@ -97,7 +103,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy { type: this.type, data: this.data, options: this.options, - plugins: this.plugins, + plugins: this.plugins }); this.cdRef.detectChanges(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts index 39cf9b73a2..a1911b38ff 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts @@ -4,6 +4,7 @@ import { Toaster } from '../../models/toaster'; @Component({ selector: 'abp-confirmation', + // tslint:disable-next-line: component-max-inline-declarations template: ` diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss similarity index 100% rename from npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.scss rename to npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts new file mode 100644 index 0000000000..b86eb78c6b --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts @@ -0,0 +1,23 @@ +import { Component, Renderer2, ElementRef } from '@angular/core'; +import { Config } from '@abp/ng.core'; + +@Component({ + selector: 'abp-error', + templateUrl: './error.component.html', + styleUrls: ['error.component.scss'], +}) +export class ErrorComponent { + title: string | Config.LocalizationWithDefault = 'Oops!'; + + details: string | Config.LocalizationWithDefault = 'Sorry, an error has occured.'; + + renderer: Renderer2; + + elementRef: ElementRef; + + host: any; + + destroy() { + this.renderer.removeChild(this.host, this.elementRef.nativeElement); + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts deleted file mode 100644 index 708b48dc47..0000000000 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Component, Renderer2, ElementRef } from '@angular/core'; - -@Component({ - selector: 'abp-error', - template: ` -
- -
-
-
-

- {{ title | abpLocalization }} -

-
- {{ details | abpLocalization }} -
- -
-
-
-
- `, - styleUrls: ['error.component.scss'], -}) -export class ErrorComponent { - title = 'Oops!'; - - details = 'Sorry, an error has occured.'; - - renderer: Renderer2; - - elementRef: ElementRef; - - host: any; - - destroy() { - this.renderer.removeChild(this.host, this.elementRef.nativeElement); - } -} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index da797cc844..d81550edaa 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -20,27 +20,9 @@ import { filter } from 'rxjs/operators'; >
`, - styleUrls: ['./loader-bar.component.scss'], + styleUrls: ['./loader-bar.component.scss'] }) export class LoaderBarComponent implements OnDestroy { - @Input() - containerClass: string = 'abp-loader-bar'; - - @Input() - color: string = '#77b6ff'; - - @Input() - isLoading: boolean = false; - - @Input() - filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; - - progressLevel: number = 0; - - interval: Subscription; - - timer: Subscription; - get boxShadow(): string { return `0 0 10px rgba(${this.color}, 0.5)`; } @@ -50,7 +32,7 @@ export class LoaderBarComponent implements OnDestroy { .pipe( ofActionSuccessful(StartLoader, StopLoader), filter(this.filter), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(action => { if (action instanceof StartLoader) this.startLoading(); @@ -61,15 +43,32 @@ export class LoaderBarComponent implements OnDestroy { .pipe( filter( event => - event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError, + event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError ), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(event => { if (event instanceof NavigationStart) this.startLoading(); else this.stopLoading(); }); } + @Input() + containerClass = 'abp-loader-bar'; + + @Input() + color = '#77b6ff'; + + @Input() + isLoading = false; + + progressLevel = 0; + + interval: Subscription; + + timer: Subscription; + + @Input() + filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; ngOnDestroy() { this.interval.unsubscribe(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md deleted file mode 100644 index cf51e03845..0000000000 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md +++ /dev/null @@ -1,25 +0,0 @@ -

 Abp Modal

- -Example Usage: - -```html - - -

Modal Title

-
- - -
- - -
-
- - - - - -
- - -``` diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.animations.ts new file mode 100644 index 0000000000..22513b0c6f --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.animations.ts @@ -0,0 +1,12 @@ +import { transition, trigger, useAnimation } from '@angular/animations'; +import { fadeIn, fadeInDown, fadeOut } from '../../animations/fade.animations'; + +export const backdropAnimation = trigger('backdrop', [ + transition(':enter', useAnimation(fadeIn)), + transition(':leave', useAnimation(fadeOut)), +]); + +export const dialogAnimation = trigger('dialog', [ + transition(':enter', useAnimation(fadeInDown)), + transition(':leave', useAnimation(fadeOut)), +]); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html index 75c48d2d15..96a43f4eff 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html @@ -1,38 +1,29 @@ -