Create Boiler Template project

This commit is contained in:
sanjoy 2022-08-05 11:19:02 +05:30
commit 35ebc530c4
381 changed files with 72770 additions and 0 deletions
.gitattributes.gitignoreLICENSEREADME.md
_screenshots
angular
.browserslistrc.editorconfig.gitignore
.vscode
DockerfileMeetingSchedule.AngularUI.csprojMeetingSchedule.AngularUI.sln
Properties
README.mdangular.jsonapp.config
e2e
karma.conf.js
nswag
package.jsonprotractor.conf.js
src
account
app-initializer.ts
app
assets

2
.gitattributes vendored Normal file

@ -0,0 +1,2 @@
**/wwwroot/lib/** linguist-vendored
**/wwwroot/css/** linguist-vendored

9
.gitignore vendored Normal file

@ -0,0 +1,9 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# IDEs and editors
.vs/
obj/
bin/
aspnet-core/src/MeetingSchedule.Web.Host/App_Data/Logs/
aspnet-core/src/MeetingSchedule.Web.Mvc/App_Data/Logs/
aspnet-core/src/MeetingSchedule.Migrator/Logs/Logs.txt

21
LICENSE Normal file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 ASP.NET Boilerplate
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

37
README.md Normal file

@ -0,0 +1,37 @@
# Important
Issues of this repository are tracked on https://github.com/aspnetboilerplate/aspnetboilerplate. Please create your issues on https://github.com/aspnetboilerplate/aspnetboilerplate/issues.
# Introduction
This is a template to create **ASP.NET Core MVC / Angular** based startup projects for [ASP.NET Boilerplate](https://aspnetboilerplate.com/Pages/Documents). It has 2 different versions:
1. [ASP.NET Core MVC & jQuery](https://aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template-Core) (server rendered multi-page application).
2. [ASP.NET Core & Angular](https://aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template-Angular) (single page application).
User Interface is based on [AdminLTE theme](https://github.com/ColorlibHQ/AdminLTE).
# Download
Create & download your project from https://aspnetboilerplate.com/Templates
# Screenshots
#### Sample Dashboard Page
![](_screenshots/module-zero-core-template-ui-home.png)
#### User Creation Modal
![](_screenshots/module-zero-core-template-ui-user-create-modal.png)
#### Login Page
![](_screenshots/module-zero-core-template-ui-login.png)
# Documentation
* [ASP.NET Core MVC & jQuery version.](https://aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template-Core)
* [ASP.NET Core & Angular version.](https://aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template-Angular)
# License
[MIT](LICENSE).

BIN
_screenshots/ui-home.png Normal file

Binary file not shown.

After

(image error) Size: 100 KiB

BIN
_screenshots/ui-login.png Normal file

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 70 KiB

12
angular/.browserslistrc Normal file

@ -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'.

12
angular/.editorconfig Normal file

@ -0,0 +1,12 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

43
angular/.gitignore vendored Normal file

@ -0,0 +1,43 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
.vs/
[Oo]bj/
bin/
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
#System Files
.DS_Store
Thumbs.db

16
angular/.vscode/launch.json vendored Normal file

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:4200",
"sourceMaps": true,
"webRoot": "${workspaceFolder}"
}
]
}

3
angular/Dockerfile Normal file

@ -0,0 +1,3 @@
FROM nginx
COPY . /usr/share/nginx/html

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>angular</AssemblyName>
<PackageId>angular</PackageId>
</PropertyGroup>
<ItemGroup>
<Compile Remove="node_modules\**" />
<EmbeddedResource Remove="node_modules\**" />
<None Remove="node_modules\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="dist" />
<None Include="app.config" />
<None Update="wwwroot\**\*;web.config">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

@ -0,0 +1,33 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeetingSchedule.AngularUI", "MeetingSchedule.AngularUI.csproj", "{11BD8782-23F0-45A0-9A00-A213373B0F5D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|x64.ActiveCfg = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|x64.Build.0 = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|x86.ActiveCfg = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Debug|x86.Build.0 = Debug|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|Any CPU.Build.0 = Release|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|x64.ActiveCfg = Release|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|x64.Build.0 = Release|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|x86.ActiveCfg = Release|Any CPU
{11BD8782-23F0-45A0-9A00-A213373B0F5D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14424/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"MeetingSchedule.AngularUI": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

27
angular/README.md Normal file

@ -0,0 +1,27 @@
# MeetingScheduleTemplate
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-beta.31.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Before running the tests make sure you are serving the app via `ng serve`.
## 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).

205
angular/angular.json Normal file

@ -0,0 +1,205 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"MeetingSchedule": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/favicon.ico",
{
"glob": "abp.signalr.js",
"input": "node_modules/abp-web-resources/Abp/Framework/scripts/libs",
"output": "/assets/abp"
},
{
"glob": "abp.signalr-client.js",
"input": "node_modules/abp-web-resources/Abp/Framework/scripts/libs",
"output": "/assets/abp"
}
],
"styles": [
"node_modules/famfamfam-flags/dist/sprite/famfamfam-flags.css",
"node_modules/sweetalert2/dist/sweetalert2.css",
"src/assets/freeze-ui/freeze-ui.css",
"node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"node_modules/admin-lte-css-only/css/adminlte.min.css",
"src/shared/core.less"
],
"scripts": [
"node_modules/moment/min/moment.min.js",
"node_modules/@aspnet/signalr/dist/browser/signalr.min.js",
"node_modules/sweetalert2/dist/sweetalert2.js",
"src/assets/freeze-ui/freeze-ui.js",
"node_modules/push.js/bin/push.min.js",
"node_modules/abp-web-resources/Abp/Framework/scripts/abp.js",
"src/assets/abp-web-resources/abp.sweet-alert.js",
"src/assets/abp-web-resources/abp.freeze-ui.js",
"node_modules/abp-web-resources/Abp/Framework/scripts/libs/abp.moment.js"
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"hmr": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
}
},
"defaultConfiguration": ""
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "MeetingSchedule:build"
},
"configurations": {
"production": {
"browserTarget": "MeetingSchedule:build:production"
},
"hmr": {
"browserTarget": "MeetingSchedule:build:hmr"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "MeetingSchedule:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"assets": [
"src/assets",
"src/favicon.ico",
{
"glob": "abp.signalr.js",
"input": "node_modules/abp-web-resources/Abp/Framework/scripts/libs",
"output": "/assets/abp"
},
{
"glob": "abp.signalr-client.js",
"input": "node_modules/abp-web-resources/Abp/Framework/scripts/libs",
"output": "/assets/abp"
}
],
"styles": [
"node_modules/famfamfam-flags/dist/sprite/famfamfam-flags.css",
"node_modules/sweetalert2/dist/sweetalert2.css",
"src/assets/freeze-ui/freeze-ui.css",
"node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"node_modules/admin-lte-css-only/css/adminlte.min.css",
"src/shared/core.less"
],
"scripts": [
"node_modules/moment/min/moment.min.js",
"node_modules/@aspnet/signalr/dist/browser/signalr.min.js",
"node_modules/sweetalert2/dist/sweetalert2.js",
"src/assets/freeze-ui/freeze-ui.js",
"node_modules/push.js/bin/push.min.js",
"node_modules/abp-web-resources/Abp/Framework/scripts/abp.js",
"src/assets/abp-web-resources/abp.sweet-alert.js",
"src/assets/abp-web-resources/abp.freeze-ui.js",
"node_modules/abp-web-resources/Abp/Framework/scripts/libs/abp.moment.js"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.json"
],
"exclude": []
}
}
}
},
"MeetingSchedule-e2e": {
"root": "",
"sourceRoot": "",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "MeetingSchedule:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"e2e/tsconfig.json"
],
"exclude": []
}
}
}
}
},
"defaultProject": "MeetingSchedule",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"style": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}

5
angular/app.config Normal file

@ -0,0 +1,5 @@
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>

@ -0,0 +1,14 @@
import { MeetingScheduleTemplatePage } from './app.po';
describe('MeetingSchedule App', function() {
let page: MeetingScheduleTemplatePage;
beforeEach(() => {
page = new MeetingScheduleTemplatePage();
});
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('app works!');
});
});

11
angular/e2e/app.po.ts Normal file

@ -0,0 +1,11 @@
import { browser, element, by } from 'protractor';
export class MeetingScheduleTemplatePage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

29
angular/e2e/tsconfig.json Normal file

@ -0,0 +1,29 @@
{
"compileOnSave": false,
"compilerOptions": {
"declaration": false,
"experimentalDecorators": true,
"lib": [
"es2016"
],
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../dist/out-tsc-e2e",
"sourceMap": true,
"target": "es6",
"typeRoots": [
"../node_modules/@types"
],
"baseUrl": ".",
"paths": {
"@abp/*": [ "../node_modules/abp-ng2-module/dist/src/*" ],
"@app/*": [ "./app/*" ],
"@shared/*": [ "./shared/*" ],
"@node_modules/*": [ "../node_modules/*" ]
}
},
"files": [
"../src/polyfills.ts",
"../src/test.ts"
],
}

41
angular/karma.conf.js Normal file

@ -0,0 +1,41 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/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-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
files: [
],
preprocessors: {
},
mime: {
'text/x-typescript': ['ts','tsx']
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
config: './.angular-cli.json',
environment: 'dev'
},
reporters: config.angularCli && config.angularCli.codeCoverage
? ['progress', 'coverage-istanbul']
: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

@ -0,0 +1 @@
"..\node_modules\.bin\nswag" run

@ -0,0 +1,209 @@
{
"runtime": "Default",
"defaultVariables": null,
"documentGenerator": {
"fromDocument": {
"url": "https://localhost:44311/swagger/v1/swagger.json",
"output": null
}
},
"codeGenerators": {
"openApiToTypeScriptClient": {
"className": "{controller}ServiceProxy",
"moduleName": "",
"namespace": "",
"typeScriptVersion": 2.0,
"template": "Angular",
"promiseType": "Promise",
"httpClass": "HttpClient",
"useSingletonProvider": false,
"injectionTokenType": "InjectionToken",
"rxJsVersion": 6.0,
"dateTimeType": "MomentJS",
"nullValue": "Undefined",
"generateClientClasses": true,
"generateClientInterfaces": false,
"generateOptionalParameters": false,
"exportTypes": true,
"wrapDtoExceptions": false,
"exceptionClass": "ApiException",
"clientBaseClass": null,
"wrapResponses": false,
"wrapResponseMethods": [],
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"protectedMethods": [],
"configurationClass": null,
"useTransformOptionsMethod": false,
"useTransformResultMethod": false,
"generateDtoTypes": true,
"operationGenerationMode": "MultipleClientsFromPathSegments",
"markOptionalProperties": false,
"generateCloneMethod": true,
"typeStyle": "Class",
"classTypes": [],
"extendedClasses": [],
"extensionCode": null,
"generateDefaultValues": true,
"excludedTypeNames": [],
"excludedParameterNames": [],
"handleReferences": false,
"generateConstructorInterface": true,
"convertConstructorInterfaceData": false,
"importRequiredTypes": true,
"useGetBaseUrlMethod": false,
"baseUrlTokenName": "API_BASE_URL",
"queryNullValue": "",
"inlineNamedDictionaries": false,
"inlineNamedAny": false,
"templateDirectory": null,
"typeNameGeneratorType": null,
"propertyNameGeneratorType": null,
"enumNameGeneratorType": null,
"serviceHost": null,
"serviceSchemes": null,
"output": "../src/shared/service-proxies/service-proxies.ts"
},
"openApiToCSharpClient": {
"clientBaseClass": null,
"configurationClass": null,
"generateClientClasses": true,
"generateClientInterfaces": false,
"injectHttpClient": false,
"disposeHttpClient": true,
"protectedMethods": [],
"generateExceptionClasses": true,
"exceptionClass": "SwaggerException",
"wrapDtoExceptions": true,
"useHttpClientCreationMethod": false,
"httpClientType": "System.Net.Http.HttpClient",
"useHttpRequestMessageCreationMethod": false,
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateSyncMethods": false,
"exposeJsonSerializerSettings": false,
"clientClassAccessModifier": "public",
"typeAccessModifier": "public",
"generateContractsOutput": false,
"contractsNamespace": null,
"contractsOutputFilePath": null,
"parameterDateTimeFormat": "s",
"generateUpdateJsonSerializerSettingsMethod": true,
"serializeTypeInformation": false,
"queryNullValue": "",
"className": "{controller}Client",
"operationGenerationMode": "MultipleClientsFromOperationId",
"additionalNamespaceUsages": [],
"additionalContractNamespaceUsages": [],
"generateOptionalParameters": false,
"generateJsonMethods": true,
"enforceFlagEnums": false,
"parameterArrayType": "System.Collections.Generic.IEnumerable",
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
"responseArrayType": "System.Collections.ObjectModel.ObservableCollection",
"responseDictionaryType": "System.Collections.Generic.Dictionary",
"wrapResponses": false,
"wrapResponseMethods": [],
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"namespace": "MyNamespace",
"requiredPropertiesMustBeDefined": true,
"dateType": "System.DateTime",
"jsonConverters": null,
"anyType": "object",
"dateTimeType": "System.DateTime",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.ObjectModel.ObservableCollection",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryType": "System.Collections.Generic.Dictionary",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"classStyle": "Inpc",
"generateDefaultValues": true,
"generateDataAnnotations": true,
"excludedTypeNames": [],
"excludedParameterNames": [],
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"jsonSerializerSettingsTransformationMethod": null,
"inlineNamedArrays": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedAny": false,
"generateDtoTypes": true,
"generateOptionalPropertiesAsNullable": false,
"templateDirectory": null,
"typeNameGeneratorType": null,
"propertyNameGeneratorType": null,
"enumNameGeneratorType": null,
"serviceHost": null,
"serviceSchemes": null,
"output": null
},
"openApiToCSharpController": {
"controllerBaseClass": null,
"controllerStyle": "Partial",
"controllerTarget": "AspNet",
"useCancellationToken": false,
"useActionResultType": false,
"generateModelValidationAttributes": false,
"routeNamingStrategy": "None",
"className": "{controller}",
"operationGenerationMode": "MultipleClientsFromOperationId",
"additionalNamespaceUsages": [
"System.Web.Http"
],
"additionalContractNamespaceUsages": [],
"generateOptionalParameters": false,
"generateJsonMethods": true,
"enforceFlagEnums": false,
"parameterArrayType": "System.Collections.Generic.IEnumerable",
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
"responseArrayType": "System.Collections.ObjectModel.ObservableCollection",
"responseDictionaryType": "System.Collections.Generic.Dictionary",
"wrapResponses": false,
"wrapResponseMethods": [],
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"namespace": "MyNamespace",
"requiredPropertiesMustBeDefined": true,
"dateType": "System.DateTime",
"jsonConverters": null,
"anyType": "object",
"dateTimeType": "System.DateTime",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.Generic.IEnumerable",
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
"dictionaryType": "System.Collections.Generic.Dictionary",
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"classStyle": "Inpc",
"generateDefaultValues": true,
"generateDataAnnotations": true,
"excludedTypeNames": [],
"excludedParameterNames": [],
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"jsonSerializerSettingsTransformationMethod": null,
"inlineNamedArrays": false,
"inlineNamedDictionaries": false,
"inlineNamedTuples": true,
"inlineNamedAny": false,
"generateDtoTypes": true,
"generateOptionalPropertiesAsNullable": false,
"templateDirectory": null,
"typeNameGeneratorType": null,
"propertyNameGeneratorType": null,
"enumNameGeneratorType": null,
"serviceHost": null,
"serviceSchemes": null,
"output": null
}
}
}

70
angular/package.json Normal file

@ -0,0 +1,70 @@
{
"name": "MeetingSchedule",
"version": "4.7.1",
"private": true,
"license": "MIT",
"scripts": {
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "protractor",
"hmr": "ng serve --host 0.0.0.0 --port 4200 --hmr",
"lint": "tslint --force --project src/tsconfig.json src/**/*.ts -t verbose",
"ng": "ng",
"start": "ng serve --host 0.0.0.0 --port 4200",
"test": "ng test"
},
"dependencies": {
"@angular/animations": "^12.0.5",
"@angular/common": "^12.0.5",
"@angular/compiler": "^12.0.5",
"@angular/core": "^12.0.5",
"@angular/forms": "^12.0.5",
"@angular/platform-browser": "^12.0.5",
"@angular/platform-browser-dynamic": "^12.0.5",
"@angular/router": "^12.0.5",
"@aspnet/signalr": "^1.1.4",
"@fortawesome/fontawesome-free": "^5.15.3",
"abp-ng2-module": "^6.2.0",
"abp-web-resources": "^5.3.0",
"admin-lte-css-only": "^3.1.0",
"core-js": "^3.2.1",
"famfamfam-flags": "^1.0.0",
"lodash-es": "^4.17.15",
"moment": "2.24.0",
"moment-timezone": "0.5.33",
"ngx-bootstrap": "^5.6.1",
"ngx-pagination": "^5.0.0",
"push.js": "1.0.12",
"rxjs": "^6.4.0",
"sweetalert2": "^10.15.6",
"ts-helpers": "^1.1.2",
"tslib": "^2.0.0",
"web-animations-js": "^2.3.2",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.0.5",
"@angular/cli": "^12.0.5",
"@angular/compiler-cli": "^12.0.5",
"@angularclass/hmr": "^2.1.3",
"@types/jasmine": "~3.6.0",
"@types/lodash-es": "^4.17.3",
"@types/moment-timezone": "^0.5.30",
"@types/node": "^13.13.4",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.4",
"karma-chrome-launcher": "~3.1.0",
"karma-cli": "^2.0.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"nswag": "^13.4.2",
"protractor": "~7.0.0",
"ts-node": "^8.10.1",
"tslint": "~6.1.0",
"typescript": "4.2.4",
"webpack-bundle-analyzer": "^3.7.0"
},
"angular-cli": {}
}

@ -0,0 +1,32 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
/*global jasmine */
var SpecReporter = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
useAllAngular2AppRoots: true,
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
});
},
onPrepare: function() {
jasmine.getEnv().addReporter(new SpecReporter());
}
};

@ -0,0 +1,24 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { AccountComponent } from './account.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: AccountComponent,
children: [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent }
]
}
])
],
exports: [
RouterModule
]
})
export class AccountRoutingModule { }

@ -0,0 +1,15 @@
<div class="login-box">
<account-header></account-header>
<div class="card">
<div *ngIf="showTenantChange()" class="card-header">
<tenant-change></tenant-change>
</div>
<div class="card-body login-card-body">
<router-outlet></router-outlet>
</div>
<div class="card-footer">
<account-languages></account-languages>
</div>
</div>
<account-footer></account-footer>
</div>

@ -0,0 +1,26 @@
import {
Component,
OnInit,
ViewEncapsulation,
Injector,
Renderer2
} from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
@Component({
templateUrl: './account.component.html',
encapsulation: ViewEncapsulation.None
})
export class AccountComponent extends AppComponentBase implements OnInit {
constructor(injector: Injector, private renderer: Renderer2) {
super(injector);
}
showTenantChange(): boolean {
return abp.multiTenancy.isEnabled;
}
ngOnInit(): void {
this.renderer.addClass(document.body, 'login-page');
}
}

@ -0,0 +1,50 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientJsonpModule } from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { ModalModule } from 'ngx-bootstrap/modal';
import { AccountRoutingModule } from './account-routing.module';
import { ServiceProxyModule } from '@shared/service-proxies/service-proxy.module';
import { SharedModule } from '@shared/shared.module';
import { AccountComponent } from './account.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { AccountLanguagesComponent } from './layout/account-languages.component';
import { AccountHeaderComponent } from './layout/account-header.component';
import { AccountFooterComponent } from './layout/account-footer.component';
// tenants
import { TenantChangeComponent } from './tenant/tenant-change.component';
import { TenantChangeDialogComponent } from './tenant/tenant-change-dialog.component';
@NgModule({
imports: [
CommonModule,
FormsModule,
HttpClientModule,
HttpClientJsonpModule,
SharedModule,
ServiceProxyModule,
AccountRoutingModule,
ModalModule.forChild()
],
declarations: [
AccountComponent,
LoginComponent,
RegisterComponent,
AccountLanguagesComponent,
AccountHeaderComponent,
AccountFooterComponent,
// tenant
TenantChangeComponent,
TenantChangeDialogComponent,
],
entryComponents: [
// tenant
TenantChangeDialogComponent
]
})
export class AccountModule {
}

@ -0,0 +1,9 @@
<div class="row">
<div class="col-md-12 text-center">
<small>
Copyright &copy; {{ currentYear }}
<b class="ml-2">{{ "Version" | localize }}</b>
{{ versionText }}
</small>
</div>
</div>

@ -0,0 +1,23 @@
import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
@Component({
selector: 'account-footer',
templateUrl: './account-footer.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AccountFooterComponent extends AppComponentBase {
currentYear: number;
versionText: string;
constructor(injector: Injector) {
super(injector);
this.currentYear = new Date().getFullYear();
this.versionText =
this.appSession.application.version +
' [' +
this.appSession.application.releaseDate.format('YYYYDDMM') +
']';
}
}

@ -0,0 +1,3 @@
<div class="login-logo">
<a href="/"><b>MeetingSchedule</b></a>
</div>

@ -0,0 +1,8 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'account-header',
templateUrl: './account-header.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AccountHeaderComponent {}

@ -0,0 +1,18 @@
<div class="text-center">
<ng-container *ngFor="let language of languages">
<a
*ngIf="language.name != currentLanguage.name"
href="javascript:void(0);"
(click)="changeLanguage(language.name)"
>
<span
title="{{ language.displayName }}"
[attr.class.current-language-icon]="
language.name != currentLanguage.name
"
>
<i class="d-inline-block mx-1 {{ language.icon }}"></i>
</span>
</a>
</ng-container>
</div>

@ -0,0 +1,42 @@
import {
Component,
OnInit,
Injector,
ChangeDetectionStrategy
} from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import { filter as _filter } from 'lodash-es';
@Component({
selector: 'account-languages',
templateUrl: './account-languages.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AccountLanguagesComponent extends AppComponentBase
implements OnInit {
languages: abp.localization.ILanguageInfo[];
currentLanguage: abp.localization.ILanguageInfo;
constructor(injector: Injector) {
super(injector);
}
ngOnInit() {
this.languages = _filter(
this.localization.languages,
(l) => !l.isDisabled
);
this.currentLanguage = this.localization.currentLanguage;
}
changeLanguage(languageName: string): void {
abp.utils.setCookieValue(
'Abp.Localization.CultureName',
languageName,
new Date(new Date().getTime() + 5 * 365 * 86400000), // 5 year
abp.appPath
);
location.reload();
}
}

@ -0,0 +1,83 @@
<div [@routerTransition]>
<h4 class="text-center mb-3">{{ "LogIn" | localize }}</h4>
<form novalidate autocomplete="off" #loginForm="ngForm" (ngSubmit)="login()">
<div class="form-group">
<div class="input-group">
<input
type="text"
class="form-control"
name="userNameOrEmailAddress"
[(ngModel)]="authService.authenticateModel.userNameOrEmailAddress"
[placeholder]="'UserNameOrEmail' | localize"
required
maxlength="256"
#userNameOrEmailAddressModel="ngModel"
#userNameOrEmailAddressEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-user"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="userNameOrEmailAddressModel"
[controlEl]="userNameOrEmailAddressEl"
></abp-validation-summary>
</div>
<div class="form-group">
<div class="input-group">
<input
type="password"
class="form-control"
name="password"
[(ngModel)]="authService.authenticateModel.password"
[placeholder]="'Password' | localize"
required
maxlength="32"
#passwordModel="ngModel"
#passwordEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="passwordModel"
[controlEl]="passwordEl"
></abp-validation-summary>
</div>
<div class="form-group row">
<div class="col-md-8">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
id="rememberMe"
name="rememberMe"
[(ngModel)]="authService.rememberMe"
/>
<label for="rememberMe" class="custom-control-label">
{{ "RememberMe" | localize }}
</label>
</div>
</div>
<div class="col-md-4">
<button
type="submit"
class="btn btn-primary btn-block"
[disabled]="!loginForm.form.valid || submitting"
>
{{ "LogIn" | localize }}
</button>
</div>
</div>
</form>
<p *ngIf="isSelfRegistrationAllowed" class="mb-1">
<a [routerLink]="['../register']">
<i class="fa fa-plus-circle"></i> {{ "Register" | localize }}
</a>
</p>
</div>

@ -0,0 +1,38 @@
import { Component, Injector } from '@angular/core';
import { AbpSessionService } from 'abp-ng2-module';
import { AppComponentBase } from '@shared/app-component-base';
import { accountModuleAnimation } from '@shared/animations/routerTransition';
import { AppAuthService } from '@shared/auth/app-auth.service';
@Component({
templateUrl: './login.component.html',
animations: [accountModuleAnimation()]
})
export class LoginComponent extends AppComponentBase {
submitting = false;
constructor(
injector: Injector,
public authService: AppAuthService,
private _sessionService: AbpSessionService
) {
super(injector);
}
get multiTenancySideIsTeanant(): boolean {
return this._sessionService.tenantId > 0;
}
get isSelfRegistrationAllowed(): boolean {
if (!this._sessionService.tenantId) {
return false;
}
return true;
}
login(): void {
this.submitting = true;
this.authService.authenticate(() => (this.submitting = false));
}
}

@ -0,0 +1,147 @@
<div [@routerTransition]>
<h4 class="text-center mb-3">{{ "Register" | localize }}</h4>
<form autocomplete="off" #registerForm="ngForm" (ngSubmit)="save()">
<div class="form-group">
<div class="input-group">
<input
type="text"
class="form-control"
name="name"
placeholder="{{ 'Name' | localize }}"
required
maxlength="64"
[(ngModel)]="model.name"
#nameModel="ngModel"
#nameEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-arrow-left"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
<div class="form-group">
<div class="input-group">
<input
type="text"
class="form-control"
name="surname"
placeholder="{{ 'Surname' | localize }}"
required
maxlength="64"
[(ngModel)]="model.surname"
#surnameModel="ngModel"
#surnameEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-arrow-left"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="surnameModel"
[controlEl]="surnameEl"
></abp-validation-summary>
</div>
<div class="form-group">
<div class="input-group">
<input
type="email"
class="form-control"
name="emailAddress"
placeholder="{{ 'EmailAddress' | localize }}"
required
maxlength="256"
pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,})+$"
[(ngModel)]="model.emailAddress"
#emailAddressModel="ngModel"
#emailAddressEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-envelope"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="emailAddressModel"
[controlEl]="emailAddressEl"
></abp-validation-summary>
</div>
<div class="form-group">
<div class="input-group">
<input
type="email"
class="form-control"
name="userName"
placeholder=" {{ 'UserName' | localize }}"
required
maxlength="32"
[(ngModel)]="model.userName"
#userNameModel="ngModel"
#userNameEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-user"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="userNameModel"
[controlEl]="userNameEl"
></abp-validation-summary>
</div>
<div class="form-group">
<div class="input-group">
<input
type="password"
class="form-control"
name="password"
placeholder="{{ 'Password' | localize }}"
[(ngModel)]="model.password"
required
maxlength="32"
#passwordModel="ngModel"
#passwordEl
/>
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<abp-validation-summary
[control]="passwordModel"
[controlEl]="passwordEl"
></abp-validation-summary>
</div>
<div class="row">
<div class="col-8">
<button
type="button"
class="btn btn-default"
[disabled]="saving"
[routerLink]="['../login']"
>
<i class="fa fa-arrow-circle-left"></i> {{ "Back" | localize }}
</button>
</div>
<div class="col-4">
<button
type="submit"
class="btn btn-primary btn-block"
[disabled]="!registerForm.form.valid || saving"
>
{{ "Register" | localize }}
</button>
</div>
</div>
</form>
</div>

@ -0,0 +1,55 @@
import { Component, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { finalize } from 'rxjs/operators';
import { AppComponentBase } from '@shared/app-component-base';
import {
AccountServiceProxy,
RegisterInput,
RegisterOutput
} from '@shared/service-proxies/service-proxies';
import { accountModuleAnimation } from '@shared/animations/routerTransition';
import { AppAuthService } from '@shared/auth/app-auth.service';
@Component({
templateUrl: './register.component.html',
animations: [accountModuleAnimation()]
})
export class RegisterComponent extends AppComponentBase {
model: RegisterInput = new RegisterInput();
saving = false;
constructor(
injector: Injector,
private _accountService: AccountServiceProxy,
private _router: Router,
private authService: AppAuthService
) {
super(injector);
}
save(): void {
this.saving = true;
this._accountService
.register(this.model)
.pipe(
finalize(() => {
this.saving = false;
})
)
.subscribe((result: RegisterOutput) => {
if (!result.canLogin) {
this.notify.success(this.l('SuccessfullyRegistered'));
this._router.navigate(['/login']);
return;
}
// Autheticate
this.saving = true;
this.authService.authenticateModel.userNameOrEmailAddress = this.model.userName;
this.authService.authenticateModel.password = this.model.password;
this.authService.authenticate(() => {
this.saving = false;
});
});
}
}

@ -0,0 +1,37 @@
<form
class="form-horizontal"
autocomplete="off"
#changeTenantForm="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'ChangeTenant' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<div class="form-group row">
<label class="col-md-3 col-form-label" for="tenancyName">
{{ "TenancyName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
id="tenancyName"
name="tenancyName"
[(ngModel)]="tenancyName"
maxlength="64"
/>
</div>
</div>
<span>
<i class="fa fa-info-circle"></i>
{{ "LeaveEmptyToSwitchToHost" | localize }}
</span>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!changeTenantForm.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,60 @@
import { Component, Injector } from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { AppComponentBase } from '@shared/app-component-base';
import { AccountServiceProxy } from '@shared/service-proxies/service-proxies';
import { AppTenantAvailabilityState } from '@shared/AppEnums';
import {
IsTenantAvailableInput,
IsTenantAvailableOutput
} from '@shared/service-proxies/service-proxies';
@Component({
templateUrl: './tenant-change-dialog.component.html'
})
export class TenantChangeDialogComponent extends AppComponentBase {
saving = false;
tenancyName = '';
constructor(
injector: Injector,
private _accountService: AccountServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
save(): void {
if (!this.tenancyName) {
abp.multiTenancy.setTenantIdCookie(undefined);
this.bsModalRef.hide();
location.reload();
return;
}
const input = new IsTenantAvailableInput();
input.tenancyName = this.tenancyName;
this.saving = true;
this._accountService.isTenantAvailable(input).subscribe(
(result: IsTenantAvailableOutput) => {
switch (result.state) {
case AppTenantAvailabilityState.Available:
abp.multiTenancy.setTenantIdCookie(result.tenantId);
location.reload();
return;
case AppTenantAvailabilityState.InActive:
this.message.warn(this.l('TenantIsNotActive', this.tenancyName));
break;
case AppTenantAvailabilityState.NotFound:
this.message.warn(
this.l('ThereIsNoTenantDefinedWithName{0}', this.tenancyName)
);
break;
}
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,12 @@
<div *ngIf="isMultiTenancyEnabled" class="text-center tenant-change-component">
<span>
{{ "CurrentTenant" | localize }}:
<span *ngIf="tenancyName" title="{{ name }}">
<strong>{{ tenancyName }}</strong>
</span>
<span *ngIf="!tenancyName">{{ "NotSelected" | localize }}</span>
(<a href="javascript:;" (click)="showChangeModal()">
{{ "Change" | localize }} </a
>)
</span>
</div>

@ -0,0 +1,35 @@
import { Component, OnInit, Injector } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import { TenantChangeDialogComponent } from './tenant-change-dialog.component';
import { BsModalService } from 'ngx-bootstrap/modal';
@Component({
selector: 'tenant-change',
templateUrl: './tenant-change.component.html'
})
export class TenantChangeComponent extends AppComponentBase implements OnInit {
tenancyName = '';
name = '';
constructor(injector: Injector, private _modalService: BsModalService) {
super(injector);
}
get isMultiTenancyEnabled(): boolean {
return abp.multiTenancy.isEnabled;
}
ngOnInit() {
if (this.appSession.tenant) {
this.tenancyName = this.appSession.tenant.tenancyName;
this.name = this.appSession.tenant.name;
}
}
showChangeModal(): void {
const modal = this._modalService.show(TenantChangeDialogComponent);
if (this.appSession.tenant) {
modal.content.tenancyName = this.appSession.tenant.tenancyName;
}
}
}

@ -0,0 +1,170 @@
import { Injectable, Injector } from '@angular/core';
import { PlatformLocation, registerLocaleData } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import * as moment from 'moment-timezone';
import { filter as _filter, merge as _merge } from 'lodash-es';
import { AppConsts } from '@shared/AppConsts';
import { AppSessionService } from '@shared/session/app-session.service';
import { environment } from './environments/environment';
@Injectable({
providedIn: 'root',
})
export class AppInitializer {
constructor(
private _injector: Injector,
private _platformLocation: PlatformLocation,
private _httpClient: HttpClient
) { }
init(): () => Promise<boolean> {
return () => {
abp.ui.setBusy();
return new Promise<boolean>((resolve, reject) => {
AppConsts.appBaseHref = this.getBaseHref();
const appBaseUrl = this.getDocumentOrigin() + AppConsts.appBaseHref;
this.getApplicationConfig(appBaseUrl, () => {
this.getUserConfiguration(() => {
abp.event.trigger('abp.dynamicScriptsInitialized');
// do not use constructor injection for AppSessionService
const appSessionService = this._injector.get(AppSessionService);
appSessionService.init().then(
(result) => {
abp.ui.clearBusy();
if (this.shouldLoadLocale()) {
const angularLocale = this.convertAbpLocaleToAngularLocale(
abp.localization.currentLanguage.name
);
import(`@angular/common/locales/${angularLocale}.js`).then(
(module) => {
registerLocaleData(module.default);
resolve(result);
},
reject
);
} else {
resolve(result);
}
},
(err) => {
abp.ui.clearBusy();
reject(err);
}
);
});
});
});
};
}
private getBaseHref(): string {
const baseUrl = this._platformLocation.getBaseHrefFromDOM();
if (baseUrl) {
return baseUrl;
}
return '/';
}
private getDocumentOrigin(): string {
if (!document.location.origin) {
const port = document.location.port ? ':' + document.location.port : '';
return (
document.location.protocol + '//' + document.location.hostname + port
);
}
return document.location.origin;
}
private shouldLoadLocale(): boolean {
return (
abp.localization.currentLanguage.name &&
abp.localization.currentLanguage.name !== 'en-US'
);
}
private convertAbpLocaleToAngularLocale(locale: string): string {
if (!AppConsts.localeMappings) {
return locale;
}
const localeMapings = _filter(AppConsts.localeMappings, { from: locale });
if (localeMapings && localeMapings.length) {
return localeMapings[0]['to'];
}
return locale;
}
private getCurrentClockProvider(
currentProviderName: string
): abp.timing.IClockProvider {
if (currentProviderName === 'unspecifiedClockProvider') {
return abp.timing.unspecifiedClockProvider;
}
if (currentProviderName === 'utcClockProvider') {
return abp.timing.utcClockProvider;
}
return abp.timing.localClockProvider;
}
private getUserConfiguration(callback: () => void): void {
const cookieLangValue = abp.utils.getCookieValue(
'Abp.Localization.CultureName'
);
const token = abp.auth.getToken();
const requestHeaders = {
'Abp.TenantId': `${abp.multiTenancy.getTenantIdCookie()}`,
'.AspNetCore.Culture': `c=${cookieLangValue}|uic=${cookieLangValue}`,
};
if (token) {
requestHeaders['Authorization'] = `Bearer ${token}`;
}
this._httpClient
.get<any>(
`${AppConsts.remoteServiceBaseUrl}/AbpUserConfiguration/GetAll`,
{
headers: requestHeaders,
}
)
.subscribe((response) => {
const result = response.result;
_merge(abp, result);
abp.clock.provider = this.getCurrentClockProvider(
result.clock.provider
);
moment.locale(abp.localization.currentLanguage.name);
if (abp.clock.provider.supportsMultipleTimezone) {
moment.tz.setDefault(abp.timing.timeZoneInfo.iana.timeZoneId);
}
callback();
});
}
private getApplicationConfig(appRootUrl: string, callback: () => void) {
this._httpClient
.get<any>(`${appRootUrl}assets/${environment.appConfig}`, {
headers: {
'Abp.TenantId': `${abp.multiTenancy.getTenantIdCookie()}`,
},
})
.subscribe((response) => {
AppConsts.appBaseUrl = response.appBaseUrl;
AppConsts.remoteServiceBaseUrl = response.remoteServiceBaseUrl;
AppConsts.localeMappings = response.localeMappings;
callback();
});
}
}

@ -0,0 +1,136 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h1>{{ "About" | localize }}</h1>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="card">
<div class="card-body">
<p>
This is a simple startup template based on ASP.NET Boilerplate
framework and Module Zero. If you need an enterprise startup
project, check
<a href="http://aspnetzero.com?ref=abptmpl" target="_blank">
ASP.NET ZERO </a
>.
</p>
<h3>What is ASP.NET Boilerplate?</h3>
<p>
ASP.NET Boilerplate is an application framework built on latest
<strong>ASP.NET Core</strong> framework. It makes easy to use
authorization, dependency injection, validation, exception handling,
localization, logging, caching, background jobs and so on. It's
built on already familiar tools like Entity Framework, AutoMapper,
Castle Windsor...
</p>
<p>
ASP.NET Boilerplate implements
<strong>NLayer architecture</strong> (Domain, Application,
Infrastructure and Presentation Layers) and
<strong>Domain Driven Design</strong> (Entities, Repositories,
Domain/Application Services, DTO's...). Also implements and provides
a good infrastructure to implement common software development
<strong>best practices</strong>.
</p>
<h3>What is Module Zero?</h3>
<p>
ASP.NET Boilerplate framework is designed to be independent of any
database schema and to be as generic as possible. Therefore, It
leaves some concepts
<strong>abstract</strong> and <strong>optional</strong> (like audit
logging, permission and setting stores) which requires some
<strong>data store</strong>.
</p>
<p>
<strong>Module Zero </strong>implements all fundamental concepts of
ASP.NET Boilerplate framework such as
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Zero/Tenant-Management"
>tenant management</a
>
(<strong>multi-tenancy</strong>),
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Zero/Role-Management"
>
role management </a
>,
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Zero/User-Management"
>user management</a
>,
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Authorization"
>authorization</a
>
(<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Zero/Permission-Management"
>
permission management </a
>),
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Setting-Management"
>setting management</a
>,
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Zero/Language-Management"
>
language management </a
>,
<a
href="http://www.aspnetboilerplate.com/Pages/Documents/Audit-Logging"
>audit logging</a
>
and so on.
</p>
<p>
Module-Zero defines entities and implements
<strong>domain logic</strong>
(domain layer) and leaves application and presentation layers to
you.
</p>
<h4>Based on Microsoft ASP.NET Core Identity</h4>
<p>
Module Zero is based on Microsoft's
<a
href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity"
target="_blank"
>ASP.NET Core Identity</a
>
library. It extends user and role managers and implements user and
role stores using generic repositories.
</p>
<h3>Documentation</h3>
<ul>
<li>
<a
href="https://www.aspnetboilerplate.com/Pages/Documents/Zero/Startup-Template-Core"
>Documentation for this template</a
>
</li>
<li>
<a href="http://www.aspnetboilerplate.com/Pages/Documents"
>ASP.NET Boilerplate documentation</a
>
</li>
</ul>
<h3>Source code</h3>
<p>
This template is developed open source on Github. You can contribute
to the template.
<a
href="https://github.com/aspnetboilerplate/module-zero-core-template"
target="_blank"
>https://github.com/aspnetboilerplate/module-zero-core-template</a
>
</p>
</div>
</div>
</div>
</section>
</div>

@ -0,0 +1,14 @@
import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import { appModuleAnimation } from '@shared/animations/routerTransition';
@Component({
templateUrl: './about.component.html',
animations: [appModuleAnimation()],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AboutComponent extends AppComponentBase {
constructor(injector: Injector) {
super(injector);
}
}

@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { AppRouteGuard } from '@shared/auth/auth-route-guard';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { UsersComponent } from './users/users.component';
import { TenantsComponent } from './tenants/tenants.component';
import { RolesComponent } from 'app/roles/roles.component';
import { ChangePasswordComponent } from './users/change-password/change-password.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: AppComponent,
children: [
{ path: 'home', component: HomeComponent, canActivate: [AppRouteGuard] },
{ path: 'users', component: UsersComponent, data: { permission: 'Pages.Users' }, canActivate: [AppRouteGuard] },
{ path: 'roles', component: RolesComponent, data: { permission: 'Pages.Roles' }, canActivate: [AppRouteGuard] },
{ path: 'tenants', component: TenantsComponent, data: { permission: 'Pages.Tenants' }, canActivate: [AppRouteGuard] },
{ path: 'about', component: AboutComponent, canActivate: [AppRouteGuard] },
{ path: 'update-password', component: ChangePasswordComponent, canActivate: [AppRouteGuard] }
]
}
])
],
exports: [RouterModule]
})
export class AppRoutingModule { }

@ -0,0 +1,9 @@
<div class="wrapper">
<app-header></app-header>
<sidebar></sidebar>
<div class="content-wrapper">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
<div id="sidebar-overlay" (click)="toggleSidebar()"></div>
</div>

@ -0,0 +1,97 @@
import { TestBed, async } from "@angular/core/testing";
import { AppComponent } from "./app.component";
import { LayoutStoreService } from "../shared/layout/layout-store.service";
import { AppSessionService } from "../shared/session/app-session.service";
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CommonModule } from "@angular/common";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { HttpClientJsonpModule } from "@angular/common/http";
import { HttpClientModule } from "@angular/common/http";
import { ModalModule } from "ngx-bootstrap/modal";
import { BsDropdownModule } from "ngx-bootstrap/dropdown";
import { CollapseModule } from "ngx-bootstrap/collapse";
import { TabsModule } from "ngx-bootstrap/tabs";
import { NgxPaginationModule } from "ngx-pagination";
import { RouterTestingModule } from "@angular/router/testing";
import { ServiceProxyModule } from "../shared/service-proxies/service-proxy.module";
import { SharedModule } from "../shared/shared.module";
import { HomeComponent } from "../app/home/home.component";
import { AboutComponent } from "../app/about/about.component";
// layout
import { HeaderComponent } from "./layout/header.component";
import { HeaderLeftNavbarComponent } from "./layout/header-left-navbar.component";
import { HeaderLanguageMenuComponent } from "./layout/header-language-menu.component";
import { HeaderUserMenuComponent } from "./layout/header-user-menu.component";
import { FooterComponent } from "./layout/footer.component";
import { SidebarComponent } from "./layout/sidebar.component";
import { SidebarLogoComponent } from "./layout/sidebar-logo.component";
import { SidebarUserPanelComponent } from "./layout/sidebar-user-panel.component";
import { SidebarMenuComponent } from "./layout/sidebar-menu.component";
describe("AppComponent", () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
// layout
HeaderComponent,
HeaderLeftNavbarComponent,
HeaderLanguageMenuComponent,
HeaderUserMenuComponent,
FooterComponent,
SidebarComponent,
SidebarLogoComponent,
SidebarUserPanelComponent,
SidebarMenuComponent,
],
imports: [
BrowserAnimationsModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
HttpClientJsonpModule,
ModalModule.forChild(),
BsDropdownModule.forRoot(),
CollapseModule.forRoot(),
TabsModule.forRoot(),
RouterTestingModule,
ServiceProxyModule,
SharedModule.forRoot(),
NgxPaginationModule,
],
providers: [
LayoutStoreService,
{
provide: AppSessionService,
useValue: {
application: {
version: "",
releaseDate: {
format: function () {
return "";
},
},
},
getShownLoginName: function(){
return 'admin';
}
},
},
],
});
TestBed.compileComponents();
});
it("should create the app", async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
});

@ -0,0 +1,48 @@
import { Component, Injector, OnInit, Renderer2 } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import { SignalRAspNetCoreHelper } from '@shared/helpers/SignalRAspNetCoreHelper';
import { LayoutStoreService } from '@shared/layout/layout-store.service';
@Component({
templateUrl: './app.component.html'
})
export class AppComponent extends AppComponentBase implements OnInit {
sidebarExpanded: boolean;
constructor(
injector: Injector,
private renderer: Renderer2,
private _layoutStore: LayoutStoreService
) {
super(injector);
}
ngOnInit(): void {
this.renderer.addClass(document.body, 'sidebar-mini');
SignalRAspNetCoreHelper.initSignalR();
abp.event.on('abp.notifications.received', (userNotification) => {
abp.notifications.showUiNotifyForUserNotification(userNotification);
// Desktop notification
Push.create('AbpZeroTemplate', {
body: userNotification.notification.data.message,
icon: abp.appPath + 'assets/app-logo-small.png',
timeout: 6000,
onClick: function () {
window.focus();
this.close();
}
});
});
this._layoutStore.sidebarExpanded.subscribe((value) => {
this.sidebarExpanded = value;
});
}
toggleSidebar(): void {
this._layoutStore.setSidebarExpanded(!this.sidebarExpanded);
}
}

@ -0,0 +1,101 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientJsonpModule } from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { ModalModule } from 'ngx-bootstrap/modal';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { NgxPaginationModule } from 'ngx-pagination';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ServiceProxyModule } from '@shared/service-proxies/service-proxy.module';
import { SharedModule } from '@shared/shared.module';
import { HomeComponent } from '@app/home/home.component';
import { AboutComponent } from '@app/about/about.component';
// tenants
import { TenantsComponent } from '@app/tenants/tenants.component';
import { CreateTenantDialogComponent } from './tenants/create-tenant/create-tenant-dialog.component';
import { EditTenantDialogComponent } from './tenants/edit-tenant/edit-tenant-dialog.component';
// roles
import { RolesComponent } from '@app/roles/roles.component';
import { CreateRoleDialogComponent } from './roles/create-role/create-role-dialog.component';
import { EditRoleDialogComponent } from './roles/edit-role/edit-role-dialog.component';
// users
import { UsersComponent } from '@app/users/users.component';
import { CreateUserDialogComponent } from '@app/users/create-user/create-user-dialog.component';
import { EditUserDialogComponent } from '@app/users/edit-user/edit-user-dialog.component';
import { ChangePasswordComponent } from './users/change-password/change-password.component';
import { ResetPasswordDialogComponent } from './users/reset-password/reset-password.component';
// layout
import { HeaderComponent } from './layout/header.component';
import { HeaderLeftNavbarComponent } from './layout/header-left-navbar.component';
import { HeaderLanguageMenuComponent } from './layout/header-language-menu.component';
import { HeaderUserMenuComponent } from './layout/header-user-menu.component';
import { FooterComponent } from './layout/footer.component';
import { SidebarComponent } from './layout/sidebar.component';
import { SidebarLogoComponent } from './layout/sidebar-logo.component';
import { SidebarUserPanelComponent } from './layout/sidebar-user-panel.component';
import { SidebarMenuComponent } from './layout/sidebar-menu.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
// tenants
TenantsComponent,
CreateTenantDialogComponent,
EditTenantDialogComponent,
// roles
RolesComponent,
CreateRoleDialogComponent,
EditRoleDialogComponent,
// users
UsersComponent,
CreateUserDialogComponent,
EditUserDialogComponent,
ChangePasswordComponent,
ResetPasswordDialogComponent,
// layout
HeaderComponent,
HeaderLeftNavbarComponent,
HeaderLanguageMenuComponent,
HeaderUserMenuComponent,
FooterComponent,
SidebarComponent,
SidebarLogoComponent,
SidebarUserPanelComponent,
SidebarMenuComponent
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
HttpClientJsonpModule,
ModalModule.forChild(),
BsDropdownModule,
CollapseModule,
TabsModule,
AppRoutingModule,
ServiceProxyModule,
SharedModule,
NgxPaginationModule,
],
providers: [],
entryComponents: [
// tenants
CreateTenantDialogComponent,
EditTenantDialogComponent,
// roles
CreateRoleDialogComponent,
EditRoleDialogComponent,
// users
CreateUserDialogComponent,
EditUserDialogComponent,
ResetPasswordDialogComponent,
],
})
export class AppModule {}

@ -0,0 +1,296 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h1>{{ "HomePage" | localize }}</h1>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="alert alert-info alert-dismissible">
<h6><i class="icon fa fa-info"></i> This is a sample Dashboard which doesn't show any server side data.
However, you can develop your own dashboard inspired by this one and its source code.</h6>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-6">
<div class="small-box bg-success">
<div class="inner">
<h3>8.2k+</h3>
<p>Stargazers</p>
</div>
<div class="icon">
<i class="fas fa-star"></i>
</div>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/stargazers"
class="small-box-footer"
target="_blank">
More info
<i class="fas fa-arrow-circle-right"></i>
</a>
</div>
</div>
<div class="col-lg-3 col-6">
<div class="small-box bg-info">
<div class="inner">
<h3>140+</h3>
<p>Contributors</p>
</div>
<div class="icon">
<i class="fas fa-users"></i>
</div>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/graphs/contributors"
class="small-box-footer"
target="_blank">
More info
<i class="fas fa-arrow-circle-right"></i>
</a>
</div>
</div>
<div class="col-lg-3 col-6">
<div class="small-box bg-warning">
<div class="inner">
<h3>1.6k+</h3>
<p>Used / Dependents</p>
</div>
<div class="icon">
<i class="fas fa-tools"></i>
</div>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/network/dependents"
class="small-box-footer"
target="_blank">
More info
<i class="fas fa-arrow-circle-right"></i>
</a>
</div>
</div>
<div class="col-lg-3 col-6">
<div class="small-box bg-primary">
<div class="inner">
<h3>3.1k+</h3>
<p>Forks</p>
</div>
<div class="icon">
<i class="fas fa-code"></i>
</div>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/network/members"
class="small-box-footer"
target="_blank">
More info
<i class="fas fa-arrow-circle-right"></i>
</a>
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<span class="info-box-icon bg-primary elevation-1">
<i class="fas fa-plus-circle"></i>
</span>
<div class="info-box-content">
<span class="info-box-text">Commits</span>
<span class="info-box-number">
6,350+
</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box mb-3">
<span class="info-box-icon bg-warning elevation-1">
<i class="fas fa-exclamation-triangle"></i>
</span>
<div class="info-box-content">
<span class="info-box-text">Issues</span>
<span class="info-box-number">
170+
</span>
</div>
</div>
</div>
<div class="clearfix hidden-md-up"></div>
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box mb-3">
<span class="info-box-icon bg-info elevation-1">
<i class="fas fa-tag"></i>
</span>
<div class="info-box-content">
<span class="info-box-text">Releases</span>
<span class="info-box-number">
200+
</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box mb-3">
<span class="info-box-icon bg-success elevation-1">
<i class="fas fa-eye"></i>
</span>
<div class="info-box-content">
<span class="info-box-text">Watching by</span>
<span class="info-box-number">
810+
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Open Issues</h3>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-hover text-nowrap">
<thead class="bg-light">
<tr>
<th>Id</th>
<th class="w-25">Title</th>
<th class="w-25">Labels</th>
<th>Date</th>
<th>Opened by</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5452"
target="_blank">
5452
</a>
</td>
<td>Angular UI migration to AdminLTE 3</td>
<td>
<span class="badge badge-secondary mx-1">
module-zero-core-template
</span>
<span class="badge badge-primary mx-1">
feature
</span>
</td>
<td>11 days ago</td>
<td>iyilm4z</td>
</tr>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5391"
target="_blank">
5391
</a>
</td>
<td>
AbpCacheBase should lock the same object for sync and
async
</td>
<td>
<span class="badge badge-danger mx-1">
bug
</span>
<span class="badge badge-success mx-1">
pull request candidate
</span>
</td>
<td>26 days ago</td>
<td>acjh</td>
</tr>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5390"
target="_blank">
5390
</a>
</td>
<td>AbpCache sliding/absolute expire time</td>
<td>
<span class="badge badge-warning mx-1">
breaking-change
</span>
<span class="badge badge-info mx-1">enhancement</span>
</td>
<td>27 days ago</td>
<td>ryancyq</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Closed Pull Requests</h3>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-hover text-nowrap">
<thead class="bg-light">
<tr>
<th>Id</th>
<th class="w-25">Title</th>
<th class="w-25">Milestone</th>
<th>Date</th>
<th>Made by</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/pull/5430"
target="_blank">
5430
</a>
</td>
<td>
Added Dynamic-Parameter-System doc to documentation menu
</td>
<td>v5.6</td>
<td>18 days ago</td>
<td>maliming</td>
</tr>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/pull/5362"
target="_blank">
5362
</a>
</td>
<td>
Dynamic Parameter Module
</td>
<td>v5.4</td>
<td>25 days ago</td>
<td>demirmusa</td>
</tr>
<tr>
<td>
<a href="https://github.com/aspnetboilerplate/aspnetboilerplate/pull/4924"
target="_blank">
4924
</a>
</td>
<td>
ASP.NET Core 3.0 Upgrade
</td>
<td>v5.0</td>
<td>Oct 15</td>
<td>ismcagdas</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

@ -0,0 +1,14 @@
import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import { appModuleAnimation } from '@shared/animations/routerTransition';
@Component({
templateUrl: './home.component.html',
animations: [appModuleAnimation()],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HomeComponent extends AppComponentBase {
constructor(injector: Injector) {
super(injector);
}
}

@ -0,0 +1,9 @@
<footer class="main-footer">
<strong>
Copyright &copy; {{ currentYear }}
<a href="javascript:;">MeetingSchedule</a>.
</strong>
<div class="float-right d-none d-sm-inline-block">
<b>{{ "Version" | localize }} </b> {{ versionText }}
</div>
</footer>

@ -0,0 +1,23 @@
import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FooterComponent extends AppComponentBase {
currentYear: number;
versionText: string;
constructor(injector: Injector) {
super(injector);
this.currentYear = new Date().getFullYear();
this.versionText =
this.appSession.application.version +
' [' +
this.appSession.application.releaseDate.format('YYYYDDMM') +
']';
}
}

@ -0,0 +1,21 @@
<li class="nav-item dropdown" dropdown>
<a href="javascript:;" class="nav-link" dropdownToggle>
<i class="d-inline-block {{ currentLanguage.icon }}"></i>
<span class="d-none d-md-inline-block ml-1">
{{ currentLanguage.displayName }}
</span>
</a>
<div class="dropdown-menu dropdown-menu-right p-0" *dropdownMenu>
<ng-container *ngFor="let language of languages">
<a
*ngIf="language.name != currentLanguage.name"
class="dropdown-item"
href="javascript:;"
(click)="changeLanguage(language.name)"
>
<i class="d-inline-block {{ language.icon }} mr-1"></i>
{{ language.displayName }}
</a>
</ng-container>
</div>
</li>

@ -0,0 +1,51 @@
import {
Component,
ChangeDetectionStrategy,
OnInit,
Injector
} from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import {
UserServiceProxy,
ChangeUserLanguageDto
} from '@shared/service-proxies/service-proxies';
import { filter as _filter } from 'lodash-es';
@Component({
selector: 'header-language-menu',
templateUrl: './header-language-menu.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderLanguageMenuComponent extends AppComponentBase
implements OnInit {
languages: abp.localization.ILanguageInfo[];
currentLanguage: abp.localization.ILanguageInfo;
constructor(injector: Injector, private _userService: UserServiceProxy) {
super(injector);
}
ngOnInit() {
this.languages = _filter(
this.localization.languages,
(l) => !l.isDisabled
);
this.currentLanguage = this.localization.currentLanguage;
}
changeLanguage(languageName: string): void {
const input = new ChangeUserLanguageDto();
input.languageName = languageName;
this._userService.changeLanguage(input).subscribe(() => {
abp.utils.setCookieValue(
'Abp.Localization.CultureName',
languageName,
new Date(new Date().getTime() + 5 * 365 * 86400000), // 5 year
abp.appPath
);
window.location.reload();
});
}
}

@ -0,0 +1,17 @@
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="javascript:;" (click)="toggleSidebar()">
<i class="fas fa-bars"></i>
</a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a class="nav-link" [routerLink]="['home']">
{{ "HomePage" | localize }}
</a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a class="nav-link" [routerLink]="['about']">
{{ "About" | localize }}
</a>
</li>
</ul>

@ -0,0 +1,23 @@
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { LayoutStoreService } from '@shared/layout/layout-store.service';
@Component({
selector: 'header-left-navbar',
templateUrl: './header-left-navbar.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderLeftNavbarComponent implements OnInit {
sidebarExpanded: boolean;
constructor(private _layoutStore: LayoutStoreService) {}
ngOnInit(): void {
this._layoutStore.sidebarExpanded.subscribe((value) => {
this.sidebarExpanded = value;
});
}
toggleSidebar(): void {
this._layoutStore.setSidebarExpanded(!this.sidebarExpanded);
}
}

@ -0,0 +1,17 @@
<li class="nav-item dropdown nav-user-menu" dropdown>
<a href="javascript:;" class="nav-link" dropdownToggle>
<img
class="user-image img-circle elevation-2"
src="assets/img/user.png"
alt="User Image"
/>
</a>
<div class="dropdown-menu dropdown-menu-right" *dropdownMenu>
<a class="dropdown-item" [routerLink]="['update-password']">
<i class="fas fa-user-edit"></i> {{ "UpdatePassword" | localize }}
</a>
<a class="dropdown-item" href="javascript:;" (click)="logout()">
<i class="fas fa-sign-out-alt"></i> {{ "Logout" | localize }}
</a>
</div>
</li>

@ -0,0 +1,15 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { AppAuthService } from '@shared/auth/app-auth.service';
@Component({
selector: 'header-user-menu',
templateUrl: './header-user-menu.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderUserMenuComponent {
constructor(private _authService: AppAuthService) {}
logout(): void {
this._authService.logout();
}
}

@ -0,0 +1,7 @@
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<header-left-navbar></header-left-navbar>
<ul class="navbar-nav ml-auto">
<header-language-menu></header-language-menu>
<header-user-menu></header-user-menu>
</ul>
</nav>

@ -0,0 +1,8 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderComponent {}

@ -0,0 +1,9 @@
<a class="brand-link" [routerLink]="['home']">
<img
src="assets/img/logo.png"
alt="Logo"
class="brand-image img-circle elevation-3"
style="opacity: 0.8;"
/>
<span class="brand-text font-weight-light">MeetingSchedule</span>
</a>

@ -0,0 +1,8 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'sidebar-logo',
templateUrl: './sidebar-logo.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SidebarLogoComponent {}

@ -0,0 +1,71 @@
<nav class="mt-2">
<ul
class="nav nav-pills nav-sidebar flex-column nav-flat"
data-widget="treeview"
role="menu"
data-accordion="false"
>
<ng-container *ngFor="let item of menuItems">
<ng-container
*ngTemplateOutlet="sidebarInner; context: { item: item }"
></ng-container>
</ng-container>
</ul>
</nav>
<ng-template #sidebarInner let-item="item">
<li
*ngIf="isMenuItemVisible(item)"
class="nav-item"
[class.menu-open]="!item.isCollapsed"
[class.has-treeview]="item.children"
>
<a
*ngIf="item.route && item.route.indexOf('http') != 0"
class="nav-link"
[routerLink]="item.route"
[class.active]="item.isActive"
>
<i class="nav-icon {{ item.icon }}"></i>
<p>
{{ item.label }}
</p>
</a>
<a
*ngIf="item.route && item.route.indexOf('http') == 0 && !item.children"
class="nav-link"
target="_blank"
[href]="item.route"
>
<i class="nav-icon {{ item.icon }}"></i>
<p>
{{ item.label }}
</p>
</a>
<a
*ngIf="!item.route && item.children"
class="nav-link"
href="javascript:;"
[class.active]="item.isActive"
(click)="item.isCollapsed = !item.isCollapsed"
>
<i class="nav-icon {{ item.icon }}"></i>
<p>
{{ item.label }}
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul
*ngIf="item.children"
class="nav nav-treeview"
[collapse]="item.isCollapsed"
[isAnimated]="true"
>
<ng-container *ngFor="let item of item.children">
<ng-container
*ngTemplateOutlet="sidebarInner; context: { item: item }"
></ng-container>
</ng-container>
</ul>
</li>
</ng-template>

@ -0,0 +1,186 @@
import {Component, Injector, OnInit} from '@angular/core';
import {AppComponentBase} from '@shared/app-component-base';
import {
Router,
RouterEvent,
NavigationEnd,
PRIMARY_OUTLET
} from '@angular/router';
import {BehaviorSubject} from 'rxjs';
import {filter} from 'rxjs/operators';
import {MenuItem} from '@shared/layout/menu-item';
@Component({
selector: 'sidebar-menu',
templateUrl: './sidebar-menu.component.html'
})
export class SidebarMenuComponent extends AppComponentBase implements OnInit {
menuItems: MenuItem[];
menuItemsMap: { [key: number]: MenuItem } = {};
activatedMenuItems: MenuItem[] = [];
routerEvents: BehaviorSubject<RouterEvent> = new BehaviorSubject(undefined);
homeRoute = '/app/about';
constructor(injector: Injector, private router: Router) {
super(injector);
this.router.events.subscribe(this.routerEvents);
}
ngOnInit(): void {
this.menuItems = this.getMenuItems();
this.patchMenuItems(this.menuItems);
this.routerEvents
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe((event) => {
const currentUrl = event.url !== '/' ? event.url : this.homeRoute;
const primaryUrlSegmentGroup = this.router.parseUrl(currentUrl).root
.children[PRIMARY_OUTLET];
if (primaryUrlSegmentGroup) {
this.activateMenuItems('/' + primaryUrlSegmentGroup.toString());
}
});
}
getMenuItems(): MenuItem[] {
return [
new MenuItem(this.l('About'), '/app/about', 'fas fa-info-circle'),
new MenuItem(this.l('HomePage'), '/app/home', 'fas fa-home'),
new MenuItem(
this.l('Roles'),
'/app/roles',
'fas fa-theater-masks',
'Pages.Roles'
),
new MenuItem(
this.l('Tenants'),
'/app/tenants',
'fas fa-building',
'Pages.Tenants'
),
new MenuItem(
this.l('Users'),
'/app/users',
'fas fa-users',
'Pages.Users'
),
new MenuItem(this.l('MultiLevelMenu'), '', 'fas fa-circle', '', [
new MenuItem('ASP.NET Boilerplate', '', 'fas fa-dot-circle', '', [
new MenuItem(
'Home',
'https://aspnetboilerplate.com?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Templates',
'https://aspnetboilerplate.com/Templates?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Samples',
'https://aspnetboilerplate.com/Samples?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Documents',
'https://aspnetboilerplate.com/Pages/Documents?ref=abptmpl',
'far fa-circle'
),
]),
new MenuItem('ASP.NET Zero', '', 'fas fa-dot-circle', '', [
new MenuItem(
'Home',
'https://aspnetzero.com?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Features',
'https://aspnetzero.com/Features?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Pricing',
'https://aspnetzero.com/Pricing?ref=abptmpl#pricing',
'far fa-circle'
),
new MenuItem(
'Faq',
'https://aspnetzero.com/Faq?ref=abptmpl',
'far fa-circle'
),
new MenuItem(
'Documents',
'https://aspnetzero.com/Documents?ref=abptmpl',
'far fa-circle'
)
])
])
];
}
patchMenuItems(items: MenuItem[], parentId?: number): void {
items.forEach((item: MenuItem, index: number) => {
item.id = parentId ? Number(parentId + '' + (index + 1)) : index + 1;
if (parentId) {
item.parentId = parentId;
}
if (parentId || item.children) {
this.menuItemsMap[item.id] = item;
}
if (item.children) {
this.patchMenuItems(item.children, item.id);
}
});
}
activateMenuItems(url: string): void {
this.deactivateMenuItems(this.menuItems);
this.activatedMenuItems = [];
const foundedItems = this.findMenuItemsByUrl(url, this.menuItems);
foundedItems.forEach((item) => {
this.activateMenuItem(item);
});
}
deactivateMenuItems(items: MenuItem[]): void {
items.forEach((item: MenuItem) => {
item.isActive = false;
item.isCollapsed = true;
if (item.children) {
this.deactivateMenuItems(item.children);
}
});
}
findMenuItemsByUrl(
url: string,
items: MenuItem[],
foundedItems: MenuItem[] = []
): MenuItem[] {
items.forEach((item: MenuItem) => {
if (item.route === url) {
foundedItems.push(item);
} else if (item.children) {
this.findMenuItemsByUrl(url, item.children, foundedItems);
}
});
return foundedItems;
}
activateMenuItem(item: MenuItem): void {
item.isActive = true;
if (item.children) {
item.isCollapsed = false;
}
this.activatedMenuItems.push(item);
if (item.parentId) {
this.activateMenuItem(this.menuItemsMap[item.parentId]);
}
}
isMenuItemVisible(item: MenuItem): boolean {
if (!item.permissionName) {
return true;
}
return this.permission.isGranted(item.permissionName);
}
}

@ -0,0 +1,12 @@
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img
src="assets/img/user.png"
class="img-circle elevation-2"
alt="User Image"
/>
</div>
<div class="info">
<a class="d-block" href="javascript:;">{{ shownLoginName }}</a>
</div>
</div>

@ -0,0 +1,25 @@
import {
Component,
ChangeDetectionStrategy,
Injector,
OnInit
} from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
@Component({
selector: 'sidebar-user-panel',
templateUrl: './sidebar-user-panel.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SidebarUserPanelComponent extends AppComponentBase
implements OnInit {
shownLoginName = '';
constructor(injector: Injector) {
super(injector);
}
ngOnInit() {
this.shownLoginName = this.appSession.getShownLoginName();
}
}

@ -0,0 +1,7 @@
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<sidebar-logo></sidebar-logo>
<div class="sidebar">
<sidebar-user-panel></sidebar-user-panel>
<sidebar-menu></sidebar-menu>
</div>
</aside>

@ -0,0 +1,47 @@
import {
Component,
ChangeDetectionStrategy,
Renderer2,
OnInit
} from '@angular/core';
import { LayoutStoreService } from '@shared/layout/layout-store.service';
@Component({
// tslint:disable-next-line:component-selector
selector: 'sidebar',
templateUrl: './sidebar.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SidebarComponent implements OnInit {
sidebarExpanded: boolean;
constructor(
private renderer: Renderer2,
private _layoutStore: LayoutStoreService
) {}
ngOnInit(): void {
this._layoutStore.sidebarExpanded.subscribe((value) => {
this.sidebarExpanded = value;
this.toggleSidebar();
});
}
toggleSidebar(): void {
if (this.sidebarExpanded) {
this.hideSidebar();
} else {
this.showSidebar();
}
}
showSidebar(): void {
this.renderer.removeClass(document.body, 'sidebar-collapse');
this.renderer.addClass(document.body, 'sidebar-open');
}
hideSidebar(): void {
this.renderer.removeClass(document.body, 'sidebar-open');
this.renderer.addClass(document.body, 'sidebar-collapse');
}
}

@ -0,0 +1,103 @@
<form
class="form-horizontal"
autocomplete="off"
#createRoleForm="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'CreateNewRole' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<tabset>
<tab [heading]="'Details' | localize" class="pt-3 px-2">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
required
minlength="2"
maxlength="32"
[(ngModel)]="role.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="displayName">
{{ "DisplayName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="displayName"
id="displayName"
required
minlength="2"
maxlength="32"
[(ngModel)]="role.displayName"
#displayNameModel="ngModel"
#displayNameEl
/>
<abp-validation-summary
[control]="displayNameModel"
[controlEl]="displayNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label" for="description">
{{ "RoleDescription" | localize }}
</label>
<div class="col-md-9">
<textarea
type="text"
class="form-control"
name="description"
id="description"
[(ngModel)]="role.description"
>
</textarea>
</div>
</div>
</tab>
<tab [heading]="'Permissions' | localize" class="pt-3 px-2">
<div class="form-group row mb-0">
<ng-container *ngFor="let permission of permissions; let i = index">
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[id]="'permission_' + i"
[checked]="isPermissionChecked(permission.name)"
(change)="onPermissionChange(permission, $event)"
/>
<label class="custom-control-label" [for]="'permission_' + i">
{{ permission.displayName }}
</label>
</div>
</div>
</ng-container>
</div>
</tab>
</tabset>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!createRoleForm.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,97 @@
import {
Component,
Injector,
OnInit,
EventEmitter,
Output,
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { AppComponentBase } from '@shared/app-component-base';
import {
RoleServiceProxy,
RoleDto,
PermissionDto,
CreateRoleDto,
PermissionDtoListResultDto
} from '@shared/service-proxies/service-proxies';
import { forEach as _forEach, map as _map } from 'lodash-es';
@Component({
templateUrl: 'create-role-dialog.component.html'
})
export class CreateRoleDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
role = new RoleDto();
permissions: PermissionDto[] = [];
checkedPermissionsMap: { [key: string]: boolean } = {};
defaultPermissionCheckedStatus = true;
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
private _roleService: RoleServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this._roleService
.getAllPermissions()
.subscribe((result: PermissionDtoListResultDto) => {
this.permissions = result.items;
this.setInitialPermissionsStatus();
});
}
setInitialPermissionsStatus(): void {
_map(this.permissions, (item) => {
this.checkedPermissionsMap[item.name] = this.isPermissionChecked(
item.name
);
});
}
isPermissionChecked(permissionName: string): boolean {
// just return default permission checked status
// it's better to use a setting
return this.defaultPermissionCheckedStatus;
}
onPermissionChange(permission: PermissionDto, $event) {
this.checkedPermissionsMap[permission.name] = $event.target.checked;
}
getCheckedPermissions(): string[] {
const permissions: string[] = [];
_forEach(this.checkedPermissionsMap, function (value, key) {
if (value) {
permissions.push(key);
}
});
return permissions;
}
save(): void {
this.saving = true;
const role = new CreateRoleDto();
role.init(this.role);
role.grantedPermissions = this.getCheckedPermissions();
this._roleService
.create(role)
.subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,104 @@
<form
class="form-horizontal"
autocomplete="off"
#editRoleForm="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'EditRole' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<tabset>
<tab [heading]="'Details' | localize" class="pt-3 px-2">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
required
minlength="2"
maxlength="32"
[(ngModel)]="role.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="displayName">
{{ "DisplayName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="displayName"
id="displayName"
required
minlength="2"
maxlength="32"
[(ngModel)]="role.displayName"
#displayNameModel="ngModel"
#displayNameEl
/>
<abp-validation-summary
[control]="displayNameModel"
[controlEl]="displayNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label" for="description">
{{ "RoleDescription" | localize }}
</label>
<div class="col-md-9">
<textarea
type="text"
class="form-control"
name="description"
id="description"
[(ngModel)]="role.description"
>
</textarea>
</div>
</div>
</tab>
<tab [heading]="'Permissions' | localize" class="pt-3 px-2">
<div class="form-group row mb-0">
<ng-container *ngFor="let permission of permissions; let i = index">
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[id]="'permission_' + i"
[checked]="isPermissionChecked(permission.name)"
[disabled]="role.isStatic"
(change)="onPermissionChange(permission, $event)"
/>
<label class="custom-control-label" [for]="'permission_' + i">
{{ permission.displayName }}
</label>
</div>
</div>
</ng-container>
</div>
</tab>
</tabset>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!editRoleForm.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,97 @@
import {
Component,
Injector,
OnInit,
EventEmitter,
Output,
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { forEach as _forEach, includes as _includes, map as _map } from 'lodash-es';
import { AppComponentBase } from '@shared/app-component-base';
import {
RoleServiceProxy,
GetRoleForEditOutput,
RoleDto,
PermissionDto,
RoleEditDto,
FlatPermissionDto
} from '@shared/service-proxies/service-proxies';
@Component({
templateUrl: 'edit-role-dialog.component.html'
})
export class EditRoleDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
id: number;
role = new RoleEditDto();
permissions: FlatPermissionDto[];
grantedPermissionNames: string[];
checkedPermissionsMap: { [key: string]: boolean } = {};
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
private _roleService: RoleServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this._roleService
.getRoleForEdit(this.id)
.subscribe((result: GetRoleForEditOutput) => {
this.role = result.role;
this.permissions = result.permissions;
this.grantedPermissionNames = result.grantedPermissionNames;
this.setInitialPermissionsStatus();
});
}
setInitialPermissionsStatus(): void {
_map(this.permissions, (item) => {
this.checkedPermissionsMap[item.name] = this.isPermissionChecked(
item.name
);
});
}
isPermissionChecked(permissionName: string): boolean {
return _includes(this.grantedPermissionNames, permissionName);
}
onPermissionChange(permission: PermissionDto, $event) {
this.checkedPermissionsMap[permission.name] = $event.target.checked;
}
getCheckedPermissions(): string[] {
const permissions: string[] = [];
_forEach(this.checkedPermissionsMap, function (value, key) {
if (value) {
permissions.push(key);
}
});
return permissions;
}
save(): void {
this.saving = true;
const role = new RoleDto();
role.init(this.role);
role.grantedPermissions = this.getCheckedPermissions();
this._roleService.update(role).subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,112 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-6">
<h1>{{ "Roles" | localize }}</h1>
</div>
<div class="col-6 text-right">
<a href="javascript:;"
class="btn bg-blue"
(click)="createRole()">
<i class="fa fa-plus-square"></i>
{{ "Create" | localize }}
</a>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col-md-6">&emsp;</div>
<div class="col-md-6">
<div class="input-group">
<div class="input-group-prepend">
<button type="button"
class="btn bg-blue"
(click)="getDataPage(1)">
<i class="fas fa-search"></i>
</button>
</div>
<input type="text"
class="form-control"
name="keyword"
[placeholder]="'SearchWithThreeDot' | localize"
[(ngModel)]="keyword"
(keyup.enter)="getDataPage(1)" />
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-bordered"
[busy]="isTableLoading">
<thead class="bg-light">
<tr>
<th>{{ "RoleName" | localize }}</th>
<th>{{ "DisplayName" | localize }}</th>
<th style="width: 200px;">{{ "Actions" | localize }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="
let role of roles
| paginate
: {
id: 'server',
itemsPerPage: pageSize,
currentPage: pageNumber,
totalItems: totalItems
}
">
<td>{{ role.name }}</td>
<td>{{ role.displayName }}</td>
<td>
<button type="button"
class="btn btn-sm bg-secondary"
(click)="editRole(role)">
<i class="fas fa-pencil-alt"></i>
{{ "Edit" | localize }}
</button>
<button type="button"
class="btn btn-sm bg-danger mx-2"
(click)="delete(role)">
<i class="fas fa-trash"></i>
{{ "Delete" | localize }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer table-card-footer bg-light border-top">
<div class="row">
<div class="col-sm-4 col-12 text-sm-left text-center">
<button class="btn btn-secondary"
(click)="refresh()">
<i class="fas fa-redo-alt"></i>
</button>
</div>
<div class="col-sm-4 col-12 text-center">
<p class="mb-0 my-2">
{{ "TotalRecordsCount" | localize: totalItems }}
</p>
</div>
<div class="col-sm-4 col-12">
<div class="float-sm-right m-auto">
<abp-pagination-controls id="server"
(pageChange)="getDataPage($event)">
</abp-pagination-controls>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

@ -0,0 +1,110 @@
import { Component, Injector } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import {
PagedListingComponentBase,
PagedRequestDto
} from '@shared/paged-listing-component-base';
import {
RoleServiceProxy,
RoleDto,
RoleDtoPagedResultDto
} from '@shared/service-proxies/service-proxies';
import { CreateRoleDialogComponent } from './create-role/create-role-dialog.component';
import { EditRoleDialogComponent } from './edit-role/edit-role-dialog.component';
class PagedRolesRequestDto extends PagedRequestDto {
keyword: string;
}
@Component({
templateUrl: './roles.component.html',
animations: [appModuleAnimation()]
})
export class RolesComponent extends PagedListingComponentBase<RoleDto> {
roles: RoleDto[] = [];
keyword = '';
constructor(
injector: Injector,
private _rolesService: RoleServiceProxy,
private _modalService: BsModalService
) {
super(injector);
}
list(
request: PagedRolesRequestDto,
pageNumber: number,
finishedCallback: Function
): void {
request.keyword = this.keyword;
this._rolesService
.getAll(request.keyword, request.skipCount, request.maxResultCount)
.pipe(
finalize(() => {
finishedCallback();
})
)
.subscribe((result: RoleDtoPagedResultDto) => {
this.roles = result.items;
this.showPaging(result, pageNumber);
});
}
delete(role: RoleDto): void {
abp.message.confirm(
this.l('RoleDeleteWarningMessage', role.displayName),
undefined,
(result: boolean) => {
if (result) {
this._rolesService
.delete(role.id)
.pipe(
finalize(() => {
abp.notify.success(this.l('SuccessfullyDeleted'));
this.refresh();
})
)
.subscribe(() => {});
}
}
);
}
createRole(): void {
this.showCreateOrEditRoleDialog();
}
editRole(role: RoleDto): void {
this.showCreateOrEditRoleDialog(role.id);
}
showCreateOrEditRoleDialog(id?: number): void {
let createOrEditRoleDialog: BsModalRef;
if (!id) {
createOrEditRoleDialog = this._modalService.show(
CreateRoleDialogComponent,
{
class: 'modal-lg',
}
);
} else {
createOrEditRoleDialog = this._modalService.show(
EditRoleDialogComponent,
{
class: 'modal-lg',
initialState: {
id: id,
},
}
);
}
createOrEditRoleDialog.content.onSave.subscribe(() => {
this.refresh();
});
}
}

@ -0,0 +1,121 @@
<form
class="form-horizontal"
autocomplete="off"
#createTenantForm="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'CreateNewTenant' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="tenancyName">
{{ "TenancyName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="tenancyName"
id="tenancyName"
minlength="2"
maxlength="64"
required
[(ngModel)]="tenant.tenancyName"
#tenancyNameModel="ngModel"
#tenancyNameEl
/>
<abp-validation-summary
[control]="tenancyNameModel"
[controlEl]="tenancyNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
maxlength="128"
required
[(ngModel)]="tenant.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 col-form-label" for="connectionString">
{{ "DatabaseConnectionString" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="connectionString"
id="connectionString"
maxlength="1024"
[(ngModel)]="tenant.connectionString"
/>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="adminEmailAddress">
{{ "AdminEmailAddress" | localize }}
</label>
<div class="col-md-9">
<input
type="email"
class="form-control"
name="adminEmailAddress"
id="adminEmailAddress"
pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,})+$"
maxlength="256"
required
[(ngModel)]="tenant.adminEmailAddress"
#adminEmailAddressModel="ngModel"
#adminEmailAddressEl
/>
<abp-validation-summary
[control]="adminEmailAddressModel"
[controlEl]="adminEmailAddressEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
name="isActive"
id="isActive"
[(ngModel)]="tenant.isActive"
/>
<label class="custom-control-label mt-2" for="isActive"></label>
</div>
</div>
</div>
<p class="text-center text-info mb-0">
{{ "DefaultPasswordIs" | localize: "123qwe" }}
</p>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!createTenantForm.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,51 @@
import {
Component,
Injector,
OnInit,
Output,
EventEmitter
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { AppComponentBase } from '@shared/app-component-base';
import {
CreateTenantDto,
TenantServiceProxy
} from '@shared/service-proxies/service-proxies';
@Component({
templateUrl: 'create-tenant-dialog.component.html'
})
export class CreateTenantDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
tenant: CreateTenantDto = new CreateTenantDto();
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
public _tenantService: TenantServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this.tenant.isActive = true;
}
save(): void {
this.saving = true;
this._tenantService.create(this.tenant).subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,80 @@
<form
class="form-horizontal"
autocomplete="off"
#editTenantForm="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'EditTenant' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="tenancyName">
{{ "TenancyName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="tenancyName"
id="tenancyName"
minlength="2"
maxlength="64"
required
[(ngModel)]="tenant.tenancyName"
#tenancyNameModel="ngModel"
#tenancyNameEl
/>
<abp-validation-summary
[control]="tenancyNameModel"
[controlEl]="tenancyNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
maxlength="128"
required
[(ngModel)]="tenant.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
name="isActive"
id="isActive"
[(ngModel)]="tenant.isActive"
/>
<label class="custom-control-label mt-2" for="isActive"></label>
</div>
</div>
</div>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!editTenantForm.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,54 @@
import {
Component,
Injector,
OnInit,
Output,
EventEmitter
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { AppComponentBase } from '@shared/app-component-base';
import {
TenantServiceProxy,
TenantDto
} from '@shared/service-proxies/service-proxies';
@Component({
templateUrl: 'edit-tenant-dialog.component.html'
})
export class EditTenantDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
tenant: TenantDto = new TenantDto();
id: number;
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
public _tenantService: TenantServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this._tenantService.get(this.id).subscribe((result: TenantDto) => {
this.tenant = result;
});
}
save(): void {
this.saving = true;
this._tenantService.update(this.tenant).subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,224 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-6">
<h1>{{ "Tenants" | localize }}</h1>
</div>
<div class="col-6 text-right">
<a href="javascript:;" class="btn bg-blue" (click)="createTenant()">
<i class="fa fa-plus-square"></i>
{{ "Create" | localize }}
</a>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col-md-6">&emsp;</div>
<div class="col-md-6">
<div class="input-group">
<div class="input-group-prepend">
<button
type="button"
class="btn bg-blue"
(click)="getDataPage(1)"
>
<i class="fas fa-search"></i>
</button>
</div>
<input
type="text"
class="form-control"
name="keyword"
[placeholder]="'SearchWithThreeDot' | localize"
[(ngModel)]="keyword"
(keyup.enter)="getDataPage(1)"
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-default"
(click)="advancedFiltersVisible = !advancedFiltersVisible"
>
<i
class="fas"
[class.fa-angle-up]="advancedFiltersVisible"
[class.fa-angle-down]="!advancedFiltersVisible"
></i>
</button>
</div>
</div>
</div>
</div>
<div *ngIf="advancedFiltersVisible" class="card mb-0 mt-1">
<div class="card-body">
<form class="form-horizontal">
<div class="row">
<div class="col-md-6">
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9 pt-2">
<div class="custom-control custom-radio d-inline">
<input
type="radio"
class="custom-control-input"
id="isActiveAll"
name="isActive"
[(ngModel)]="isActive"
[value]="undefined"
checked
/>
<label class="custom-control-label" for="isActiveAll">
{{ "All" | localize }}
</label>
</div>
<div class="custom-control custom-radio d-inline mx-3">
<input
type="radio"
class="custom-control-input"
id="isActiveActive"
name="isActive"
[(ngModel)]="isActive"
[value]="true"
/>
<label
class="custom-control-label"
for="isActiveActive"
>
{{ "Yes" | localize }}
</label>
</div>
<div class="custom-control custom-radio d-inline">
<input
type="radio"
class="custom-control-input"
id="isActivePassive"
name="isActive"
[(ngModel)]="isActive"
[value]="false"
/>
<label
class="custom-control-label"
for="isActivePassive"
>
{{ "No" | localize }}
</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="card-footer">
<button
type="button"
class="btn bg-blue"
(click)="getDataPage(1)"
>
{{ "Search" | localize }}
</button>
<button
type="button"
class="btn btn-default float-right"
(click)="clearFilters()"
>
{{ "Clear" | localize }}
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-bordered" [busy]="isTableLoading">
<thead class="bg-light">
<tr>
<th>{{ "TenancyName" | localize }}</th>
<th>{{ "Name" | localize }}</th>
<th>{{ "IsActive" | localize }}</th>
<th style="width: 200px;">{{ "Actions" | localize }}</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="
let tenant of tenants
| paginate
: {
id: 'server',
itemsPerPage: pageSize,
currentPage: pageNumber,
totalItems: totalItems
}
"
>
<td>{{ tenant.tenancyName }}</td>
<td>{{ tenant.name }}</td>
<td>
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
disabled
[checked]="tenant.isActive"
/>
<label class="custom-control-label"></label>
</div>
</td>
<td>
<button
type="button"
class="btn btn-sm bg-secondary"
(click)="editTenant(tenant)"
>
<i class="fas fa-pencil-alt"></i>
{{ "Edit" | localize }}
</button>
<button
type="button"
class="btn btn-sm bg-danger mx-2"
(click)="delete(tenant)"
>
<i class="fas fa-trash"></i>
{{ "Delete" | localize }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer table-card-footer bg-light border-top">
<div class="row">
<div class="col-sm-4 col-12 text-sm-left text-center">
<button class="btn btn-secondary" (click)="refresh()">
<i class="fas fa-redo-alt"></i>
</button>
</div>
<div class="col-sm-4 col-12 text-center">
<p class="mb-0 my-2">
{{ "TotalRecordsCount" | localize: totalItems }}
</p>
</div>
<div class="col-sm-4 col-12">
<div class="float-sm-right m-auto">
<abp-pagination-controls
id="server"
(pageChange)="getDataPage($event)"
>
</abp-pagination-controls>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

@ -0,0 +1,125 @@
import { Component, Injector } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import {
PagedListingComponentBase,
PagedRequestDto,
} from '@shared/paged-listing-component-base';
import {
TenantServiceProxy,
TenantDto,
TenantDtoPagedResultDto,
} from '@shared/service-proxies/service-proxies';
import { CreateTenantDialogComponent } from './create-tenant/create-tenant-dialog.component';
import { EditTenantDialogComponent } from './edit-tenant/edit-tenant-dialog.component';
class PagedTenantsRequestDto extends PagedRequestDto {
keyword: string;
isActive: boolean | null;
}
@Component({
templateUrl: './tenants.component.html',
animations: [appModuleAnimation()]
})
export class TenantsComponent extends PagedListingComponentBase<TenantDto> {
tenants: TenantDto[] = [];
keyword = '';
isActive: boolean | null;
advancedFiltersVisible = false;
constructor(
injector: Injector,
private _tenantService: TenantServiceProxy,
private _modalService: BsModalService
) {
super(injector);
}
list(
request: PagedTenantsRequestDto,
pageNumber: number,
finishedCallback: Function
): void {
request.keyword = this.keyword;
request.isActive = this.isActive;
this._tenantService
.getAll(
request.keyword,
request.isActive,
request.skipCount,
request.maxResultCount
)
.pipe(
finalize(() => {
finishedCallback();
})
)
.subscribe((result: TenantDtoPagedResultDto) => {
this.tenants = result.items;
this.showPaging(result, pageNumber);
});
}
delete(tenant: TenantDto): void {
abp.message.confirm(
this.l('TenantDeleteWarningMessage', tenant.name),
undefined,
(result: boolean) => {
if (result) {
this._tenantService
.delete(tenant.id)
.pipe(
finalize(() => {
abp.notify.success(this.l('SuccessfullyDeleted'));
this.refresh();
})
)
.subscribe(() => {});
}
}
);
}
createTenant(): void {
this.showCreateOrEditTenantDialog();
}
editTenant(tenant: TenantDto): void {
this.showCreateOrEditTenantDialog(tenant.id);
}
showCreateOrEditTenantDialog(id?: number): void {
let createOrEditTenantDialog: BsModalRef;
if (!id) {
createOrEditTenantDialog = this._modalService.show(
CreateTenantDialogComponent,
{
class: 'modal-lg',
}
);
} else {
createOrEditTenantDialog = this._modalService.show(
EditTenantDialogComponent,
{
class: 'modal-lg',
initialState: {
id: id,
},
}
);
}
createOrEditTenantDialog.content.onSave.subscribe(() => {
this.refresh();
});
}
clearFilters(): void {
this.keyword = '';
this.isActive = undefined;
this.getDataPage(1);
}
}

@ -0,0 +1,115 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-6">
<h1>{{ "UpdatePassword" | localize }}</h1>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="card">
<form
class="form-horizontal"
autocomplete="off"
#changePasswordForm="ngForm"
(ngSubmit)="changePassword()"
>
<div class="card-body">
<div class="modal-body">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="currentPassword">
{{ "CurrentPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
name="currentPassword"
id="currentPassword"
required
minlength="2"
maxlength="32"
[(ngModel)]="changePasswordDto.currentPassword"
#currentPasswordModel="ngModel"
#currentPasswordEl
/>
<abp-validation-summary
[control]="currentPasswordModel"
[controlEl]="currentPasswordEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="newPassword">
{{ "NewPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
name="newPassword"
id="newPassword"
required
minlength="2"
maxlength="32"
validateEqual="confirmNewPassword"
reverse="true"
pattern="(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)[0-9a-zA-Z!@#$%^&*()]*$"
[(ngModel)]="changePasswordDto.newPassword"
#newPasswordModel="ngModel"
#newPasswordEl
/>
<abp-validation-summary
[control]="newPasswordModel"
[controlEl]="newPasswordEl"
[customValidationErrors]="newPasswordValidationErrors"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="confirmNewPassword">
{{ "ConfirmNewPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
name="confirmNewPassword"
id="confirmNewPassword"
required
minlength="2"
maxlength="32"
validateEqual="newPassword"
reverse="false"
ngModel
#confirmNewPasswordModel="ngModel"
#confirmNewPasswordEl
/>
<abp-validation-summary
[control]="confirmNewPasswordModel"
[controlEl]="confirmNewPasswordEl"
[customValidationErrors]="
confirmNewPasswordValidationErrors
"
></abp-validation-summary>
</div>
</div>
</div>
</div>
<div class="card-footer justify-content-between">
<button
type="submit"
class="btn btn-primary"
[disabled]="!changePasswordForm.form.valid || saving"
>
{{ "Save" | localize }}
</button>
</div>
</form>
</div>
</div>
</section>
</div>

@ -0,0 +1,58 @@
import { Component, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { finalize } from 'rxjs/operators';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import { AppComponentBase } from '@shared/app-component-base';
import {
ChangePasswordDto,
UserServiceProxy
} from '@shared/service-proxies/service-proxies';
import { AbpValidationError } from '@shared/components/validation/abp-validation.api';
@Component({
templateUrl: './change-password.component.html',
animations: [appModuleAnimation()]
})
export class ChangePasswordComponent extends AppComponentBase {
saving = false;
changePasswordDto = new ChangePasswordDto();
newPasswordValidationErrors: Partial<AbpValidationError>[] = [
{
name: 'pattern',
localizationKey:
'PasswordsMustBeAtLeast8CharactersContainLowercaseUppercaseNumber',
},
];
confirmNewPasswordValidationErrors: Partial<AbpValidationError>[] = [
{
name: 'validateEqual',
localizationKey: 'PasswordsDoNotMatch',
},
];
constructor(
injector: Injector,
private userServiceProxy: UserServiceProxy,
private router: Router
) {
super(injector);
}
changePassword() {
this.saving = true;
this.userServiceProxy
.changePassword(this.changePasswordDto)
.pipe(
finalize(() => {
this.saving = false;
})
)
.subscribe((success) => {
if (success) {
abp.message.success('Password changed successfully', 'Success');
this.router.navigate(['/']);
}
});
}
}

@ -0,0 +1,200 @@
<form
class="form-horizontal"
autocomplete="off"
#createUserModal="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'CreateNewUser' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<tabset>
<tab [heading]="'UserDetails' | localize" class="pt-3 px-2">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
required
maxlength="32"
[(ngModel)]="user.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="surname">
{{ "Surname" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="surname"
id="surname"
required
maxlength="32"
[(ngModel)]="user.surname"
#surnameModel="ngModel"
#surnameEl
/>
<abp-validation-summary
[control]="surnameModel"
[controlEl]="surnameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="userName">
{{ "UserName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="userName"
id="userName"
required
minlength="2"
maxlength="32"
[(ngModel)]="user.userName"
#userNameModel="ngModel"
#userNameEl
/>
<abp-validation-summary
[control]="userNameModel"
[controlEl]="userNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="password">
{{ "Password" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
name="password"
id="password"
required
maxlength="32"
validateEqual="confirmPassword"
reverse="true"
pattern="(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)[0-9a-zA-Z!@#$%^&*()]*$"
[(ngModel)]="user.password"
#passwordModel="ngModel"
#passwordEl
/>
<abp-validation-summary
[control]="passwordModel"
[controlEl]="passwordEl"
[customValidationErrors]="passwordValidationErrors"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="confirmPassword">
{{ "ConfirmPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
name="confirmPassword"
id="confirmPassword"
required
maxlength="32"
validateEqual="password"
reverse="false"
ngModel
#confirmPasswordModel="ngModel"
#confirmPasswordEl
/>
<abp-validation-summary
[control]="confirmPasswordModel"
[controlEl]="confirmPasswordEl"
[customValidationErrors]="confirmPasswordValidationErrors"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="emailAddress">
{{ "EmailAddress" | localize }}
</label>
<div class="col-md-9">
<input
type="email"
class="form-control"
name="emailAddress"
id="emailAddress"
required
maxlength="256"
pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,})+$"
[(ngModel)]="user.emailAddress"
#emailAddressModel="ngModel"
#emailAddressEl
/>
<abp-validation-summary
[control]="emailAddressModel"
[controlEl]="emailAddressEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
name="isActive"
id="isActive"
[(ngModel)]="user.isActive"
/>
<label class="custom-control-label mt-2" for="isActive"></label>
</div>
</div>
</div>
</tab>
<tab [heading]="'UserRoles' | localize" class="pt-3 px-2">
<div class="form-group row mb-0">
<ng-container *ngFor="let role of roles; let i = index">
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[id]="'role_' + i"
[checked]="isRoleChecked(role.normalizedName)"
(change)="onRoleChange(role, $event)"
/>
<label class="custom-control-label" [for]="'role_' + i">
{{ role.name }}
</label>
</div>
</div>
</ng-container>
</div>
</tab>
</tabset>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!createUserModal.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,105 @@
import {
Component,
Injector,
OnInit,
EventEmitter,
Output
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { forEach as _forEach, map as _map } from 'lodash-es';
import { AppComponentBase } from '@shared/app-component-base';
import {
UserServiceProxy,
CreateUserDto,
RoleDto
} from '@shared/service-proxies/service-proxies';
import { AbpValidationError } from '@shared/components/validation/abp-validation.api';
@Component({
templateUrl: './create-user-dialog.component.html'
})
export class CreateUserDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
user = new CreateUserDto();
roles: RoleDto[] = [];
checkedRolesMap: { [key: string]: boolean } = {};
defaultRoleCheckedStatus = false;
passwordValidationErrors: Partial<AbpValidationError>[] = [
{
name: 'pattern',
localizationKey:
'PasswordsMustBeAtLeast8CharactersContainLowercaseUppercaseNumber',
},
];
confirmPasswordValidationErrors: Partial<AbpValidationError>[] = [
{
name: 'validateEqual',
localizationKey: 'PasswordsDoNotMatch',
},
];
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
public _userService: UserServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this.user.isActive = true;
this._userService.getRoles().subscribe((result) => {
this.roles = result.items;
this.setInitialRolesStatus();
});
}
setInitialRolesStatus(): void {
_map(this.roles, (item) => {
this.checkedRolesMap[item.normalizedName] = this.isRoleChecked(
item.normalizedName
);
});
}
isRoleChecked(normalizedName: string): boolean {
// just return default role checked status
// it's better to use a setting
return this.defaultRoleCheckedStatus;
}
onRoleChange(role: RoleDto, $event) {
this.checkedRolesMap[role.normalizedName] = $event.target.checked;
}
getCheckedRoles(): string[] {
const roles: string[] = [];
_forEach(this.checkedRolesMap, function (value, key) {
if (value) {
roles.push(key);
}
});
return roles;
}
save(): void {
this.saving = true;
this.user.roleNames = this.getCheckedRoles();
this._userService.create(this.user).subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,149 @@
<form
class="form-horizontal"
autocomplete="off"
#editUserModal="ngForm"
(ngSubmit)="save()"
>
<abp-modal-header
[title]="'EditUser' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<tabset>
<tab [heading]="'UserDetails' | localize" class="pt-3 px-2">
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="name">
{{ "Name" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="name"
id="name"
required
maxlength="32"
[(ngModel)]="user.name"
#nameModel="ngModel"
#nameEl
/>
<abp-validation-summary
[control]="nameModel"
[controlEl]="nameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="surname">
{{ "Surname" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="surname"
id="surname"
required
maxlength="32"
[(ngModel)]="user.surname"
#surnameModel="ngModel"
#surnameEl
/>
<abp-validation-summary
[control]="surnameModel"
[controlEl]="surnameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="userName">
{{ "UserName" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="userName"
id="userName"
required
minlength="2"
maxlength="32"
[(ngModel)]="user.userName"
#userNameModel="ngModel"
#userNameEl
/>
<abp-validation-summary
[control]="userNameModel"
[controlEl]="userNameEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="emailAddress">
{{ "EmailAddress" | localize }}
</label>
<div class="col-md-9">
<input
type="email"
class="form-control"
name="emailAddress"
id="emailAddress"
required
maxlength="256"
pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,})+$"
[(ngModel)]="user.emailAddress"
#emailAddressModel="ngModel"
#emailAddressEl
/>
<abp-validation-summary
[control]="emailAddressModel"
[controlEl]="emailAddressEl"
></abp-validation-summary>
</div>
</div>
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
name="isActive"
id="isActive"
[(ngModel)]="user.isActive"
/>
<label class="custom-control-label mt-2" for="isActive"></label>
</div>
</div>
</div>
</tab>
<tab [heading]="'UserRoles' | localize" class="pt-3 px-2">
<div class="form-group row mb-0">
<ng-container *ngFor="let role of roles; let i = index">
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[id]="'role_' + i"
[checked]="isRoleChecked(role.normalizedName)"
(change)="onRoleChange(role, $event)"
/>
<label class="custom-control-label" [for]="'role_' + i">
{{ role.name }}
</label>
</div>
</div>
</ng-container>
</div>
</tab>
</tabset>
</div>
<abp-modal-footer
[cancelDisabled]="saving"
[saveDisabled]="!editUserModal.form.valid || saving"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,91 @@
import {
Component,
Injector,
OnInit,
EventEmitter,
Output
} from '@angular/core';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { forEach as _forEach, includes as _includes, map as _map } from 'lodash-es';
import { AppComponentBase } from '@shared/app-component-base';
import {
UserServiceProxy,
UserDto,
RoleDto
} from '@shared/service-proxies/service-proxies';
@Component({
templateUrl: './edit-user-dialog.component.html'
})
export class EditUserDialogComponent extends AppComponentBase
implements OnInit {
saving = false;
user = new UserDto();
roles: RoleDto[] = [];
checkedRolesMap: { [key: string]: boolean } = {};
id: number;
@Output() onSave = new EventEmitter<any>();
constructor(
injector: Injector,
public _userService: UserServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit(): void {
this._userService.get(this.id).subscribe((result) => {
this.user = result;
this._userService.getRoles().subscribe((result2) => {
this.roles = result2.items;
this.setInitialRolesStatus();
});
});
}
setInitialRolesStatus(): void {
_map(this.roles, (item) => {
this.checkedRolesMap[item.normalizedName] = this.isRoleChecked(
item.normalizedName
);
});
}
isRoleChecked(normalizedName: string): boolean {
return _includes(this.user.roleNames, normalizedName);
}
onRoleChange(role: RoleDto, $event) {
this.checkedRolesMap[role.normalizedName] = $event.target.checked;
}
getCheckedRoles(): string[] {
const roles: string[] = [];
_forEach(this.checkedRolesMap, function (value, key) {
if (value) {
roles.push(key);
}
});
return roles;
}
save(): void {
this.saving = true;
this.user.roleNames = this.getCheckedRoles();
this._userService.update(this.user).subscribe(
() => {
this.notify.info(this.l('SavedSuccessfully'));
this.bsModalRef.hide();
this.onSave.emit();
},
() => {
this.saving = false;
}
);
}
}

@ -0,0 +1,68 @@
<form
class="form-horizontal"
autocomplete="off"
#resetPasswordModal="ngForm"
(ngSubmit)="resetPassword()"
>
<abp-modal-header
[title]="'ResetPassword' | localize"
(onCloseClick)="bsModalRef.hide()"
></abp-modal-header>
<div class="modal-body">
<div class="row">
<div class="col-md-9 offset-md-3">
<p class="text-info mb-1">
{{ "ResetPasswordStepOneInfo" | localize }}
</p>
</div>
</div>
<div class="form-group row required">
<label class="col-md-3 col-form-label" for="adminPassword">
{{ "AdminPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="password"
class="form-control"
id="adminPassword"
name="adminPassword"
required
[(ngModel)]="resetPasswordDto.adminPassword"
#adminPasswordModel="ngModel"
#adminPasswordEl
/>
<abp-validation-summary
[control]="adminPasswordModel"
[controlEl]="adminPasswordEl"
></abp-validation-summary>
</div>
</div>
<div class="row">
<div class="col-md-9 offset-md-3">
<p class="text-info mb-1">
{{ "ResetPasswordStepTwoInfo" | localize }}
</p>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 col-form-label" for="newPassword">
{{ "NewPassword" | localize }}
</label>
<div class="col-md-9">
<input
type="text"
class="form-control"
name="NewPassword"
id="newPassword"
readonly
[ngModel]="resetPasswordDto.newPassword"
/>
</div>
</div>
</div>
<abp-modal-footer
[cancelDisabled]="isLoading"
[saveDisabled]="!resetPasswordModal.form.valid || isLoading"
(onCancelClick)="bsModalRef.hide()"
></abp-modal-footer>
</form>

@ -0,0 +1,49 @@
import { Component, OnInit, Injector } from '@angular/core';
import { AppComponentBase } from '@shared/app-component-base';
import {
UserServiceProxy,
ResetPasswordDto
} from '@shared/service-proxies/service-proxies';
import { BsModalRef } from 'ngx-bootstrap/modal';
@Component({
selector: 'app-reset-password',
templateUrl: './reset-password.component.html'
})
export class ResetPasswordDialogComponent extends AppComponentBase
implements OnInit {
public isLoading = false;
public resetPasswordDto: ResetPasswordDto;
id: number;
constructor(
injector: Injector,
private _userService: UserServiceProxy,
public bsModalRef: BsModalRef
) {
super(injector);
}
ngOnInit() {
this.isLoading = true;
this.resetPasswordDto = new ResetPasswordDto();
this.resetPasswordDto.userId = this.id;
this.resetPasswordDto.newPassword = Math.random()
.toString(36)
.substr(2, 10);
this.isLoading = false;
}
public resetPassword(): void {
this.isLoading = true;
this._userService.resetPassword(this.resetPasswordDto).subscribe(
() => {
this.notify.info('Password Reset');
this.bsModalRef.hide();
},
() => {
this.isLoading = false;
}
);
}
}

@ -0,0 +1,234 @@
<div [@routerTransition]>
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-6">
<h1>{{ "Users" | localize }}</h1>
</div>
<div class="col-6 text-right">
<a href="javascript:;" class="btn bg-blue" (click)="createUser()">
<i class="fa fa-plus-square"></i>
{{ "Create" | localize }}
</a>
</div>
</div>
</div>
</section>
<section class="content px-2">
<div class="container-fluid">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col-md-6">&emsp;</div>
<div class="col-md-6">
<div class="input-group">
<div class="input-group-prepend">
<button
type="button"
class="btn bg-blue"
(click)="getDataPage(1)"
>
<i class="fas fa-search"></i>
</button>
</div>
<input
type="text"
class="form-control"
name="keyword"
[placeholder]="'SearchWithThreeDot' | localize"
[(ngModel)]="keyword"
(keyup.enter)="getDataPage(1)"
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-default"
(click)="advancedFiltersVisible = !advancedFiltersVisible"
>
<i
class="fas"
[class.fa-angle-up]="advancedFiltersVisible"
[class.fa-angle-down]="!advancedFiltersVisible"
></i>
</button>
</div>
</div>
</div>
</div>
<div *ngIf="advancedFiltersVisible" class="card mb-0 mt-1">
<div class="card-body">
<form class="form-horizontal">
<div class="row">
<div class="col-md-6">
<div class="form-group row mb-0">
<label class="col-md-3 col-form-label">
{{ "IsActive" | localize }}
</label>
<div class="col-md-9 pt-2">
<div class="custom-control custom-radio d-inline">
<input
type="radio"
class="custom-control-input"
id="isActiveAll"
name="isActive"
[(ngModel)]="isActive"
[value]="undefined"
checked
/>
<label class="custom-control-label" for="isActiveAll">
{{ "All" | localize }}
</label>
</div>
<div class="custom-control custom-radio d-inline mx-3">
<input
type="radio"
class="custom-control-input"
id="isActiveActive"
name="isActive"
[(ngModel)]="isActive"
[value]="true"
/>
<label
class="custom-control-label"
for="isActiveActive"
>
{{ "Yes" | localize }}
</label>
</div>
<div class="custom-control custom-radio d-inline">
<input
type="radio"
class="custom-control-input"
id="isActivePassive"
name="isActive"
[(ngModel)]="isActive"
[value]="false"
/>
<label
class="custom-control-label"
for="isActivePassive"
>
{{ "No" | localize }}
</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="card-footer">
<button
type="button"
class="btn bg-blue"
(click)="getDataPage(1)"
>
{{ "Search" | localize }}
</button>
<button
type="button"
class="btn btn-default float-right"
(click)="clearFilters()"
>
{{ "Clear" | localize }}
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-bordered" [busy]="isTableLoading">
<thead class="bg-light">
<tr>
<th>{{ "UserName" | localize }}</th>
<th>{{ "FullName" | localize }}</th>
<th>{{ "EmailAddress" | localize }}</th>
<th>{{ "IsActive" | localize }}</th>
<th style="width: 310px;">{{ "Actions" | localize }}</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="
let user of users
| paginate
: {
id: 'server',
itemsPerPage: pageSize,
currentPage: pageNumber,
totalItems: totalItems
}
"
>
<td>{{ user.userName }}</td>
<td>{{ user.fullName }}</td>
<td>{{ user.emailAddress }}</td>
<td>
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
disabled
[checked]="user.isActive"
/>
<label class="custom-control-label"></label>
</div>
</td>
<td>
<button
type="button"
class="btn btn-sm bg-secondary"
(click)="editUser(user)"
>
<i class="fas fa-pencil-alt"></i>
{{ "Edit" | localize }}
</button>
<button
type="button"
class="btn btn-sm bg-danger mx-2"
(click)="delete(user)"
>
<i class="fas fa-trash"></i>
{{ "Delete" | localize }}
</button>
<button
type="button"
class="btn btn-sm bg-secondary"
(click)="resetPassword(user)"
>
<i class="fas fa-lock"></i>
{{ "ResetPassword" | localize }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer table-card-footer bg-light border-top">
<div class="row">
<div class="col-sm-4 col-12 text-sm-left text-center">
<button class="btn btn-secondary" (click)="refresh()">
<i class="fas fa-redo-alt"></i>
</button>
</div>
<div class="col-sm-4 col-12 text-center">
<p class="mb-0 my-2">
{{ "TotalRecordsCount" | localize: totalItems }}
</p>
</div>
<div class="col-sm-4 col-12">
<div class="float-sm-right m-auto">
<abp-pagination-controls
id="server"
(pageChange)="getDataPage($event)"
>
</abp-pagination-controls>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

@ -0,0 +1,134 @@
import { Component, Injector } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import {
PagedListingComponentBase,
PagedRequestDto
} from 'shared/paged-listing-component-base';
import {
UserServiceProxy,
UserDto,
UserDtoPagedResultDto
} from '@shared/service-proxies/service-proxies';
import { CreateUserDialogComponent } from './create-user/create-user-dialog.component';
import { EditUserDialogComponent } from './edit-user/edit-user-dialog.component';
import { ResetPasswordDialogComponent } from './reset-password/reset-password.component';
class PagedUsersRequestDto extends PagedRequestDto {
keyword: string;
isActive: boolean | null;
}
@Component({
templateUrl: './users.component.html',
animations: [appModuleAnimation()]
})
export class UsersComponent extends PagedListingComponentBase<UserDto> {
users: UserDto[] = [];
keyword = '';
isActive: boolean | null;
advancedFiltersVisible = false;
constructor(
injector: Injector,
private _userService: UserServiceProxy,
private _modalService: BsModalService
) {
super(injector);
}
createUser(): void {
this.showCreateOrEditUserDialog();
}
editUser(user: UserDto): void {
this.showCreateOrEditUserDialog(user.id);
}
public resetPassword(user: UserDto): void {
this.showResetPasswordUserDialog(user.id);
}
clearFilters(): void {
this.keyword = '';
this.isActive = undefined;
this.getDataPage(1);
}
protected list(
request: PagedUsersRequestDto,
pageNumber: number,
finishedCallback: Function
): void {
request.keyword = this.keyword;
request.isActive = this.isActive;
this._userService
.getAll(
request.keyword,
request.isActive,
request.skipCount,
request.maxResultCount
)
.pipe(
finalize(() => {
finishedCallback();
})
)
.subscribe((result: UserDtoPagedResultDto) => {
this.users = result.items;
this.showPaging(result, pageNumber);
});
}
protected delete(user: UserDto): void {
abp.message.confirm(
this.l('UserDeleteWarningMessage', user.fullName),
undefined,
(result: boolean) => {
if (result) {
this._userService.delete(user.id).subscribe(() => {
abp.notify.success(this.l('SuccessfullyDeleted'));
this.refresh();
});
}
}
);
}
private showResetPasswordUserDialog(id?: number): void {
this._modalService.show(ResetPasswordDialogComponent, {
class: 'modal-lg',
initialState: {
id: id,
},
});
}
private showCreateOrEditUserDialog(id?: number): void {
let createOrEditUserDialog: BsModalRef;
if (!id) {
createOrEditUserDialog = this._modalService.show(
CreateUserDialogComponent,
{
class: 'modal-lg',
}
);
} else {
createOrEditUserDialog = this._modalService.show(
EditUserDialogComponent,
{
class: 'modal-lg',
initialState: {
id: id,
},
}
);
}
createOrEditUserDialog.content.onSave.subscribe(() => {
this.refresh();
});
}
}

@ -0,0 +1,24 @@
"use strict";
var abp = abp || {};
(function () {
if (!FreezeUI || !UnFreezeUI) {
return;
}
abp.ui.setBusy = function (elm, text, delay) {
FreezeUI({
element: elm,
text: text ? text : " ",
delay: delay
});
};
abp.ui.clearBusy = function (elm, delay) {
UnFreezeUI({
element: elm,
delay: delay
});
};
})();

@ -0,0 +1,128 @@
var abp = abp || {};
(function () {
if (!Swal) {
return;
}
/* MESSAGE **************************************************/
var showMessage = function showMessage(type, message, title, isHtml, options) {
if (!title) {
title = message;
message = undefined;
}
options = options || {};
options.title = title;
options.icon = type;
options.confirmButtonText = options.confirmButtonText || abp.localization.abpWeb("Ok");
if (isHtml) {
options.html = message;
} else {
options.text = message;
}
return Swal.fire(options);
};
abp.message.info = function (message, title, isHtml, options) {
return showMessage("info", message, title, isHtml, options);
};
abp.message.success = function (message, title, isHtml, options) {
return showMessage("success", message, title, isHtml, options);
};
abp.message.warn = function (message, title, isHtml, options) {
return showMessage("warning", message, title, isHtml, options);
};
abp.message.error = function (message, title, isHtml, options) {
return showMessage("error", message, title, isHtml, options);
};
abp.message.confirm = function (message, titleOrCallback, callback, isHtml, options) {
var title = undefined;
if (typeof titleOrCallback === "function") {
callback = titleOrCallback;
} else if (titleOrCallback) {
title = titleOrCallback;
}
options = options || {};
options.title = title ? title : abp.localization.abpWeb("AreYouSure");
options.icon = "warning";
options.confirmButtonText = options.confirmButtonText || abp.localization.abpWeb("Yes");
options.cancelButtonText = options.cancelButtonText || abp.localization.abpWeb("Cancel");
options.showCancelButton = true;
if (isHtml) {
options.html = message;
} else {
options.text = message;
}
return Swal.fire(options).then(function (result) {
callback && callback(result.value);
});
};
/* NOTIFICATION *********************************************/
var Toast = Swal.mixin({
toast: true,
position: "bottom-end",
showConfirmButton: false,
timer: 3000
});
var showNotification = function showNotification(type, message, title, options) {
var icon = options.customClass.icon ? "<i class=\"mr-2 text-light ".concat(options.customClass.icon, "\"></i>") : "";
if (title) {
options.title = "".concat(icon, "<span class=\"text-light\">").concat(title, "</span>");
}
options.html = "".concat(title ? "" : icon, "\n <span class=\"text-light\">").concat(message, "</span>");
Toast.fire(options);
};
abp.notify.success = function (message, title, options) {
showNotification("success", message, title, Object.assign({
background: "#34bfa3",
customClass: {
icon: "fas fa-check-circle"
}
}, options));
};
abp.notify.info = function (message, title, options) {
showNotification("info", message, title, Object.assign({
background: "#36a3f7",
customClass: {
icon: "fas fa-info-circle"
}
}, options));
};
abp.notify.warn = function (message, title, options) {
showNotification("warning", message, title, Object.assign({
background: "#ffb822",
customClass: {
icon: "fas fa-exclamation-triangle"
}
}, options));
};
abp.notify.error = function (message, title, options) {
showNotification("error", message, title, Object.assign({
background: "#f4516c",
customClass: {
icon: "fas fa-exclamation-circle"
}
}, options));
};
})();

@ -0,0 +1,18 @@
{
"remoteServiceBaseUrl": "https://localhost:44311",
"appBaseUrl": "http://localhost:4200",
"localeMappings": [
{
"from": "pt-BR",
"to": "pt"
},
{
"from": "zh-CN",
"to": "zh"
},
{
"from": "he-IL",
"to": "he"
}
]
}

@ -0,0 +1,18 @@
{
"remoteServiceBaseUrl": "https://localhost:44311",
"appBaseUrl": "http://localhost:4200",
"localeMappings": [
{
"from": "pt-BR",
"to": "pt"
},
{
"from": "zh-CN",
"to": "zh"
},
{
"from": "he-IL",
"to": "he"
}
]
}

@ -0,0 +1,56 @@
@keyframes spin {
0% {
transform: translateZ(0) rotate(0);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
.freeze-ui {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999999;
background-color: #fff;
opacity: 0.8;
transition: opacity 0.25s;
}
.freeze-ui.is-unfreezing {
opacity: 0;
}
.freeze-ui:after {
content: attr(data-text);
display: block;
max-width: 125px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
font-family: sans-serif;
color: #343a40;
text-align: center;
text-transform: uppercase;
}
.freeze-ui:before {
content: "";
display: block;
width: 75px;
height: 75px;
border-radius: 50%;
border-width: 2px;
border-style: solid;
border-color: transparent #228ae6 #228ae6;
position: absolute;
top: calc(50% - 75px);
left: calc(50% - 75px);
will-change: transform;
animation: spin 0.75s infinite ease-in-out;
}

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