├── .gitignore ├── README.md ├── angular ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.e2e.json ├── ngsw-config.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── configs │ │ │ ├── app-settings.config.ts │ │ │ └── app-toaster.config.ts │ │ ├── core │ │ │ ├── authentication │ │ │ │ └── authentication.service.ts │ │ │ ├── core.module.ts │ │ │ ├── ensureModuleLoadedOnceGuard.ts │ │ │ ├── guards │ │ │ │ ├── admin-guard.ts │ │ │ │ ├── client-auth.guard.ts │ │ │ │ └── no-auth-guard.ts │ │ │ ├── http │ │ │ │ ├── .gitkeep │ │ │ │ └── user │ │ │ │ │ ├── user.service.spec.ts │ │ │ │ │ └── user.service.ts │ │ │ ├── interceptors │ │ │ │ ├── api-prefix.interceptor.ts │ │ │ │ ├── error-handler.interceptor.ts │ │ │ │ └── http.token.interceptor.ts │ │ │ ├── layout │ │ │ │ ├── footer │ │ │ │ │ ├── footer.component.html │ │ │ │ │ ├── footer.component.scss │ │ │ │ │ └── footer.component.ts │ │ │ │ ├── header │ │ │ │ │ ├── header.component.html │ │ │ │ │ ├── header.component.scss │ │ │ │ │ └── header.component.ts │ │ │ │ ├── layout │ │ │ │ │ ├── layout.component.html │ │ │ │ │ ├── layout.component.scss │ │ │ │ │ └── layout.component.ts │ │ │ │ └── search-input │ │ │ │ │ ├── search-input.component.html │ │ │ │ │ ├── search-input.component.scss │ │ │ │ │ └── search-input.component.ts │ │ │ ├── mocks │ │ │ │ └── .gitkeep │ │ │ ├── router.animations.ts │ │ │ ├── services │ │ │ │ ├── analytics.service.ts │ │ │ │ ├── logger.service.ts │ │ │ │ ├── static.service.ts │ │ │ │ └── title.service.ts │ │ │ ├── strategies │ │ │ │ └── preload-modules.strategy.ts │ │ │ ├── template-core.module.ts │ │ │ └── theme.module.ts │ │ ├── modules │ │ │ ├── admin │ │ │ │ └── .gitkeep │ │ │ ├── auth │ │ │ │ ├── auth-routing.module.ts │ │ │ │ ├── auth.module.spec.ts │ │ │ │ ├── auth.module.ts │ │ │ │ ├── forbidden │ │ │ │ │ ├── forbidden.component.css │ │ │ │ │ ├── forbidden.component.html │ │ │ │ │ ├── forbidden.component.spec.ts │ │ │ │ │ └── forbidden.component.ts │ │ │ │ ├── forgot-password │ │ │ │ │ ├── forgot-password.component.css │ │ │ │ │ ├── forgot-password.component.html │ │ │ │ │ ├── forgot-password.component.spec.ts │ │ │ │ │ └── forgot-password.component.ts │ │ │ │ ├── login │ │ │ │ │ ├── login.component.css │ │ │ │ │ ├── login.component.html │ │ │ │ │ ├── login.component.spec.ts │ │ │ │ │ └── login.component.ts │ │ │ │ ├── not-found │ │ │ │ │ ├── not-found.component.html │ │ │ │ │ ├── not-found.component.scss │ │ │ │ │ └── not-found.component.ts │ │ │ │ └── reset-password │ │ │ │ │ ├── reset-password.component.css │ │ │ │ │ ├── reset-password.component.html │ │ │ │ │ ├── reset-password.component.spec.ts │ │ │ │ │ └── reset-password.component.ts │ │ │ └── client │ │ │ │ ├── client-pages-menu.ts │ │ │ │ ├── client-routing.module.ts │ │ │ │ ├── client.component.html │ │ │ │ ├── client.component.ts │ │ │ │ ├── client.module.spec.ts │ │ │ │ ├── client.module.ts │ │ │ │ ├── dashboard │ │ │ │ ├── dashboard.component.html │ │ │ │ └── dashboard.component.ts │ │ │ │ └── signup │ │ │ │ ├── signup.component.css │ │ │ │ ├── signup.component.html │ │ │ │ ├── signup.component.spec.ts │ │ │ │ └── signup.component.ts │ │ └── shared │ │ │ ├── components │ │ │ ├── .gitkeep │ │ │ └── confirm-dialog │ │ │ │ ├── confirm-dialog.component.css │ │ │ │ ├── confirm-dialog.component.html │ │ │ │ ├── confirm-dialog.component.spec.ts │ │ │ │ └── confirm-dialog.component.ts │ │ │ ├── directives │ │ │ └── .gitkeep │ │ │ ├── models │ │ │ ├── application.model.ts │ │ │ ├── attachments.model.ts │ │ │ ├── fortgot-password.ts │ │ │ ├── login.model.ts │ │ │ ├── reset-password.ts │ │ │ ├── user.model.ts │ │ │ └── userInfo.model.ts │ │ │ ├── module │ │ │ ├── shared.module.spec.ts │ │ │ └── shared.module.ts │ │ │ └── pipes │ │ │ ├── capitalize.pipe.ts │ │ │ ├── index.ts │ │ │ ├── plural.pipe.ts │ │ │ ├── round.pipe.ts │ │ │ └── timing.pipe.ts │ ├── assets │ │ ├── icons │ │ │ ├── icon-128x128.png │ │ │ ├── icon-144x144.png │ │ │ ├── icon-152x152.png │ │ │ ├── icon-192x192.png │ │ │ ├── icon-384x384.png │ │ │ ├── icon-512x512.png │ │ │ ├── icon-72x72.png │ │ │ └── icon-96x96.png │ │ ├── images │ │ │ └── logo │ │ │ │ └── logo.jpg │ │ └── scss │ │ │ └── theme │ │ │ ├── font-size.scss │ │ │ ├── pace.theme.scss │ │ │ ├── styles.scss │ │ │ ├── theme.corporate.ts │ │ │ ├── theme.cosmic.ts │ │ │ ├── theme.default.ts │ │ │ └── themes.scss │ ├── browserslist │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── manifest.json │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json ├── tsconfig.json └── tslint.json └── node ├── .env ├── .gitignore ├── README.md ├── app.js ├── bin └── www ├── package-lock.json ├── package.json ├── public ├── static │ └── countries.json └── stylesheets │ └── style.css ├── routes ├── client-signup.js ├── file-delete.js ├── file-upload.js ├── forgot-password.js ├── index.js ├── login.js ├── reset-password.js └── users.js └── views ├── error.ejs └── index.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # IDEs and editors 3 | /.idea 4 | .project 5 | .classpath 6 | .c9/ 7 | *.launch 8 | .settings/ 9 | *.sublime-workspace 10 | 11 | # IDE - VSCode 12 | .vscode/* 13 | !.vscode/settings.json 14 | !.vscode/tasks.json 15 | !.vscode/launch.json 16 | !.vscode/extensions.json 17 | 18 | 19 | /nbproject/private/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular6 realworld highly scalable application 2 | 3 | The project is inspired by an article wrote on Medium called How to define a highly scalable folder structure for your Angular project. The project is based on best practices from the community, other github Angular projects and my own experiences from working on couple of Angular projects. 4 | 5 | # Angular Requirements 6 | 7 | The angular project needs to be a minimum version of listed below. 8 | 9 | 1. Angular 6.x 10 | 2. Node.js 10.x and npm 6.x 11 | 12 | #Angular Project setup 13 | 14 | 1. git clone https://github.com/rajaramtt/angular6-realworld-highly-scalable-application.git 15 | 2. cd angular6-realworld-highly-scalable-application/angular 16 | 3. 17 | ```sh 18 | npm install 19 | ``` 20 | 4. Congigurations - Pending- Skip now 21 | 22 | 5. unit tests [Karma](https://karma-runner.github.io) - Pending- Skip now 23 | `ng test` 24 | 25 | 6. end-to-end tests [Protractor](http://www.protractortest.org/) - Pending- Skip now 26 | ` ng e2e` 27 | 28 | 7. 29 | ```sh 30 | npm start 31 | ``` 32 | OR 33 | 34 | `ng serve` 35 | 36 | 8. Open brower `http://localhost:4200/` 37 | 38 | 9. Build 39 | `ng build --prod` 40 | 41 | 42 | ## Node.js Requirements: 43 | 44 | The node.js project needs to be a minimum version of listed below. 45 | 46 | 1. ExpressJs 4.x 47 | 2. Node.js 10.x and npm 6.x 48 | 3. MongoDB 4.x 49 | 50 | #Angular Project setup 51 | 52 | 1. git clone https://github.com/rajaramtt/angular6-realworld-highly-scalable-application.git 53 | 2. cd angular6-realworld-highly-scalable-application/node 54 | 3. 55 | ```sh 56 | npm install 57 | ``` 58 | 4. start MongoDB 59 | 5. 60 | ```sh 61 | npm start 62 | ``` 63 | 6. Open brower http://localhost:3000 64 | 65 | ## Help/Assistance 66 | 67 | Developer: Raja Rama Mohan Thavalam 68 | 69 | ## License 70 | 71 | 72 | (The MIT License) 73 | 74 | Copyright (c) 2018 Raja Rama Mohan T 75 | 76 | Permission is hereby granted, free of charge, to any person obtaining 77 | a copy of this software and associated documentation files (the 78 | 'Software'), to deal in the Software without restriction, including 79 | without limitation the rights to use, copy, modify, merge, publish, 80 | distribute, sublicense, and/or sell copies of the Software, and to 81 | permit persons to whom the Software is furnished to do so, subject to 82 | the following conditions: 83 | 84 | The above copyright notice and this permission notice shall be 85 | included in all copies or substantial portions of the Software. 86 | 87 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 88 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 89 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 90 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 91 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 92 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 93 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /angular/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /angular/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /angular/README.md: -------------------------------------------------------------------------------- 1 | # angular6 realworld highly scalable application 2 | 3 | The project is inspired by an article wrote on Medium called How to define a highly scalable folder structure for your Angular project. The project is based on best practices from the community, other github Angular projects and my own experiences from working on couple of Angular projects. 4 | 5 | # Angular Requirements 6 | 7 | The angular project needs to be a minimum version of listed below. 8 | 9 | 1. Angular 6.x 10 | 2. Node.js 10.x and npm 6.x 11 | 12 | #Angular Project setup 13 | 14 | 1. git clone https://github.com/rajaramtt/angular6-realworld-highly-scalable-application.git 15 | 2. cd angular6-realworld-highly-scalable-application/angular 16 | 3. 17 | ```sh 18 | npm install 19 | ``` 20 | 4. Congigurations - Pending- Skip now 21 | 22 | 5. unit tests [Karma](https://karma-runner.github.io) - Pending- Skip now 23 | `ng test` 24 | 25 | 6. end-to-end tests [Protractor](http://www.protractortest.org/) - Pending- Skip now 26 | ` ng e2e` 27 | 28 | 7. 29 | ```sh 30 | npm start 31 | ``` 32 | OR 33 | 34 | `ng serve` 35 | 36 | 8. Open brower `http://localhost:4200/` 37 | 38 | 9. Build 39 | `ng build --prod` 40 | 41 | ## Help/Assistance 42 | 43 | Developer: Raja Rama Mohan Thavalam -------------------------------------------------------------------------------- /angular/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular6-realworld-highly-scalable-application": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular6-realworld-highly-scalable-application", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets", 24 | "src/manifest.json" 25 | ], 26 | "styles": [ 27 | "node_modules/bootstrap/dist/css/bootstrap.css", 28 | "node_modules/font-awesome/css/font-awesome.css", 29 | "node_modules/typeface-exo/index.css", 30 | "node_modules/roboto-fontface/css/roboto/roboto-fontface.css", 31 | "node_modules/ionicons/scss/ionicons.scss", 32 | "node_modules/socicon/css/socicon.css", 33 | "node_modules/nebular-icons/scss/nebular-icons.scss", 34 | "node_modules/pace-js/templates/pace-theme-flash.tmpl.css", 35 | "src/assets/scss/theme/styles.scss", 36 | "node_modules/angular2-toaster/toaster.min.css", 37 | "src/styles.scss" 38 | ], 39 | "scripts": [], 40 | 41 | }, 42 | "configurations": { 43 | "production": { 44 | "fileReplacements": [ 45 | { 46 | "replace": "src/environments/environment.ts", 47 | "with": "src/environments/environment.prod.ts" 48 | } 49 | ], 50 | "optimization": true, 51 | "outputHashing": "all", 52 | "sourceMap": false, 53 | "extractCss": true, 54 | "namedChunks": false, 55 | "aot": true, 56 | "extractLicenses": true, 57 | "vendorChunk": false, 58 | "buildOptimizer": true, 59 | "serviceWorker": true 60 | } 61 | } 62 | }, 63 | "serve": { 64 | "builder": "@angular-devkit/build-angular:dev-server", 65 | "options": { 66 | "browserTarget": "angular6-realworld-highly-scalable-application:build" 67 | }, 68 | "configurations": { 69 | "production": { 70 | "browserTarget": "angular6-realworld-highly-scalable-application:build:production" 71 | } 72 | } 73 | }, 74 | "extract-i18n": { 75 | "builder": "@angular-devkit/build-angular:extract-i18n", 76 | "options": { 77 | "browserTarget": "angular6-realworld-highly-scalable-application:build" 78 | } 79 | }, 80 | "test": { 81 | "builder": "@angular-devkit/build-angular:karma", 82 | "options": { 83 | "main": "src/test.ts", 84 | "polyfills": "src/polyfills.ts", 85 | "tsConfig": "src/tsconfig.spec.json", 86 | "karmaConfig": "src/karma.conf.js", 87 | "styles": [ 88 | "src/styles.css" 89 | ], 90 | "scripts": [], 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets", 94 | "src/manifest.json" 95 | ] 96 | } 97 | }, 98 | "lint": { 99 | "builder": "@angular-devkit/build-angular:tslint", 100 | "options": { 101 | "tsConfig": [ 102 | "src/tsconfig.app.json", 103 | "src/tsconfig.spec.json" 104 | ], 105 | "exclude": [ 106 | "**/node_modules/**" 107 | ] 108 | } 109 | } 110 | } 111 | }, 112 | "angular6-realworld-highly-scalable-application-e2e": { 113 | "root": "e2e/", 114 | "projectType": "application", 115 | "architect": { 116 | "e2e": { 117 | "builder": "@angular-devkit/build-angular:protractor", 118 | "options": { 119 | "protractorConfig": "e2e/protractor.conf.js", 120 | "devServerTarget": "angular6-realworld-highly-scalable-application:serve" 121 | }, 122 | "configurations": { 123 | "production": { 124 | "devServerTarget": "angular6-realworld-highly-scalable-application:serve:production" 125 | } 126 | } 127 | }, 128 | "lint": { 129 | "builder": "@angular-devkit/build-angular:tslint", 130 | "options": { 131 | "tsConfig": "e2e/tsconfig.e2e.json", 132 | "exclude": [ 133 | "**/node_modules/**" 134 | ] 135 | } 136 | } 137 | } 138 | } 139 | }, 140 | "defaultProject": "angular6-realworld-highly-scalable-application" 141 | } -------------------------------------------------------------------------------- /angular/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /angular/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular6 realworld highly scalable application!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /angular/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /angular/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /angular/ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "/index.html", 3 | "assetGroups": [{ 4 | "name": "app", 5 | "installMode": "prefetch", 6 | "resources": { 7 | "files": [ 8 | "/favicon.ico", 9 | "/index.html", 10 | "/*.css", 11 | "/*.js" 12 | ] 13 | } 14 | }, { 15 | "name": "assets", 16 | "installMode": "lazy", 17 | "updateMode": "prefetch", 18 | "resources": { 19 | "files": [ 20 | "/assets/**" 21 | ] 22 | } 23 | }] 24 | } -------------------------------------------------------------------------------- /angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular6-realworld-highly-scalable-application", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.0.3", 15 | "@angular/cdk": "^6.0.0", 16 | "@angular/common": "^6.0.3", 17 | "@angular/compiler": "^6.0.3", 18 | "@angular/core": "^6.0.3", 19 | "@angular/forms": "^6.0.3", 20 | "@angular/http": "^6.0.3", 21 | "@angular/platform-browser": "^6.0.3", 22 | "@angular/platform-browser-dynamic": "^6.0.3", 23 | "@angular/pwa": "^0.6.8", 24 | "@angular/router": "^6.0.3", 25 | "@angular/service-worker": "^6.0.3", 26 | "@nebular/bootstrap": "2.0.1", 27 | "@nebular/theme": "2.0.1", 28 | "@ng-bootstrap/ng-bootstrap": "^2.2.0", 29 | "angular2-text-mask": "^9.0.0", 30 | "angular2-toaster": "^6.1.0", 31 | "bootstrap": "^4.1.2", 32 | "classlist.js": "1.1.20150312", 33 | "core-js": "2.5.1", 34 | "font-awesome": "^4.7.0", 35 | "http-status-codes": "^1.3.0", 36 | "intl": "1.2.5", 37 | "ionicons": "2.0.1", 38 | "nebular-icons": "1.0.8", 39 | "ng2-file-upload": "^1.3.0", 40 | "ng2-smart-table": "^1.3.5", 41 | "node-sass": "^4.9.3", 42 | "normalize.css": "6.0.0", 43 | "pace-js": "1.0.2", 44 | "roboto-fontface": "^0.9.0", 45 | "rxjs": "^6.1.0", 46 | "rxjs-compat": "^6.1.0", 47 | "socicon": "3.0.5", 48 | "typeface-exo": "0.0.22", 49 | "web-animations-js": "2.2.5", 50 | "zone.js": "^0.8.26" 51 | }, 52 | "devDependencies": { 53 | "@angular-devkit/build-angular": "~0.6.8", 54 | "@angular/cli": "~6.0.8", 55 | "@angular/compiler-cli": "^6.0.3", 56 | "@angular/language-service": "^6.0.3", 57 | "@types/jasmine": "~2.8.6", 58 | "@types/jasminewd2": "~2.0.3", 59 | "@types/node": "~8.9.4", 60 | "codelyzer": "~4.2.1", 61 | "jasmine-core": "~2.99.1", 62 | "jasmine-spec-reporter": "~4.2.1", 63 | "karma": "~1.7.1", 64 | "karma-chrome-launcher": "~2.2.0", 65 | "karma-coverage-istanbul-reporter": "~2.0.0", 66 | "karma-jasmine": "~1.1.1", 67 | "karma-jasmine-html-reporter": "^0.2.2", 68 | "ng2-completer": "^2.0.8", 69 | "protractor": "~5.3.0", 70 | "ts-node": "~5.0.1", 71 | "tslint": "~5.9.1", 72 | "typescript": "~2.7.2" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /angular/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes, PreloadAllModules, NoPreloading, ExtraOptions } from '@angular/router'; 3 | 4 | import { PreloadModulesStrategy } from './core/strategies/preload-modules.strategy'; 5 | 6 | const app_routes: Routes = [ 7 | { path: 'auth', loadChildren: './modules/auth/auth.module#AuthModule' }, 8 | { path: 'client', loadChildren: './modules/client/client.module#ClientModule' }, 9 | { path: '', pathMatch: 'full', redirectTo: '/auth/login' }, 10 | { path: '**', redirectTo: '/auth/404' }, 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ RouterModule.forRoot(app_routes, { preloadingStrategy: PreloadAllModules }) ], 15 | exports: [ RouterModule ] 16 | }) 17 | export class AppRoutingModule {} 18 | -------------------------------------------------------------------------------- /angular/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/app.component.css -------------------------------------------------------------------------------- /angular/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /angular/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'app'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('app'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular6 realworld highly scalable application!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /angular/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ToasterConfig } from 'angular2-toaster'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent { 10 | 11 | public config: ToasterConfig = new ToasterConfig({ 12 | animation: 'fade', 13 | positionClass: 'toast-top-full-width', 14 | showCloseButton: true, 15 | timeout: 8000 16 | }); 17 | 18 | title = 'angular6 realworld highly scalable application'; 19 | } 20 | -------------------------------------------------------------------------------- /angular/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | 4 | import { NgModule } from '@angular/core'; 5 | 6 | import { AppComponent } from './app.component'; 7 | import { ServiceWorkerModule } from '@angular/service-worker'; 8 | import { environment } from '../environments/environment'; 9 | 10 | import { AppRoutingModule } from './app-routing.module'; 11 | import { CoreModule } from './core/core.module'; 12 | import { ThemeModule } from './core/theme.module'; 13 | import { TemplateCoreModule } from './core/template-core.module'; 14 | import { ToasterModule } from 'angular2-toaster'; 15 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent, 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | BrowserAnimationsModule, 24 | ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }), 25 | AppRoutingModule, 26 | CoreModule, 27 | ThemeModule.forRoot(), 28 | TemplateCoreModule.forRoot(), 29 | ToasterModule.forRoot(), 30 | NgbModule.forRoot() 31 | 32 | ], 33 | providers: [], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule { } 37 | -------------------------------------------------------------------------------- /angular/src/app/configs/app-settings.config.ts: -------------------------------------------------------------------------------- 1 | export const appSettings = { 2 | appTitle: 'Raja Ram App', 3 | appLogo: '/assets/images/logo/logo.jpg', 4 | applicationTypes: ['Visa', 'Renewal', 'Permanent Residence', 'Others' ] 5 | }; 6 | -------------------------------------------------------------------------------- /angular/src/app/configs/app-toaster.config.ts: -------------------------------------------------------------------------------- 1 | export const appToaster = { 2 | successHead: 'Success', 3 | errorHead: 'An API Error Occurred', 4 | apiErrorHead: 'Error', 5 | loginSucess: 'You have successfully login', 6 | logoutSucess: 'You have successfully log out' 7 | }; 8 | -------------------------------------------------------------------------------- /angular/src/app/core/authentication/authentication.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import 'rxjs/add/operator/map'; 5 | import { catchError, map, tap, debounce } from 'rxjs/operators'; 6 | import { of as observableOf } from 'rxjs'; 7 | 8 | import { environment } from '../../../environments/environment'; 9 | 10 | import { Login } from '../../shared/models/login.model'; 11 | const credentialsKey = 'currentUser'; 12 | 13 | @Injectable() 14 | export class AuthenticationService { 15 | constructor(private http: HttpClient) { 16 | } 17 | 18 | login(loginData: Login): Observable { 19 | const href = `${ environment.login }`; 20 | return this.http.post(href, loginData).pipe( 21 | tap( 22 | function (data) { 23 | if (data.status === 'success') { 24 | const storage = loginData.remember ? localStorage : sessionStorage; 25 | storage.setItem(credentialsKey, JSON.stringify(data)); 26 | } 27 | return data; 28 | } 29 | ) 30 | ); 31 | } 32 | 33 | logout(): Observable { 34 | sessionStorage.removeItem(credentialsKey); 35 | localStorage.removeItem(credentialsKey); 36 | return observableOf(true); 37 | } 38 | 39 | getUserInfo(): Observable { 40 | const savedCredentials = this.getUser(); 41 | return observableOf(savedCredentials); 42 | } 43 | 44 | isLogin() { 45 | if (localStorage.getItem(credentialsKey) || sessionStorage.getItem(credentialsKey)) { 46 | return true; 47 | } 48 | return false; 49 | 50 | } 51 | 52 | getToken() { 53 | const savedCredentials = this.getUser(); 54 | return savedCredentials && savedCredentials['token']; 55 | } 56 | 57 | getUserRole(): Observable { 58 | const savedCredentials = this.getUser(); 59 | return observableOf(savedCredentials['role']); 60 | } 61 | 62 | getUserType() { 63 | const savedCredentials = this.getUser(); 64 | if ( this.isLogin() ) { 65 | return savedCredentials['role']; 66 | } else { 67 | return false; 68 | } 69 | 70 | 71 | } 72 | 73 | private getUser() { 74 | const savedCredentials = sessionStorage.getItem(credentialsKey) || localStorage.getItem(credentialsKey); 75 | return JSON.parse( savedCredentials ); 76 | } 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /angular/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 5 | import { EnsureModuleLoadedOnceGuard } from './ensureModuleLoadedOnceGuard'; 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 7 | 8 | import { AuthenticationService } from './authentication/authentication.service'; 9 | import { TitleService } from './services/title.service'; 10 | import { StaticService } from './services/static.service'; 11 | 12 | 13 | import { ErrorHandlerInterceptor } from './interceptors/error-handler.interceptor'; 14 | import { ApiPrefixInterceptor } from './interceptors/api-prefix.interceptor'; 15 | import { HttpTokenInterceptor } from './interceptors/http.token.interceptor'; 16 | 17 | 18 | @NgModule({ 19 | imports: [ 20 | CommonModule, 21 | RouterModule, 22 | HttpClientModule, 23 | FormsModule, 24 | ReactiveFormsModule, 25 | ], 26 | exports: [ 27 | RouterModule, 28 | HttpClientModule, 29 | FormsModule, 30 | ReactiveFormsModule, 31 | ], 32 | providers: [ 33 | AuthenticationService, 34 | { 35 | provide: HTTP_INTERCEPTORS, 36 | useClass: HttpTokenInterceptor, 37 | multi: true 38 | }, 39 | { 40 | provide: HTTP_INTERCEPTORS, 41 | useClass: ErrorHandlerInterceptor, 42 | multi: true, 43 | }, 44 | { 45 | provide: HTTP_INTERCEPTORS, 46 | useClass: ApiPrefixInterceptor, 47 | multi: true, 48 | }, 49 | TitleService, 50 | StaticService 51 | ] 52 | }) 53 | export class CoreModule extends EnsureModuleLoadedOnceGuard { // Ensure that CoreModule is only loaded into AppModule 54 | // Looks for the module in the parent injector to see if it's already been loaded (only want it loaded once) 55 | constructor(@Optional() @SkipSelf() parentModule: CoreModule, titleService: TitleService) { 56 | super(parentModule); 57 | titleService.init(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /angular/src/app/core/ensureModuleLoadedOnceGuard.ts: -------------------------------------------------------------------------------- 1 | export class EnsureModuleLoadedOnceGuard { 2 | 3 | constructor(targetModule: any) { 4 | if (targetModule) { 5 | throw new Error(`${targetModule.constructor.name} has already been loaded. Import this module in the AppModule only.`); 6 | } 7 | } 8 | 9 | } 10 | 11 | export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) { 12 | if (parentModule) { 13 | throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`); 14 | } 15 | } -------------------------------------------------------------------------------- /angular/src/app/core/guards/admin-guard.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/core/guards/admin-guard.ts -------------------------------------------------------------------------------- /angular/src/app/core/guards/client-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router'; 3 | 4 | import { LoggerService } from '../services/logger.service'; 5 | import { AuthenticationService } from '../authentication/authentication.service'; 6 | import { appToaster } from '../../configs/app-toaster.config'; 7 | import { ToasterService } from 'angular2-toaster'; 8 | 9 | 10 | @Injectable() 11 | export class ClientAuthGuard implements CanActivate, CanActivateChild { 12 | 13 | constructor( 14 | private router: Router, 15 | private logger: LoggerService, 16 | private toasterService: ToasterService, 17 | private authenticationService: AuthenticationService) { } 18 | 19 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { 20 | return this.chekUser(route, state); 21 | 22 | } 23 | 24 | canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { 25 | return this.chekUser(route, state); 26 | } 27 | 28 | private chekUser(route, state): boolean { 29 | const userType = this.authenticationService.getUserType(); 30 | const isLogin = this.authenticationService.isLogin(); 31 | if (userType === 'client' && isLogin) { 32 | return true; 33 | } else if (isLogin) { 34 | this.toasterService.pop('error', appToaster.errorHead, 'Unauthorized: Access is denied'); 35 | // this.router.navigate(['/client/dashboard']); 36 | 37 | this.router.navigateByUrl('/auth/403'); 38 | return false; 39 | } else { 40 | 41 | this.logger.log('Not authenticated, redirecting...'); 42 | this.router.navigate(['/auth/login'], { queryParams: { returnUrl: state.url } }); 43 | return false; 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /angular/src/app/core/guards/no-auth-guard.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/core/guards/no-auth-guard.ts -------------------------------------------------------------------------------- /angular/src/app/core/http/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/core/http/.gitkeep -------------------------------------------------------------------------------- /angular/src/app/core/http/user/user.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { UserService } from './user.service'; 4 | 5 | describe('UserService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [UserService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([UserService], (service: UserService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /angular/src/app/core/http/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { catchError, map, tap, debounce } from 'rxjs/operators'; 5 | import { of as observableOf } from 'rxjs'; 6 | import { FortgotPassword } from '../../../shared/models/fortgot-password'; 7 | import { ResetPassword } from '../../../shared/models/reset-password'; 8 | import { Attachments } from '../../../shared/models/attachments.model'; 9 | import { User } from '../../../shared/models/user.model'; 10 | 11 | import { environment } from '../../../../environments/environment'; 12 | 13 | @Injectable({ 14 | providedIn: 'root' 15 | }) 16 | export class UserService { 17 | 18 | constructor(private http: HttpClient) { 19 | } 20 | 21 | forgotPassword(fotgotData: FortgotPassword): Observable { 22 | const href = `${ environment.fotgotPassword }`; 23 | return this.http.post(href, fotgotData); 24 | } 25 | 26 | 27 | resetPassword(resetData: ResetPassword): Observable { 28 | const href = `${ environment.resetPassword }`; 29 | return this.http.post(href, resetData); 30 | } 31 | 32 | deleteAttachment(attachment: Attachments): Observable { 33 | const href = `${ environment.deleteAttachment }`; 34 | return this.http.post(href, attachment); 35 | } 36 | 37 | clientSignup(clientData: User): Observable { 38 | const href = `${ environment.clientSignup }`; 39 | return this.http.post(href, clientData); 40 | } 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /angular/src/app/core/interceptors/api-prefix.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { environment } from '../../../environments/environment'; 6 | 7 | /** 8 | * Prefixes all requests with `environment.host`. 9 | */ 10 | @Injectable() 11 | export class ApiPrefixInterceptor implements HttpInterceptor { 12 | 13 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 14 | request = request.clone({ url: environment.host + request.url }); 15 | return next.handle(request); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /angular/src/app/core/interceptors/error-handler.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { catchError } from 'rxjs/operators'; 5 | import { Router } from '@angular/router'; 6 | import {UNAUTHORIZED, BAD_REQUEST, FORBIDDEN} from 'http-status-codes'; 7 | 8 | import { LoggerService } from '../services/logger.service'; 9 | import { environment } from '../../../environments/environment'; 10 | import { ToasterService } from 'angular2-toaster'; 11 | import { appToaster } from '../../configs/app-toaster.config'; 12 | 13 | /** 14 | * Adds a default error handler to all requests. 15 | */ 16 | @Injectable() 17 | export class ErrorHandlerInterceptor implements HttpInterceptor { 18 | constructor( 19 | private logger: LoggerService, 20 | private toasterService: ToasterService, 21 | private router: Router 22 | ) { } 23 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 24 | return next.handle(request).pipe(catchError(error => this.errorHandler(error))); 25 | } 26 | 27 | // Customize the default error handler here if needed 28 | private errorHandler(response: HttpEvent): Observable> { 29 | if (!environment.production) { 30 | // Do something with the error 31 | this.logger.logError('Request error ' + JSON.stringify(response)); 32 | } 33 | 34 | // console.error(error); 35 | const httpErrorCode = response['status']; 36 | switch (httpErrorCode) { 37 | case UNAUTHORIZED: 38 | this.router.navigateByUrl('/auth/login'); 39 | break; 40 | case FORBIDDEN: 41 | this.router.navigateByUrl('/auth/403'); 42 | break; 43 | // case BAD_REQUEST: 44 | // this.showError(error.message); 45 | // break; 46 | default: 47 | this.toasterService.pop('error', appToaster.errorHead, response['message']); 48 | } 49 | 50 | 51 | throw response; 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /angular/src/app/core/interceptors/http.token.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector } from '@angular/core'; 2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { AuthenticationService } from '../authentication/authentication.service'; 6 | 7 | @Injectable() 8 | export class HttpTokenInterceptor implements HttpInterceptor { 9 | constructor(private authenticationService: AuthenticationService) {} 10 | 11 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 12 | const headersConfig = { 13 | 'Content-Type': 'application/json', 14 | 'Accept': 'application/json' 15 | }; 16 | 17 | const token = this.authenticationService.getToken(); 18 | if (token) { 19 | headersConfig['Authorization'] = `${token}`; 20 | } 21 | 22 | const request = req.clone({ setHeaders: headersConfig }); 23 | return next.handle(request); 24 | } 25 | } -------------------------------------------------------------------------------- /angular/src/app/core/layout/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | ©angular6-realworld-highly-scalable-application 2018 2 |
3 | 4 | 5 | 6 | 7 |
-------------------------------------------------------------------------------- /angular/src/app/core/layout/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | @import './src/assets/scss/theme/themes'; 2 | @import '~@nebular/theme/styles/global/breakpoints'; 3 | @import '~bootstrap/scss/mixins/breakpoints'; 4 | 5 | @include nb-install-component() { 6 | width: 100%; 7 | display: flex; 8 | justify-content: space-between; 9 | align-items: center; 10 | 11 | .socials { 12 | font-size: 2rem; 13 | 14 | a { 15 | padding: 0.4rem; 16 | color: nb-theme(color-fg); 17 | transition: color ease-out 0.1s; 18 | 19 | &:hover { 20 | color: nb-theme(color-fg-heading); 21 | } 22 | } 23 | } 24 | 25 | @include media-breakpoint-down(is) { 26 | .socials { 27 | font-size: 1.5rem; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | styleUrls: ['./footer.component.scss'], 6 | templateUrl: './footer.component.html' 7 | }) 8 | export class FooterComponent { 9 | } 10 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/header/header.component.scss: -------------------------------------------------------------------------------- 1 | @import './src/assets/scss/theme/themes'; 2 | @import '~bootstrap/scss/mixins/breakpoints'; 3 | @import '~@nebular/theme/styles/global/breakpoints'; 4 | 5 | @include nb-install-component() { 6 | display: flex; 7 | justify-content: space-between; 8 | width: 100%; 9 | 10 | .left { 11 | display: flex; 12 | width: 100%; 13 | order: 0; 14 | flex-direction: row; 15 | } 16 | .right { 17 | order: 1; 18 | flex-direction: row-reverse; 19 | } 20 | 21 | .logo-containter { 22 | display: flex; 23 | align-items: center; 24 | width: calc(#{nb-theme(sidebar-width)} - #{nb-theme(header-padding)}); 25 | } 26 | 27 | .control-item { 28 | display: block; 29 | } 30 | 31 | .header-container { 32 | display: flex; 33 | align-items: center; 34 | width: auto; 35 | 36 | .navigation { 37 | @include nb-ltr(padding-right, nb-theme(padding)); 38 | @include nb-rtl(padding-left, nb-theme(padding)); 39 | font-size: 2.5rem; 40 | text-decoration: none; 41 | 42 | i { 43 | display: block; 44 | } 45 | 46 | } 47 | 48 | .logo { 49 | padding: 0 nb-theme(padding); 50 | font-size: 1.75rem; 51 | font-weight: nb-theme(font-weight-bolder); 52 | @include nb-ltr(border-left, 1px solid nb-theme(separator)); 53 | @include nb-rtl(border-right, 1px solid nb-theme(separator)); 54 | white-space: nowrap; 55 | 56 | span { 57 | font-weight: nb-theme(font-weight-normal); 58 | } 59 | } 60 | } 61 | 62 | @include nb-for-theme(corporate) { 63 | $menu-action-separator-color: #3f4550; 64 | 65 | nb-action { 66 | @include nb-ltr(border-left-color, $menu-action-separator-color); 67 | @include nb-rtl(border-right-color, $menu-action-separator-color); 68 | } 69 | 70 | .header-container .logo { 71 | @include nb-ltr(border, none); 72 | @include nb-rtl(border, none); 73 | } 74 | 75 | .header-container /deep/ ngx-theme-switcher .dropdown-toggle { 76 | color: nb-theme(color-white); 77 | background: transparent; 78 | } 79 | } 80 | 81 | ngx-layout-direction-switcher { 82 | margin: 0 1.5rem; 83 | } 84 | 85 | ngx-theme-switcher { 86 | margin: nb-theme(layout-padding); 87 | margin-top: 0; 88 | margin-bottom: 0; 89 | } 90 | 91 | @include media-breakpoint-down(xl) { 92 | ngx-layout-direction-switcher { 93 | display: none; 94 | } 95 | } 96 | 97 | .toggle-layout /deep/ a { 98 | display: block; 99 | text-decoration: none; 100 | line-height: 1; 101 | 102 | i { 103 | color: nb-theme(color-fg-highlight); 104 | font-size: 2.25rem; 105 | border-radius: 50%; 106 | position: relative; 107 | animation-name: pulse-light; 108 | 109 | &::after { 110 | content: ' '; 111 | // hack to be able to set border-radius 112 | background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); 113 | border-radius: 50%; 114 | pointer-events: none; 115 | 116 | position: absolute; 117 | top: 52.3%; 118 | left: 50%; 119 | transform: translate(-50%, -50%); 120 | width: 13%; 121 | height: 13%; 122 | 123 | animation: 3s linear infinite pulse; 124 | 125 | @include nb-for-theme(default) { 126 | animation-name: pulse-light; 127 | } 128 | } 129 | } 130 | } 131 | 132 | @include keyframes(pulse) { 133 | 0% { 134 | box-shadow: 0 0 1px 0 rgba(nb-theme(color-fg-highlight), 0); 135 | } 136 | 20% { 137 | box-shadow: 0 0 3px 10px rgba(nb-theme(color-fg-highlight), 0.4); 138 | } 139 | 100% { 140 | box-shadow: 0 0 5px 20px rgba(nb-theme(color-fg-highlight), 0); 141 | } 142 | } 143 | 144 | @include keyframes(pulse-light) { 145 | 0% { 146 | box-shadow: 0 0 1px 0 rgba(115, 255, 208, 0); 147 | } 148 | 20% { 149 | box-shadow: 0 0 3px 10px rgba(115, 255, 208, 0.4); 150 | } 151 | 100% { 152 | box-shadow: 0 0 5px 20px rgba(115, 255, 208, 0); 153 | } 154 | } 155 | 156 | @include media-breakpoint-down(md) { 157 | 158 | nb-action:not(.toggle-layout) { 159 | border: none; 160 | } 161 | 162 | .control-item { 163 | display: none; 164 | } 165 | 166 | .toggle-layout { 167 | padding: 0; 168 | } 169 | 170 | ngx-layout-direction-switcher { 171 | display: none; 172 | } 173 | 174 | ngx-theme-switcher { 175 | margin: 0 0.5rem; 176 | } 177 | } 178 | 179 | @include media-breakpoint-down(sm) { 180 | 181 | nb-user /deep/ .user-name { 182 | display: none; 183 | } 184 | } 185 | 186 | @include media-breakpoint-down(is) { 187 | 188 | .header-container { 189 | .logo { 190 | font-size: 1.25rem; 191 | } 192 | } 193 | 194 | .toggle-layout { 195 | display: none; 196 | } 197 | 198 | ngx-theme-switcher { 199 | display: none; 200 | } 201 | 202 | nb-action:not(.toggle-layout) { 203 | padding: 0; 204 | } 205 | } 206 | 207 | @include media-breakpoint-down(xs) { 208 | .right /deep/ { 209 | display: none; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | import { NbMenuService, NbSidebarService } from '@nebular/theme'; 5 | import { ToasterService } from 'angular2-toaster'; 6 | 7 | import { AuthenticationService } from '../../authentication/authentication.service'; 8 | import { AnalyticsService } from '../../services/analytics.service'; 9 | 10 | import { appSettings } from '../../../configs/app-settings.config'; 11 | import { appToaster } from '../../../configs/app-toaster.config'; 12 | 13 | @Component({ 14 | selector: 'app-header', 15 | styleUrls: ['./header.component.scss'], 16 | templateUrl: './header.component.html', 17 | }) 18 | export class HeaderComponent implements OnInit { 19 | 20 | user: any; 21 | appSettings: any; 22 | userMenu = [{ title: 'Profile' }, { title: 'Log out'}]; 23 | 24 | constructor(private sidebarService: NbSidebarService, 25 | private router: Router, 26 | private menuService: NbMenuService, 27 | private toasterService: ToasterService, 28 | private authenticationService: AuthenticationService, 29 | private analyticsService: AnalyticsService) { 30 | } 31 | 32 | ngOnInit() { 33 | this.appSettings = appSettings; 34 | this.authenticationService.getUserInfo() 35 | .subscribe((users: any) => this.user = users); 36 | 37 | this.menuService.onItemClick() 38 | .subscribe((event) => { 39 | this.onContecxtItemSelection(event.item.title); 40 | }); 41 | 42 | 43 | } 44 | 45 | onContecxtItemSelection(title) { 46 | if( title === 'Log out' ) { 47 | this.logout(); 48 | } 49 | } 50 | 51 | toggleSidebar(): boolean { 52 | this.sidebarService.toggle(true, 'menu-sidebar'); 53 | return false; 54 | } 55 | 56 | 57 | goToHome() { 58 | this.menuService.navigateHome(); 59 | } 60 | 61 | startSearch() { 62 | this.analyticsService.trackEvent('startSearch'); 63 | } 64 | 65 | logout() { 66 | this.authenticationService.getUserRole().subscribe((userType) => { 67 | let logOutURL; 68 | console.log(userType); 69 | this.authenticationService.logout().subscribe((res) => { 70 | // if (userType === 'client') { 71 | // logOutURL = '/client/login'; 72 | // } 73 | logOutURL = '/auth/login'; 74 | this.toasterService.pop('success', appToaster.successHead, appToaster.logoutSucess); 75 | //Raja Developer Imp Note: 76 | // for aviod ExpressionChangedAfterItHasBeenCheckedError page reloading. 77 | window.location.reload(); 78 | this.router.navigate([logOutURL], { replaceUrl: true }); 79 | }); 80 | }); 81 | 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/layout/layout.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Client Menu 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/layout/layout.component.scss: -------------------------------------------------------------------------------- 1 | @import './src/assets/scss/theme/themes'; 2 | @import '~@nebular/theme/styles/global/breakpoints'; 3 | @import '~bootstrap/scss/mixins/breakpoints'; 4 | 5 | @include nb-install-component() { 6 | nb-layout-column.small { 7 | flex: 0.15 !important; 8 | } 9 | 10 | nb-sidebar.settings-sidebar { 11 | $sidebar-width: 7.5rem; 12 | 13 | transition: width 0.3s ease; 14 | width: $sidebar-width; 15 | overflow: hidden; 16 | 17 | &.collapsed { 18 | width: 0; 19 | 20 | /deep/ .main-container { 21 | width: 0; 22 | 23 | .scrollable { 24 | width: $sidebar-width; 25 | padding: 1.25rem; 26 | } 27 | } 28 | } 29 | 30 | /deep/ .main-container { 31 | width: $sidebar-width; 32 | background: nb-theme(color-bg); 33 | transition: width 0.3s ease; 34 | overflow: hidden; 35 | 36 | .scrollable { 37 | width: $sidebar-width; 38 | } 39 | 40 | @include nb-for-theme(cosmic) { 41 | background: nb-theme(layout-bg); 42 | } 43 | } 44 | } 45 | 46 | nb-sidebar.menu-sidebar { 47 | 48 | margin-top: nb-theme(sidebar-header-gap); 49 | 50 | @include nb-for-theme(corporate) { 51 | margin-top: 0; 52 | } 53 | 54 | /deep/ .main-container { 55 | height: 56 | calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)} - #{nb-theme(sidebar-header-gap)}) !important; 57 | @include nb-ltr(border-top-right-radius, nb-theme(radius)); 58 | @include nb-rtl(border-top-left-radius, nb-theme(radius)); 59 | 60 | @include nb-for-theme(corporate) { 61 | border: 1px solid nb-theme(separator); 62 | height: 63 | calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important; 64 | } 65 | } 66 | 67 | /deep/ .scrollable { 68 | @include nb-for-theme(corporate) { 69 | padding-top: 0; 70 | 71 | .menu-item:first-child { 72 | border-top: none; 73 | } 74 | } 75 | } 76 | 77 | /deep/ nb-sidebar-header { 78 | padding-bottom: 0.5rem; 79 | text-align: center; 80 | } 81 | 82 | background: transparent; 83 | 84 | .main-btn { 85 | padding: 0.75rem 2.5rem; 86 | margin-top: -2rem; 87 | font-weight: bold; 88 | transition: padding 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.48); 89 | 90 | @include nb-for-theme(corporate) { 91 | border-radius: nb-theme(radius); 92 | } 93 | 94 | i { 95 | font-size: 2rem; 96 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); 97 | } 98 | span { 99 | @include nb-ltr(padding-left, 0.25rem); 100 | @include nb-rtl(padding-right, 0.25rem); 101 | } 102 | 103 | i, span { 104 | vertical-align: middle; 105 | } 106 | } 107 | 108 | &.compacted { 109 | 110 | /deep/ nb-sidebar-header { 111 | padding-left: 0; 112 | padding-right: 0; 113 | } 114 | 115 | .main-btn { 116 | width: 46px; 117 | height: 44px; 118 | padding: 0.375rem; 119 | border-radius: 5px; 120 | transition: none; 121 | 122 | span { 123 | display: none; 124 | } 125 | } 126 | } 127 | } 128 | 129 | @include media-breakpoint-down(xs) { 130 | .main-content { 131 | padding: 0.75rem !important; 132 | 133 | } 134 | } 135 | 136 | @include media-breakpoint-down(sm) { 137 | 138 | nb-sidebar.menu-sidebar { 139 | 140 | margin-top: 0; 141 | 142 | /deep/ .main-container { 143 | height: calc(#{nb-theme(sidebar-height)} - #{nb-theme(header-height)}) !important; 144 | @include nb-ltr(border-top-right-radius, 0); 145 | @include nb-rtl(border-top-left-radius, 0); 146 | 147 | .scrollable { 148 | padding-top: 0; 149 | } 150 | } 151 | } 152 | 153 | .main-btn { 154 | display: none; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/layout/layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, ChangeDetectionStrategy, Input } from '@angular/core'; 2 | import { NbThemeService } from '@nebular/theme'; 3 | import { takeWhile } from 'rxjs/operators/takeWhile'; 4 | import { AuthenticationService } from '../../authentication/authentication.service'; 5 | import { NbMenuService, NbSidebarService } from '@nebular/theme'; 6 | 7 | // TODO: move layouts into the framework 8 | @Component({ 9 | selector: 'app-layout', 10 | changeDetection: ChangeDetectionStrategy.Default, 11 | styleUrls: ['./layout.component.scss'], 12 | templateUrl: './layout.component.html' 13 | }) 14 | export class LayoutComponent implements OnDestroy { 15 | 16 | private alive = true; 17 | 18 | currentTheme: string; 19 | 20 | constructor( 21 | protected themeService: NbThemeService, 22 | private authenticationService: AuthenticationService, 23 | private menuService: NbMenuService, 24 | ) { 25 | this.themeService.getJsTheme() 26 | .pipe(takeWhile(() => this.alive)) 27 | .subscribe(theme => { 28 | this.currentTheme = theme.name; 29 | }); 30 | 31 | this.menuService.onSubmenuToggle().subscribe(obs => { 32 | if (obs.item.expanded) { 33 | obs.item.subMenuHeight = (obs.item.children.length * 40); 34 | } 35 | }); 36 | } 37 | 38 | ngOnDestroy() { 39 | this.alive = false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/search-input/search-input.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/search-input/search-input.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | align-items: center; 4 | 5 | i.control-icon { 6 | &::before { 7 | font-size: 2.3rem; 8 | } 9 | 10 | &:hover { 11 | cursor: pointer; 12 | } 13 | } 14 | 15 | input { 16 | border: none; 17 | outline: none; 18 | margin-left: 1rem; 19 | width: 15rem; 20 | transition: width 0.2s ease; 21 | 22 | &.hidden { 23 | width: 0; 24 | margin: 0; 25 | } 26 | } 27 | 28 | /deep/ search-input { 29 | input { 30 | background: transparent; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /angular/src/app/core/layout/search-input/search-input.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-search-input', 5 | styleUrls: ['./search-input.component.scss'], 6 | templateUrl: './search-input.component.html' , 7 | }) 8 | export class SearchInputComponent { 9 | @ViewChild('input') input: ElementRef; 10 | 11 | @Output() search: EventEmitter = new EventEmitter(); 12 | 13 | isInputShown = false; 14 | 15 | showInput() { 16 | this.isInputShown = true; 17 | this.input.nativeElement.focus(); 18 | } 19 | 20 | hideInput() { 21 | this.isInputShown = false; 22 | } 23 | 24 | onInput(val: string) { 25 | this.search.emit(val); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /angular/src/app/core/mocks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/core/mocks/.gitkeep -------------------------------------------------------------------------------- /angular/src/app/core/router.animations.ts: -------------------------------------------------------------------------------- 1 | import { trigger, state, animate, style, transition } from '@angular/animations'; 2 | 3 | // Post by Gerard Sans: https://medium.com/google-developer-experts/angular-2-animate-router-transitions-6de179e00204#.7h2femijg 4 | 5 | // Add into index.html for polyfill 6 | // Add the following to any component to animate the view 7 | // import { routerTransition } from './router.animations'; 8 | 9 | // @Component({ 10 | // selector: 'home', 11 | // template: `

Home

`, 12 | // animations: [routerTransition()], 13 | // host: {'[@routerTransition]': ''} 14 | // }) 15 | 16 | export function routerTransition() { 17 | return slideToLeft(); 18 | } 19 | 20 | function slideToRight() { 21 | return trigger('routerTransition', [ 22 | state('void', style({position: 'fixed', width: '40%'}) ), 23 | state('*', style({position: 'fixed', width: '0%'}) ), 24 | transition(':enter', [ 25 | style({transform: 'translateX(-40%)'}), 26 | animate('0.5s ease-in-out', style({transform: 'translateX(0%)'})) 27 | ]), 28 | transition(':leave', [ 29 | style({transform: 'translateX(0%)'}), 30 | animate('0.5s ease-in-out', style({transform: 'translateX(40%)'})) 31 | ]) 32 | ]); 33 | } 34 | 35 | function slideToLeft() { 36 | return trigger('routerTransition', [ 37 | state('void', style({position: 'fixed', width: '40%'}) ), 38 | state('*', style({position: 'fixed', width: '0%'}) ), 39 | transition(':enter', [ 40 | style({transform: 'translateX(40%)'}), 41 | animate('0.5s ease-in-out', style({transform: 'translateX(0%)'})) 42 | ]), 43 | transition(':leave', [ 44 | style({transform: 'translateX(0%)'}), 45 | animate('0.5s ease-in-out', style({transform: 'translateX(-40%)'})) 46 | ]) 47 | ]); 48 | } 49 | 50 | function slideToBottom() { 51 | return trigger('routerTransition', [ 52 | state('void', style({position: 'fixed', width: '100%', height: '100%'}) ), 53 | state('*', style({position: 'fixed', width: '100%', height: '100%'}) ), 54 | transition(':enter', [ 55 | style({transform: 'translateY(-100%)'}), 56 | animate('0.5s ease-in-out', style({transform: 'translateY(0%)'})) 57 | ]), 58 | transition(':leave', [ 59 | style({transform: 'translateY(0%)'}), 60 | animate('0.5s ease-in-out', style({transform: 'translateY(100%)'})) 61 | ]) 62 | ]); 63 | } 64 | 65 | function slideToTop() { 66 | return trigger('routerTransition', [ 67 | state('void', style({position: 'fixed', width: '100%', height: '100%'}) ), 68 | state('*', style({position: 'fixed', width: '100%', height: '100%'}) ), 69 | transition(':enter', [ 70 | style({transform: 'translateY(100%)'}), 71 | animate('0.5s ease-in-out', style({transform: 'translateY(0%)'})) 72 | ]), 73 | transition(':leave', [ 74 | style({transform: 'translateY(0%)'}), 75 | animate('0.5s ease-in-out', style({transform: 'translateY(-100%)'})) 76 | ]) 77 | ]); 78 | } 79 | -------------------------------------------------------------------------------- /angular/src/app/core/services/analytics.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { NavigationEnd, Router } from '@angular/router'; 3 | import { Location } from '@angular/common'; 4 | import { filter } from 'rxjs/operators'; 5 | 6 | declare const ga: any; 7 | 8 | @Injectable() 9 | export class AnalyticsService { 10 | private enabled: boolean; 11 | 12 | constructor(private location: Location, private router: Router) { 13 | this.enabled = false; 14 | } 15 | 16 | trackPageViews() { 17 | if (this.enabled) { 18 | this.router.events.pipe( 19 | filter((event) => event instanceof NavigationEnd), 20 | ) 21 | .subscribe(() => { 22 | ga('send', {hitType: 'pageview', page: this.location.path()}); 23 | }); 24 | } 25 | } 26 | 27 | trackEvent(eventName: string) { 28 | if (this.enabled) { 29 | ga('send', 'event', eventName); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /angular/src/app/core/services/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class LoggerService { 7 | 8 | constructor() { } 9 | 10 | log(msg: string) { 11 | console.log(msg); 12 | } 13 | 14 | logError(msg: string) { 15 | console.error(msg); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /angular/src/app/core/services/static.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs/Observable'; 4 | 5 | import { environment } from '../../../environments/environment'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class StaticService { 11 | 12 | constructor(private http: HttpClient) { 13 | } 14 | 15 | countries(): Observable { 16 | return this.http.get(environment.countires); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /angular/src/app/core/services/title.service.ts: -------------------------------------------------------------------------------- 1 | 2 | // Fature Referance Links: 3 | // https://toddmotto.com/dynamic-page-titles-angular-2-router-events 4 | // https://stackoverflow.com/questions/34597835/how-to-get-current-route 5 | // 6 | 7 | import { merge } from 'rxjs'; 8 | import { Injectable } from '@angular/core'; 9 | import { Title } from '@angular/platform-browser'; 10 | import { Router, NavigationEnd, ActivatedRoute } from '@angular/router'; 11 | import { filter, map, mergeMap } from 'rxjs/operators'; 12 | import { appSettings } from '../../configs/app-settings.config'; 13 | 14 | 15 | const APP_TITLE = appSettings.appTitle; 16 | const SEPARATOR = ' | '; 17 | 18 | @Injectable() 19 | export class TitleService { 20 | 21 | static ucFirst(string) { 22 | if ( !string ) { 23 | return string; 24 | } 25 | return string.charAt(0).toUpperCase() + string.slice(1); 26 | } 27 | 28 | constructor( 29 | private router: Router, 30 | private activatedRoute: ActivatedRoute, 31 | private titleService: Title, 32 | ) { } 33 | 34 | init() { 35 | const onNavigationEnd = this.router.events.pipe(filter(event => event instanceof NavigationEnd)); 36 | // Change page title on navigation or language change, based on route data 37 | merge(onNavigationEnd) 38 | .pipe( 39 | map(() => { 40 | let route = this.activatedRoute; 41 | while (route.firstChild) { 42 | route = route.firstChild; 43 | } 44 | return route; 45 | }), 46 | filter(route => route.outlet === 'primary'), 47 | mergeMap(route => route.data) 48 | ) 49 | .subscribe(event => { 50 | const title = event['title']; 51 | if (title) { 52 | this.titleService.setTitle(`${title} ${SEPARATOR} ${APP_TITLE} `); 53 | } else { 54 | // If not, we do a little magic on the url to create an approximation 55 | return this.router.url.split('/').reduce((acc, frag) => { 56 | if (acc && frag) { acc += SEPARATOR; } 57 | return this.router.url.split('/').reduce(( acc, frag ) => { 58 | if ( acc && frag ) { acc += SEPARATOR; } 59 | return acc + TitleService.ucFirst(frag); 60 | }); 61 | }); 62 | } 63 | }); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /angular/src/app/core/strategies/preload-modules.strategy.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Injectable } from '@angular/core'; 3 | import { PreloadingStrategy, Route } from '@angular/router'; 4 | import { Observable, of } from 'rxjs'; 5 | import { LoggerService } from '../services/logger.service'; 6 | 7 | @Injectable() 8 | export class PreloadModulesStrategy implements PreloadingStrategy { 9 | 10 | constructor(private logger: LoggerService) {} 11 | 12 | preload(route: Route, load: () => Observable): Observable { 13 | if (route.data && route.data['preload']) { 14 | this.logger.log('Preloaded: ' + route.path); 15 | return load(); 16 | } else { 17 | return of(null); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /angular/src/app/core/template-core.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { of as observableOf } from 'rxjs'; 4 | 5 | import { throwIfAlreadyLoaded } from './ensureModuleLoadedOnceGuard'; 6 | import { AnalyticsService } from './services/analytics.service'; 7 | 8 | const socialLinks = [ 9 | { 10 | url: '', 11 | target: '_blank', 12 | icon: 'socicon-github', 13 | }, 14 | { 15 | url: '', 16 | target: '_blank', 17 | icon: 'socicon-facebook', 18 | }, 19 | { 20 | url: '', 21 | target: '_blank', 22 | icon: 'socicon-twitter', 23 | }, 24 | ]; 25 | 26 | 27 | export const NB_CORE_PROVIDERS = [ 28 | AnalyticsService, 29 | ]; 30 | 31 | @NgModule({ 32 | imports: [ 33 | CommonModule, 34 | ], 35 | exports: [ 36 | ], 37 | declarations: [], 38 | }) 39 | export class TemplateCoreModule { 40 | constructor(@Optional() @SkipSelf() parentModule: TemplateCoreModule) { 41 | throwIfAlreadyLoaded(parentModule, 'TemplateCoreModule'); 42 | } 43 | 44 | static forRoot(): ModuleWithProviders { 45 | return { 46 | ngModule: TemplateCoreModule, 47 | providers: [ 48 | ...NB_CORE_PROVIDERS, 49 | ], 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /angular/src/app/core/theme.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 5 | import { TextMaskModule } from 'angular2-text-mask'; 6 | import { FileUploadModule } from 'ng2-file-upload'; 7 | 8 | import { 9 | NbActionsModule, 10 | NbCardModule, 11 | NbLayoutModule, 12 | NbMenuModule, 13 | NbRouteTabsetModule, 14 | NbSearchModule, 15 | NbSidebarModule, 16 | NbTabsetModule, 17 | NbThemeModule, 18 | NbUserModule, 19 | NbCheckboxModule, 20 | NbPopoverModule, 21 | NbContextMenuModule, 22 | } from '@nebular/theme'; 23 | 24 | import { HeaderComponent } from './layout/header/header.component'; 25 | import { FooterComponent } from './layout/footer/footer.component'; 26 | import { LayoutComponent } from './layout/layout/layout.component'; 27 | import { SearchInputComponent } from './layout/search-input/search-input.component'; 28 | 29 | import { CapitalizePipe, PluralPipe, RoundPipe, TimingPipe } from '../shared/pipes'; 30 | 31 | import { DEFAULT_THEME } from '../../assets/scss/theme/theme.default'; 32 | import { COSMIC_THEME } from '../../assets/scss/theme/theme.cosmic'; 33 | import { CORPORATE_THEME } from '../../assets/scss/theme/theme.corporate'; 34 | 35 | const BASE_MODULES = [CommonModule, FormsModule, ReactiveFormsModule]; 36 | 37 | const NB_MODULES = [ 38 | NbCardModule, 39 | NbLayoutModule, 40 | NbTabsetModule, 41 | NbRouteTabsetModule, 42 | NbMenuModule, 43 | NbUserModule, 44 | NbActionsModule, 45 | NbSearchModule, 46 | NbSidebarModule, 47 | NbCheckboxModule, 48 | NbPopoverModule, 49 | NbContextMenuModule, 50 | NgbModule, 51 | TextMaskModule, 52 | FileUploadModule 53 | ]; 54 | 55 | const COMPONENTS = [ 56 | HeaderComponent, 57 | FooterComponent, 58 | SearchInputComponent, 59 | LayoutComponent 60 | ]; 61 | 62 | const PIPES = [ 63 | CapitalizePipe, 64 | PluralPipe, 65 | RoundPipe, 66 | TimingPipe, 67 | ]; 68 | 69 | const NB_THEME_PROVIDERS = [ 70 | ...NbThemeModule.forRoot( 71 | { 72 | name: 'default', 73 | }, 74 | [ DEFAULT_THEME, COSMIC_THEME, CORPORATE_THEME ], 75 | ).providers, 76 | ...NbSidebarModule.forRoot().providers, 77 | ...NbMenuModule.forRoot().providers, 78 | ]; 79 | 80 | @NgModule({ 81 | imports: [...BASE_MODULES, ...NB_MODULES], 82 | exports: [...BASE_MODULES, ...NB_MODULES, ...COMPONENTS, ...PIPES], 83 | declarations: [...COMPONENTS, ...PIPES] 84 | }) 85 | export class ThemeModule { 86 | static forRoot(): ModuleWithProviders { 87 | return { 88 | ngModule: ThemeModule, 89 | providers: [...NB_THEME_PROVIDERS], 90 | }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /angular/src/app/modules/admin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/modules/admin/.gitkeep -------------------------------------------------------------------------------- /angular/src/app/modules/auth/auth-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { LoginComponent } from './login/login.component'; 5 | import { ForgotPasswordComponent } from './forgot-password/forgot-password.component'; 6 | import { NotFoundComponent } from './not-found/not-found.component'; 7 | import { ResetPasswordComponent } from './reset-password/reset-password.component'; 8 | import { ForbiddenComponent } from './forbidden/forbidden.component'; 9 | 10 | const routes: Routes = [ 11 | { 12 | path: 'login', 13 | component: LoginComponent, 14 | data: { title: 'Log In' } 15 | }, 16 | { 17 | path: 'forgot-password', 18 | component: ForgotPasswordComponent, 19 | data: { title: 'Forgot Password' } 20 | }, 21 | { 22 | path: 'reset-password/:resetToken', 23 | component: ResetPasswordComponent, 24 | data: { title: 'Reset password' } 25 | }, 26 | { 27 | path: '404', 28 | component: NotFoundComponent, 29 | data: { title: '404' } 30 | }, 31 | { 32 | path: '403', 33 | component: ForbiddenComponent, 34 | data: { title: '404' } 35 | } 36 | 37 | ]; 38 | 39 | @NgModule({ 40 | imports: [RouterModule.forChild(routes)], 41 | exports: [RouterModule], 42 | }) 43 | export class AuthRoutingModule { 44 | static components = [ 45 | LoginComponent, 46 | ForgotPasswordComponent, 47 | NotFoundComponent, 48 | ResetPasswordComponent, 49 | ForbiddenComponent 50 | ]; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/auth.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { AuthModule } from './auth.module'; 2 | 3 | describe('AuthModule', () => { 4 | let authModule: AuthModule; 5 | 6 | beforeEach(() => { 7 | authModule = new AuthModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(authModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | 5 | import { AuthRoutingModule } from './auth-routing.module'; 6 | import { ThemeModule } from '../../core/theme.module'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | AuthRoutingModule, 12 | ThemeModule, 13 | ], 14 | declarations: [AuthRoutingModule.components ], 15 | providers: [] 16 | }) 17 | export class AuthModule { } 18 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forbidden/forbidden.component.css: -------------------------------------------------------------------------------- 1 | .flex-centered { 2 | margin: auto; 3 | } 4 | nb-card-body { 5 | display: flex; 6 | } 7 | 8 | .title { 9 | text-align: center; 10 | } 11 | 12 | .sub-title { 13 | text-align: center; 14 | display: block; 15 | margin-bottom: 3rem; 16 | } 17 | 18 | .btn { 19 | margin-bottom: 2rem; 20 | } 21 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forbidden/forbidden.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 | 7 |
8 | 9 | {{ appSettings.appTitle }} 10 | 11 |
12 |
13 | 14 |
15 |

403 Forbidden

16 | 17 | You are not authorized to access the requested page. 18 | 19 | 22 |
23 |
24 |
25 |
26 |
27 |
28 |
-------------------------------------------------------------------------------- /angular/src/app/modules/auth/forbidden/forbidden.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ForbiddenComponent } from './forbidden.component'; 4 | 5 | describe('ForbiddenComponent', () => { 6 | let component: ForbiddenComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ForbiddenComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ForbiddenComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forbidden/forbidden.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthenticationService } from '../../../core/authentication/authentication.service'; 3 | import { Router } from '@angular/router'; 4 | import { appSettings } from '../../../configs/app-settings.config'; 5 | 6 | @Component({ 7 | selector: 'app-forbidden', 8 | templateUrl: './forbidden.component.html', 9 | styleUrls: ['./forbidden.component.css'] 10 | }) 11 | export class ForbiddenComponent implements OnInit { 12 | 13 | appSettings = appSettings; 14 | 15 | constructor( 16 | private authenticationService: AuthenticationService, 17 | private router: Router, 18 | ) { } 19 | 20 | ngOnInit() { 21 | } 22 | 23 | goToHome() { 24 | const userType = this.authenticationService.getUserType(); 25 | if ( !userType ) { 26 | this.router.navigate(['/auth/login']); 27 | } else if (userType === 'client') { 28 | this.router.navigate(['/client/dashboard']); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forgot-password/forgot-password.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/modules/auth/forgot-password/forgot-password.component.css -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forgot-password/forgot-password.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 | 11 | {{ appSettings.appTitle }} 12 | 13 |
14 |
15 | 16 |

Forgot Password

17 | 18 | Enter your email address and we’ll send a link to reset your password 19 | 20 |
21 | 22 |
24 |
25 | 26 | 27 |
28 | 29 | Please select a Email. 30 | 31 | 32 | Please enter a Email no more than 250 characters. 33 | 34 | 35 | Please enter a valid Email. 36 | 37 |
38 |
39 | 42 |
43 | 44 | 47 | Log In 48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
-------------------------------------------------------------------------------- /angular/src/app/modules/auth/forgot-password/forgot-password.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ForgotPasswordComponent } from './forgot-password.component'; 4 | 5 | describe('ForgotPasswordComponent', () => { 6 | let component: ForgotPasswordComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ForgotPasswordComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ForgotPasswordComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/forgot-password/forgot-password.component.ts: -------------------------------------------------------------------------------- 1 | import { UserService } from '../../../core/http/user/user.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { FormBuilder, Validators, FormArray, FormGroup } from '@angular/forms'; 4 | import { appSettings } from '../../../configs/app-settings.config'; 5 | import { appToaster } from '../../../configs/app-toaster.config'; 6 | import { ToasterService } from 'angular2-toaster'; 7 | import { Router } from '@angular/router'; 8 | 9 | @Component({ 10 | selector: 'app-forgot-password', 11 | templateUrl: './forgot-password.component.html', 12 | styleUrls: ['./forgot-password.component.css'] 13 | }) 14 | export class ForgotPasswordComponent implements OnInit { 15 | forgotPasswordForm: FormGroup; 16 | appSettings: any; 17 | isSubmit: boolean; 18 | isSuccess: boolean; 19 | message: string; 20 | constructor( 21 | private fb: FormBuilder, 22 | private userService: UserService, 23 | private toasterService: ToasterService, 24 | private router: Router 25 | ) { } 26 | 27 | ngOnInit() { 28 | this.appSettings = appSettings; 29 | this.forgotPasswordForm = this.fb.group({ 30 | email: ['', [Validators.required, Validators.max(250), Validators.email]], 31 | }); 32 | 33 | } 34 |  // convenience getter for easy access to form fields 35 |     get formFields() { return this.forgotPasswordForm.controls; } 36 | 37 | onSubmit(): boolean { 38 | this.isSubmit = true; 39 | if (this.forgotPasswordForm.invalid) { 40 | return false; 41 | } else { 42 | this.userService.forgotPassword(this.forgotPasswordForm.value) 43 | .subscribe((res) => { 44 | if (res.status === 'success') { 45 | this.isSuccess = true; 46 | this.message = res.message; 47 | // this.toasterService.pop('success', appToaster.successHead, res.message); 48 | } else { 49 | this.toasterService.pop('error', appToaster.errorHead, res.message); 50 | this.router.navigate(['/client/forgot-password']); 51 | } 52 | }); 53 | 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/login/login.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/modules/auth/login/login.component.css -------------------------------------------------------------------------------- /angular/src/app/modules/auth/login/login.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 | 11 | {{ appSettings.appTitle }} 12 | 13 |
14 |
15 | 16 |

{{ appSettings.appTitle }} Login

17 |
18 | 19 |
20 |
21 | 22 | 23 |
24 | 25 | Please select a Email 26 | 27 | 28 | Please enter a Email no more than 250 characters. 29 | 30 | 31 | Please enter a valid Email . 32 | 33 |
34 |
35 |
36 | 46 |
47 | 48 | 49 | 53 | 54 |
55 | 56 |
57 | 58 | Please enter a password 59 | 60 | 61 | Please enter a password no more than 16 characters. 62 | 63 | 64 | Please enter a password at least 6 characters. 65 | 66 |
67 |
68 |
69 | Remember me 70 |
71 |
72 | 75 |
76 | 77 | Client Sign Up 78 | 79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
-------------------------------------------------------------------------------- /angular/src/app/modules/auth/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginComponent } from './login.component'; 4 | 5 | describe('LoginComponent', () => { 6 | let component: LoginComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LoginComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoginComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; 3 | import { AuthenticationService } from '../../../core/authentication/authentication.service'; 4 | import { Router, ActivatedRoute, ParamMap } from '@angular/router'; 5 | import { HttpClient } from '@angular/common/http'; 6 | import { ToasterService } from 'angular2-toaster'; 7 | import 'style-loader!angular2-toaster/toaster.css'; 8 | import { appSettings } from '../../../configs/app-settings.config'; 9 | import { appToaster } from '../../../configs/app-toaster.config'; 10 | 11 | @Component({ 12 | selector: 'app-login', 13 | templateUrl: './login.component.html', 14 | styleUrls: ['./login.component.css'] 15 | }) 16 | export class LoginComponent implements OnInit { 17 | 18 | loginForm: FormGroup; 19 | isSubmit: boolean ; 20 | EMAIL_REGEX = "[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*"; 21 | hide: boolean; 22 | returnUrl: string; 23 | appSettings: any; 24 | constructor( private router: Router, 25 | private http: HttpClient, 26 | private route: ActivatedRoute, 27 | private toasterService: ToasterService, 28 | private authenticationService: AuthenticationService) { } 29 | 30 | ngOnInit() { 31 | this.appSettings = appSettings; 32 | this.authenticationService.logout(); 33 | this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || null; 34 | this.hide = true; 35 | this.loginForm = new FormGroup({ 36 | 'email': new FormControl('', [ 37 | Validators.required, 38 | Validators.maxLength(250), 39 | Validators.pattern(this.EMAIL_REGEX) 40 | ]), 41 | 'password': new FormControl('', [ 42 | Validators.required, 43 | Validators.minLength(6), 44 | Validators.maxLength(16), 45 | ]), 46 | 'remember': new FormControl(), 47 | }); 48 | } 49 | get email() { return this.loginForm.get('email'); } 50 | get password() { return this.loginForm.get('password'); } 51 | get remember() { return this.loginForm.get('remember'); } 52 | 53 | onSubmit(): boolean { 54 | this.isSubmit = true; 55 | if (this.loginForm.invalid) { 56 | return false; 57 | } else { 58 | this.authenticationService.login(this.loginForm.value) 59 | .subscribe((res) => { 60 | if (res.status === 'success') { 61 | this.toasterService.pop('success', appToaster.successHead, appToaster.loginSucess); 62 | 63 | if ( this.returnUrl === null && res.role === 'client' ) { 64 | this.returnUrl = '/client/dashboard'; 65 | } 66 | 67 | this.router.navigate([this.returnUrl]); 68 | return true; 69 | } 70 | } 71 | ); 72 | 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/not-found/not-found.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 | 7 |
8 | 9 | {{ appSettings.appTitle }} 10 | 11 |
12 |
13 | 14 |
15 |

404 Page Not Found

16 | The page you were looking for doesn't exist 17 | 20 |
OR
21 | 24 |
25 |
26 |
27 |
28 |
29 |
30 |
-------------------------------------------------------------------------------- /angular/src/app/modules/auth/not-found/not-found.component.scss: -------------------------------------------------------------------------------- 1 | .flex-centered { 2 | margin: auto; 3 | } 4 | nb-card-body { 5 | display: flex; 6 | } 7 | 8 | .title { 9 | text-align: center; 10 | } 11 | 12 | .sub-title { 13 | text-align: center; 14 | display: block; 15 | margin-bottom: 3rem; 16 | } 17 | 18 | .btn { 19 | margin-bottom: 2rem; 20 | } 21 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/not-found/not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AuthenticationService } from '../../../core/authentication/authentication.service'; 3 | import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router'; 4 | import { appSettings } from '../../../configs/app-settings.config'; 5 | 6 | @Component({ 7 | selector: 'app-not-found', 8 | styleUrls: ['./not-found.component.scss'], 9 | templateUrl: './not-found.component.html', 10 | }) 11 | export class NotFoundComponent { 12 | appSettings = appSettings; 13 | constructor( 14 | private authenticationService: AuthenticationService, 15 | private router: Router, 16 | ) { 17 | } 18 | 19 | goToHome() { 20 | const userType = this.authenticationService.getUserType(); 21 | if ( !userType ) { 22 | this.router.navigate(['/auth/login']); 23 | } else if (userType === 'client') { 24 | this.router.navigate(['/client/dashboard']); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/reset-password/reset-password.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/modules/auth/reset-password/reset-password.component.css -------------------------------------------------------------------------------- /angular/src/app/modules/auth/reset-password/reset-password.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 | 11 | {{ appSettings.appTitle }} 12 | 13 |
14 |
15 | 16 |

Reset password

17 | Please enter a new password 18 |
19 | 20 |
21 |
22 | 23 | 24 |
25 | 26 | Please enter a Password. 27 | 28 | 29 | Please enter a password no more than 16 characters. 30 | 31 | 32 | Please enter a password at least 6 characters. 33 | 34 |
35 |
36 |
37 | 38 | 39 |
40 | 41 | Please enter a Confirm Password. 42 | 43 | 44 | Please enter a password no more than 16 characters. 45 | 46 | 47 | Please enter a password at least 6 characters. 48 | 49 |
50 |
51 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
-------------------------------------------------------------------------------- /angular/src/app/modules/auth/reset-password/reset-password.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ResetPasswordComponent } from './reset-password.component'; 4 | 5 | describe('ResetPasswordComponent', () => { 6 | let component: ResetPasswordComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ResetPasswordComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ResetPasswordComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/modules/auth/reset-password/reset-password.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router, ParamMap, ActivatedRoute } from '@angular/router'; 3 | import { FormBuilder, Validators, FormArray, FormGroup } from '@angular/forms'; 4 | import { appSettings } from '../../../configs/app-settings.config'; 5 | import { appToaster } from '../../../configs/app-toaster.config'; 6 | import { ToasterService } from 'angular2-toaster'; 7 | import { UserService } from '../../../core/http/user/user.service'; 8 | 9 | 10 | @Component({ 11 | selector: 'app-reset-password', 12 | templateUrl: './reset-password.component.html', 13 | styleUrls: ['./reset-password.component.css'] 14 | }) 15 | export class ResetPasswordComponent implements OnInit { 16 | resetPasswordForm: FormGroup; 17 | appSettings = appSettings; 18 | isSubmit: boolean ; 19 | 20 | constructor( 21 | private router: Router, 22 | private route: ActivatedRoute, 23 | private fb: FormBuilder, 24 | private userService: UserService, 25 | private toasterService: ToasterService 26 | ) { } 27 | 28 | ngOnInit() { 29 | this.resetPasswordForm = this.fb.group({ 30 | password: ['', 31 | [ 32 | Validators.required, 33 | Validators.minLength(6), 34 | Validators.maxLength(16), 35 | ]], 36 | confirmPassword: ['', 37 | [ 38 | Validators.required, 39 | Validators.minLength(6), 40 | Validators.maxLength(16), 41 | ]], 42 | }); 43 | 44 | } 45 | 46 | onSubmit(): boolean { 47 | this.isSubmit = true; 48 | if (this.resetPasswordForm.invalid) { 49 | return false; 50 | } else { 51 | 52 | let postData = Object.assign({}, this.resetPasswordForm.value); 53 | postData['resetToken'] = this.route.snapshot.paramMap.get('resetToken'); 54 | 55 | this.userService.resetPassword(postData) 56 | .subscribe((res) => { 57 | if (res.status === 'success') { 58 | this.toasterService.pop('success', appToaster.successHead, res.message); 59 | this.router.navigate(['/auth/login']); 60 | } else { 61 | this.toasterService.pop('error', appToaster.errorHead, res.message); 62 | this.router.navigate(['/auth/login']); 63 | } 64 | }); 65 | 66 | } 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client-pages-menu.ts: -------------------------------------------------------------------------------- 1 | import { NbMenuItem } from '@nebular/theme'; 2 | 3 | export const MENU_ITEMS: NbMenuItem[] = [ 4 | { 5 | title: 'Dashboard', 6 | icon: 'nb-home', 7 | link: '/client/dashboard', 8 | home: true, 9 | }, 10 | { 11 | title: 'Account Settings', 12 | icon: 'nb-gear', 13 | children: [ 14 | { 15 | title: 'Profile', 16 | link: '', 17 | }, 18 | { 19 | title: 'Change Password', 20 | link: '', 21 | }, 22 | { 23 | title: 'Settings', 24 | link: '', 25 | }, 26 | { 27 | title: 'Log out', 28 | link: '', 29 | }, 30 | ] 31 | }, 32 | { 33 | title: 'Applications', 34 | icon: 'nb-lightbulb', 35 | children: [ 36 | { 37 | title: 'Application List', 38 | link: '', 39 | }, 40 | { 41 | title: 'New Application', 42 | link: '', 43 | } 44 | ] 45 | }, 46 | { 47 | title: 'Informations', 48 | icon: 'nb-notifications', 49 | children: [ 50 | { 51 | title: 'Communications', 52 | link: '', 53 | }, 54 | { 55 | title: 'Notifications', 56 | link: '', 57 | } 58 | ] 59 | } 60 | ]; 61 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { DashboardComponent } from './dashboard/dashboard.component'; 5 | import { ClientComponent } from './client.component'; 6 | import { ClientAuthGuard } from '../../core/guards/client-auth.guard'; 7 | import { SignupComponent } from './signup/signup.component'; 8 | 9 | const routes: Routes = [ 10 | { 11 | path: '', 12 | component: ClientComponent, 13 | canActivate: [ClientAuthGuard], 14 | canActivateChild: [ClientAuthGuard], 15 | children: [ 16 | { 17 | path: 'dashboard', 18 | component: DashboardComponent, 19 | data: { title: 'Client Dashboard' } 20 | }, 21 | ], 22 | }, 23 | { 24 | path: 'signup', 25 | component: SignupComponent, 26 | data: { title: 'Client Signup' } 27 | } 28 | 29 | 30 | ]; 31 | 32 | @NgModule({ 33 | imports: [RouterModule.forChild(routes)], 34 | exports: [RouterModule], 35 | }) 36 | export class ClientRoutingModule { 37 | static components = [ 38 | DashboardComponent, 39 | ClientComponent, 40 | SignupComponent 41 | ]; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { MENU_ITEMS } from './client-pages-menu'; 4 | 5 | @Component({ 6 | selector: 'app-client', 7 | templateUrl: './client.component.html', 8 | }) 9 | export class ClientComponent { 10 | 11 | menu = MENU_ITEMS; 12 | } 13 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { ClientModule } from './client.module'; 2 | 3 | describe('ClientModule', () => { 4 | let clientModule: ClientModule; 5 | 6 | beforeEach(() => { 7 | clientModule = new ClientModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(clientModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/client.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ClientRoutingModule } from './client-routing.module'; 5 | import { ThemeModule } from '../../core/theme.module'; 6 | import { SharedModule } from '../../shared/module/shared.module'; 7 | import { ClientAuthGuard } from '../../core/guards/client-auth.guard'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | ClientRoutingModule, 13 | ThemeModule, 14 | SharedModule 15 | ], 16 | declarations: [ClientRoutingModule.components], 17 | providers: [ClientAuthGuard] 18 | }) 19 | export class ClientModule { } 20 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Hello welcome to angular6 realworld-highly scalable application . 4 | 5 |
6 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import { ToasterService } from 'angular2-toaster'; 3 | import 'style-loader!angular2-toaster/toaster.css'; 4 | 5 | @Component({ 6 | selector: 'app-dashboard', 7 | templateUrl: './dashboard.component.html', 8 | }) 9 | export class DashboardComponent implements OnInit { 10 | 11 | 12 | constructor( private toasterService: ToasterService) { 13 | 14 | } 15 | 16 | ngOnInit() { 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/signup/signup.component.css: -------------------------------------------------------------------------------- 1 | .hidden-file { 2 | visibility: hidden; 3 | position: absolute; 4 | overflow: hidden; 5 | width: 0px; 6 | height:0px; 7 | border:none; 8 | margin:0; 9 | padding:0 10 | } -------------------------------------------------------------------------------- /angular/src/app/modules/client/signup/signup.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 | 11 | {{ appSettings.appTitle }} 12 | 13 |
14 |
15 | 16 |

Client Sign up

17 | 18 | Create Your Account 19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | 27 | User Info 28 | 29 |
30 | 31 | 36 | 41 |
42 |
43 | 44 | 49 | 54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 | 62 | Please enter a First Name. 63 | 64 | 65 | Please enter a First Name no more than 100 characters. 66 | 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 |
75 | 76 | Please enter a Last Name. 77 | 78 | 79 | Please enter a Last Name no more than 100 characters. 80 | 81 |
82 |
83 |
84 |
85 |
86 | 89 |
90 | 92 | 93 | 96 | 97 |
98 |
99 | 100 | Please enter a Date of Birth. 101 | 102 | 103 | Date of Birth is invalid. 104 | 105 |
106 |
107 |
108 | 109 |
110 | 111 | 114 | 115 | 116 |
117 |
118 | 119 | Please enter a Mobile Number. 120 | 121 | 122 | Mobile Number is invalid. 123 | 124 |
125 |
126 |
127 | 128 |
129 | 130 | 133 | 134 | 135 |
136 |
137 | 138 | Please enter a Email. 139 | 140 | 141 | Please enter a Email no more than 250 characters. 142 | 143 | 144 | Please enter a valid Email. 145 | 146 |
147 |
148 |
149 | 150 |
151 | 152 | 153 | 157 | 158 |
159 |
160 | 161 | Please enter a Password. 162 | 163 | 164 | Please enter a password no more than 16 characters. 165 | 166 | 167 | Please enter a password at least 6 characters. 168 | 169 |
170 |
171 |
172 | 173 | 174 |
175 | 176 | Please enter a Address. 177 | 178 | 179 | Please enter a Address no more than 250 characters. 180 | 181 |
182 |
183 |
184 | 185 | 186 |
187 | 188 | Please enter a City. 189 | 190 | 191 | Please enter a City no more than 250 characters. 192 | 193 |
194 |
195 | 196 |
197 | 198 | 199 |
200 | 201 | Please enter a State. 202 | 203 | 204 | Please enter a State no more than 250 characters. 205 | 206 |
207 |
208 | 209 |
210 | 211 | 215 |
216 | 217 | Please enter a Country. 218 | 219 | 220 | Please enter a Country no more than 250 characters. 221 | 222 |
223 |
224 | 225 |
226 | 227 | 228 |
229 | 230 | Please enter a Zip. 231 | 232 | 233 | Zip is invalid. 234 | 235 |
236 |
237 | 238 |
239 | 240 | 241 |
242 | 243 | Please enter a Qualification. 244 | 245 | 246 | Please enter a Qualification no more than 250 characters. 247 | 248 |
249 |
250 | 251 |
252 | 253 | 254 |
255 | 256 | Please enter a Profession. 257 | 258 | 259 | Please enter a Profession no more than 250 characters. 260 | 261 |
262 |
263 | 264 |
265 | 266 | 270 |
271 | 272 | Please select a Nationality. 273 | 274 |
275 |
276 |
277 |
278 |
279 |
280 | 281 | 282 | Application Info 283 | 284 |
285 | 286 | 290 |
291 | 292 | Please select a Application Type. 293 | 294 |
295 |
296 |
297 | 298 | 301 |
302 | 303 | Please enter a Profession no more than 1000 characters. 304 | 305 |
306 |
307 |
308 |
309 | 310 |
311 | 312 | 313 | 314 |
315 | 318 | 321 |
322 |
323 |
324 |
325 |
326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 339 | 350 | 353 | 354 | 355 |
FileFile NameActions
337 | 338 | 340 | 341 |
342 | 343 | Please enter a File Name. 344 | 345 | 346 | Please enter a File Name no more than 100 characters. 347 | 348 |
349 |
351 | 352 |
356 |
357 |
358 |
359 |
360 |
361 | 364 | 365 | Client Login 366 | 367 |
368 | 369 | 372 | Log In 373 | 374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
-------------------------------------------------------------------------------- /angular/src/app/modules/client/signup/signup.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SignupComponent } from './signup.component'; 4 | 5 | describe('SignupComponent', () => { 6 | let component: SignupComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SignupComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SignupComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/modules/client/signup/signup.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, EventEmitter } from '@angular/core'; 2 | import { Router, ParamMap, ActivatedRoute } from '@angular/router'; 3 | import { FormBuilder, Validators, FormArray, FormGroup } from '@angular/forms'; 4 | import { FileUploader, FileLikeObject } from 'ng2-file-upload'; 5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; 6 | 7 | import { appSettings } from '../../../configs/app-settings.config'; 8 | import { appToaster } from '../../../configs/app-toaster.config'; 9 | 10 | import { ToasterService } from 'angular2-toaster'; 11 | import { UserService } from '../../../core/http/user/user.service'; 12 | import { StaticService } from '../../../core/services/static.service'; 13 | 14 | import { environment } from '../../../../environments/environment'; 15 | 16 | import { ConfirmDialogComponent } from '../../../shared/components/confirm-dialog/confirm-dialog.component'; 17 | 18 | 19 | const URL = `${environment.host}${environment.fileupload}`; 20 | 21 | @Component({ 22 | selector: 'app-signup', 23 | templateUrl: './signup.component.html', 24 | styleUrls: ['./signup.component.css'] 25 | }) 26 | export class SignupComponent implements OnInit { 27 | signupForm: FormGroup; 28 | appSettings = appSettings; 29 | isSubmit: boolean; 30 | isSuccess: boolean; 31 | message: string; 32 | public usaPhoneFormat: Array; 33 | public usaZipFormat: Array; 34 | countries: any[]; 35 | hide: boolean; 36 | public uploader: FileUploader = new FileUploader({ 37 | url: URL, 38 | disableMultipart: false, 39 | autoUpload: true, 40 | method: 'post', 41 | itemAlias: 'attachment', 42 | allowedFileType: ['image', 'pdf', 'doc'] 43 | 44 | }); 45 | 46 | constructor( 47 | private router: Router, 48 | private route: ActivatedRoute, 49 | private fb: FormBuilder, 50 | private userService: UserService, 51 | private toasterService: ToasterService, 52 | private staticService: StaticService, 53 | private modalService: NgbModal 54 | ) { } 55 | 56 | ngOnInit() { 57 | this.hide = true; 58 | this.staticService.countries() 59 | .subscribe((data: any) => { 60 | this.countries = data; 61 | }); 62 | this.intilizeForm(); 63 | 64 | this.uploader.onCompleteItem = (item, response, status, header) => { 65 | const responseData = JSON.parse(response); 66 | if (status === 200 && responseData.status === 'success') { 67 | this.addAttachment(responseData.name); 68 | } 69 | }; 70 | } 71 | 72 | 73 | intilizeForm(): void { 74 | this.usaPhoneFormat = ['+', '1', ' ', '(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]; 75 | this.usaZipFormat = [/\d/, /\d/, /\d/, /\d/, /\d/]; 76 | this.signupForm = this.fb.group({ 77 | userInfo: this.fb.group({ 78 | maritalStatus: ['single', [Validators.required]], 79 | gender: ['male', [Validators.required]], 80 | firstName: ['', [Validators.required, Validators.maxLength(100)]], 81 | lastName: ['', [Validators.required, Validators.maxLength(100)]], 82 | dob: ['', [Validators.required]], 83 | password: ['', [Validators.required, Validators.minLength(6), Validators.maxLength(16)]], 84 | mobile: ['', [Validators.required]], 85 | email: ['', [Validators.required, Validators.maxLength(250), Validators.email]], 86 | address: ['', [Validators.required, Validators.maxLength(250)]], 87 | city: ['', [Validators.required, Validators.maxLength(250)]], 88 | state: ['', [Validators.required, Validators.maxLength(250)]], 89 | country: ['', [Validators.required, Validators.maxLength(250)]], 90 | zip: ['', [Validators.required]], 91 | qualification: ['', [Validators.required, Validators.maxLength(250)]], 92 | profession: ['', [Validators.required, Validators.maxLength(250)]], 93 | nationality: ['', [Validators.required, Validators.maxLength(250)]], 94 | }), 95 | applicationInfo: this.fb.group({ 96 | type: ['', [Validators.required]], 97 | notes: ['', [Validators.maxLength(1000)]], 98 | attachments: this.fb.array([ 99 | ]) 100 | 101 | }), 102 | }); 103 | 104 | } 105 | 106 | onSubmit(): boolean { 107 | this.isSubmit = true; 108 | if (this.signupForm.invalid) { 109 | return false; 110 | } else { 111 | this.userService.clientSignup(this.signupForm.value) 112 | .subscribe((res) => { 113 | if (res.status === 'success') { 114 | // this.toasterService.pop('success', appToaster.successHead, res.message); 115 | // this.router.navigate(['/auth/login']); 116 | this.isSuccess = true; 117 | this.message = res.message; 118 | } else { 119 | this.toasterService.pop('error', appToaster.errorHead, res.message); 120 | this.router.navigate(['/client/signup']); 121 | 122 | } 123 | }); 124 | 125 | } 126 | } 127 | 128 | public onFileSelected(event: EventEmitter) { 129 | const file: File = event[0]; 130 | } 131 | 132 | get attachments() { 133 | return this.signupForm.controls.applicationInfo.get('attachments') as FormArray; 134 | } 135 | 136 | addAttachment(filename: string): void { 137 | this.attachments.push( 138 | this.fb.group({ 139 | filename: [filename], 140 | displayName: [null, [Validators.required, Validators.maxLength(100)]], 141 | }) 142 | ); 143 | } 144 | 145 | 146 | confirm(index: number): void { 147 | 148 | const activeModal = this.modalService.open(ConfirmDialogComponent, { 149 | size: 'sm', 150 | backdrop: 'static', 151 | container: 'nb-layout', 152 | }); 153 | 154 | activeModal.componentInstance.modalHeader = 'Confirmation'; 155 | activeModal.componentInstance.modalContent = `Are you sure you want to delete this item?`; 156 | activeModal.result.then((data) => { 157 | if (data) { 158 | this.userService.deleteAttachment(this.attachments.value[index]) 159 | .subscribe((res) => { 160 | if (res.status === 'success') { 161 | this.attachments.removeAt(index); 162 | this.toasterService.pop('success', appToaster.successHead, res.message); 163 | } else { 164 | this.toasterService.pop('error', appToaster.errorHead, res.message); 165 | } 166 | }); 167 | 168 | } 169 | }, (reason) => { 170 | }); 171 | } 172 | 173 | 174 | } 175 | 176 | -------------------------------------------------------------------------------- /angular/src/app/shared/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/shared/components/.gitkeep -------------------------------------------------------------------------------- /angular/src/app/shared/components/confirm-dialog/confirm-dialog.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/shared/components/confirm-dialog/confirm-dialog.component.css -------------------------------------------------------------------------------- /angular/src/app/shared/components/confirm-dialog/confirm-dialog.component.html: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /angular/src/app/shared/components/confirm-dialog/confirm-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConfirmDialogComponent } from './confirm-dialog.component'; 4 | 5 | describe('ConfirmDialogComponent', () => { 6 | let component: ConfirmDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ConfirmDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConfirmDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular/src/app/shared/components/confirm-dialog/confirm-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 3 | 4 | @Component({ 5 | selector: 'app-confirm-dialog', 6 | templateUrl: './confirm-dialog.component.html', 7 | styleUrls: ['./confirm-dialog.component.css'], 8 | 9 | }) 10 | export class ConfirmDialogComponent { 11 | 12 | modalHeader: string; 13 | modalContent = ``; 14 | 15 | constructor(private activeModal: NgbActiveModal) { } 16 | 17 | close(): void { 18 | this.activeModal.close(false); 19 | } 20 | 21 | confirm(): void { 22 | this.activeModal.close(true); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /angular/src/app/shared/directives/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/app/shared/directives/.gitkeep -------------------------------------------------------------------------------- /angular/src/app/shared/models/application.model.ts: -------------------------------------------------------------------------------- 1 | import { Attachments } from './attachments.model'; 2 | 3 | export interface Application { 4 | id ?: number; 5 | type: string; 6 | notes: string; 7 | attachments: Attachments[]; 8 | } 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/attachments.model.ts: -------------------------------------------------------------------------------- 1 | export interface Attachments { 2 | filename: string; 3 | displayName: string; 4 | } 5 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/fortgot-password.ts: -------------------------------------------------------------------------------- 1 | export class FortgotPassword { 2 | email: string; 3 | } 4 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/login.model.ts: -------------------------------------------------------------------------------- 1 | export interface Login { 2 | username: string; 3 | password: string; 4 | remember?: boolean; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/reset-password.ts: -------------------------------------------------------------------------------- 1 | export class ResetPassword { 2 | password: string; 3 | confirmPassword: string; 4 | } 5 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/user.model.ts: -------------------------------------------------------------------------------- 1 | import { Application } from './application.model'; 2 | import { UserInfo } from './userInfo.model'; 3 | 4 | export class User { 5 | public userInfo: UserInfo; 6 | public applicationInfo: Application; 7 | } 8 | -------------------------------------------------------------------------------- /angular/src/app/shared/models/userInfo.model.ts: -------------------------------------------------------------------------------- 1 | export class UserInfo { 2 | public maritalStatus: string; 3 | public gender: string; 4 | public firstName: string; 5 | public lastName: string; 6 | public dob: string; 7 | public password: string; 8 | public mobile: number; 9 | public email: string; 10 | public address: string; 11 | public city: string; 12 | public state ?: string; 13 | public country: string; 14 | public zip: number; 15 | public qualification: string; 16 | public profession: string; 17 | public nationality: string; 18 | } -------------------------------------------------------------------------------- /angular/src/app/shared/module/shared.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { SharedModule } from './shared.module'; 2 | 3 | describe('SharedModule', () => { 4 | let sharedModule: SharedModule; 5 | 6 | beforeEach(() => { 7 | sharedModule = new SharedModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(sharedModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /angular/src/app/shared/module/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ConfirmDialogComponent } from '../components/confirm-dialog/confirm-dialog.component'; 4 | 5 | @NgModule({ 6 | imports: [ 7 | CommonModule 8 | ], 9 | declarations: [ ConfirmDialogComponent ], 10 | entryComponents: [ ConfirmDialogComponent ] 11 | 12 | }) 13 | export class SharedModule { } 14 | -------------------------------------------------------------------------------- /angular/src/app/shared/pipes/capitalize.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'ngxCapitalize' }) 4 | export class CapitalizePipe implements PipeTransform { 5 | 6 | transform(input: string): string { 7 | return input && input.length 8 | ? (input.charAt(0).toUpperCase() + input.slice(1).toLowerCase()) 9 | : input; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /angular/src/app/shared/pipes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './capitalize.pipe'; 2 | export * from './plural.pipe'; 3 | export * from './round.pipe'; 4 | export * from './timing.pipe'; 5 | -------------------------------------------------------------------------------- /angular/src/app/shared/pipes/plural.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'ngxPlural' }) 4 | export class PluralPipe implements PipeTransform { 5 | 6 | transform(input: number, label: string, pluralLabel: string = ''): string { 7 | input = input || 0; 8 | return input === 1 9 | ? `${input} ${label}` 10 | : pluralLabel 11 | ? `${input} ${pluralLabel}` 12 | : `${input} ${label}s`; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /angular/src/app/shared/pipes/round.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'ngxRound' }) 4 | export class RoundPipe implements PipeTransform { 5 | 6 | transform(input: number): number { 7 | return Math.round(input); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /angular/src/app/shared/pipes/timing.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'timing' }) 4 | export class TimingPipe implements PipeTransform { 5 | transform(time: number): string { 6 | if (time) { 7 | const minutes = Math.floor(time / 60); 8 | const seconds = Math.floor(time % 60); 9 | return `${this.initZero(minutes)}${minutes}:${this.initZero(seconds)}${seconds}`; 10 | } 11 | 12 | return '00:00'; 13 | } 14 | 15 | private initZero(time: number): string { 16 | return time < 10 ? '0' : ''; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /angular/src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /angular/src/assets/images/logo/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/assets/images/logo/logo.jpg -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/font-size.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/pace.theme.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Akveo. All Rights Reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | */ 6 | 7 | @mixin ngx-pace-theme() { 8 | 9 | .pace .pace-progress { 10 | background: nb-theme(color-fg-highlight); 11 | } 12 | 13 | .pace .pace-progress-inner { 14 | box-shadow: 0 0 10px nb-theme(color-fg-highlight), 0 0 5px nb-theme(color-fg-highlight); 15 | } 16 | 17 | .pace .pace-activity { 18 | display: none; 19 | // border-top-color: nb-theme(color-fg-highlight); 20 | // border-left-color: nb-theme(color-fg-highlight); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/styles.scss: -------------------------------------------------------------------------------- 1 | // themes - our custom or/and out of the box themes 2 | @import 'themes'; 3 | 4 | // framework component themes (styles tied to theme variables) 5 | @import '~@nebular/theme/styles/globals'; 6 | 7 | @import '~@nebular/bootstrap/styles/globals'; 8 | // ... 9 | 10 | // global app font size 11 | @import './font-size'; 12 | 13 | // loading progress bar theme 14 | @import './pace.theme'; 15 | 16 | // install the framework and custom global styles 17 | @include nb-install() { 18 | 19 | // framework global styles 20 | @include nb-theme-global(); 21 | @include nb-bootstrap-global(); 22 | 23 | // loading progress bar 24 | @include ngx-pace-theme(); 25 | 26 | // fixed in rc.9 and can be removed after upgrade 27 | .custom-control .custom-control-indicator { 28 | border-radius: 50%; // TODO: quickfix for https://github.com/akveo/nebular/issues/275 29 | } 30 | }; -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/theme.corporate.ts: -------------------------------------------------------------------------------- 1 | export const CORPORATE_THEME = { 2 | name: 'corporate', 3 | base: 'default', 4 | variables: { 5 | temperature: [ 6 | '#ffa36b', 7 | '#ffa36b', 8 | '#ff9e7a', 9 | '#ff9888', 10 | '#ff8ea0', 11 | ], 12 | 13 | solar: { 14 | gradientLeft: '#ff8ea0', 15 | gradientRight: '#ffa36b', 16 | shadowColor: 'rgba(0, 0, 0, 0)', 17 | radius: ['80%', '90%'], 18 | }, 19 | 20 | traffic: { 21 | colorBlack: '#000000', 22 | tooltipBg: '#eef2f5', 23 | tooltipBorderColor: '#eef2f5', 24 | tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;', 25 | tooltipTextColor: '#2a2a2a', 26 | tooltipFontWeight: '400', 27 | 28 | lineBg: '#c0c8d1', 29 | lineShadowBlur: '0', 30 | itemColor: '#bcc3cc', 31 | itemBorderColor: '#bcc3cc', 32 | itemEmphasisBorderColor: '#74a2ff', 33 | shadowLineDarkBg: 'rgba(0, 0, 0, 0)', 34 | shadowLineShadow: 'rgba(0, 0, 0, 0)', 35 | gradFrom: '#ffffff', 36 | gradTo: '#ffffff', 37 | }, 38 | 39 | electricity: { 40 | tooltipBg: '#edf0f4', 41 | tooltipLineColor: '#bdc4cd', 42 | tooltipLineWidth: '0', 43 | tooltipBorderColor: '#ebeef2', 44 | tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;', 45 | tooltipTextColor: '#2a2a2a', 46 | tooltipFontWeight: 'bolder', 47 | 48 | axisLineColor: 'rgba(0, 0, 0, 0)', 49 | xAxisTextColor: '#2a2a2a', 50 | yAxisSplitLine: '#ebeef2', 51 | 52 | itemBorderColor: '#73a1ff', 53 | lineStyle: 'solid', 54 | lineWidth: '4', 55 | lineGradFrom: '#bdc4cd', 56 | lineGradTo: '#c0c8d1', 57 | lineShadow: 'rgba(0, 0, 0, 0)', 58 | 59 | areaGradFrom: 'rgba(255, 255, 255, 0)', 60 | areaGradTo: 'rgba(255, 255, 255, 0)', 61 | shadowLineDarkBg: 'rgba(255, 255, 255, 0)', 62 | }, 63 | 64 | bubbleMap: { 65 | titleColor: '#484848', 66 | areaColor: '#dddddd', 67 | areaHoverColor: '#cccccc', 68 | areaBorderColor: '#ebeef2', 69 | }, 70 | 71 | echarts: { 72 | bg: '#ffffff', 73 | textColor: '#484848', 74 | axisLineColor: '#bbbbbb', 75 | splitLineColor: '#ebeef2', 76 | itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)', 77 | tooltipBackgroundColor: '#6a7985', 78 | areaOpacity: '0.7', 79 | }, 80 | 81 | chartjs: { 82 | axisLineColor: '#cccccc', 83 | textColor: '#484848', 84 | }, 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/theme.cosmic.ts: -------------------------------------------------------------------------------- 1 | export const COSMIC_THEME = { 2 | name: 'cosmic', 3 | base: 'default', 4 | variables: { 5 | 6 | temperature: [ 7 | '#2ec7fe', 8 | '#31ffad', 9 | '#7bff24', 10 | '#fff024', 11 | '#f7bd59', 12 | ], 13 | 14 | solar: { 15 | gradientLeft: '#7bff24', 16 | gradientRight: '#2ec7fe', 17 | shadowColor: '#19977E', 18 | radius: ['70%', '90%'], 19 | }, 20 | 21 | traffic: { 22 | colorBlack: '#000000', 23 | tooltipBg: 'rgba(0, 255, 170, 0.35)', 24 | tooltipBorderColor: '#00d977', 25 | tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 4px 16px;', 26 | tooltipTextColor: '#ffffff', 27 | tooltipFontWeight: 'normal', 28 | 29 | lineBg: '#d1d1ff', 30 | lineShadowBlur: '14', 31 | itemColor: '#BEBBFF', 32 | itemBorderColor: '#ffffff', 33 | itemEmphasisBorderColor: '#ffffff', 34 | shadowLineDarkBg: '#655ABD', 35 | shadowLineShadow: 'rgba(33, 7, 77, 0.5)', 36 | gradFrom: 'rgba(118, 89, 255, 0.4)', 37 | gradTo: 'rgba(164, 84, 255, 0.5)', 38 | }, 39 | 40 | electricity: { 41 | tooltipBg: 'rgba(0, 255, 170, 0.35)', 42 | tooltipLineColor: 'rgba(255, 255, 255, 0.1)', 43 | tooltipLineWidth: '1', 44 | tooltipBorderColor: '#00d977', 45 | tooltipExtraCss: 'box-shadow: 0px 2px 46px 0 rgba(0, 255, 170, 0.35); border-radius: 10px; padding: 8px 24px;', 46 | tooltipTextColor: '#ffffff', 47 | tooltipFontWeight: 'normal', 48 | 49 | axisLineColor: 'rgba(161, 161 ,229, 0.3)', 50 | xAxisTextColor: '#a1a1e5', 51 | yAxisSplitLine: 'rgba(161, 161 ,229, 0.2)', 52 | 53 | itemBorderColor: '#ffffff', 54 | lineStyle: 'dotted', 55 | lineWidth: '6', 56 | lineGradFrom: '#00ffaa', 57 | lineGradTo: '#fff835', 58 | lineShadow: 'rgba(14, 16, 48, 0.4)', 59 | 60 | areaGradFrom: 'rgba(188, 92, 255, 0.5)', 61 | areaGradTo: 'rgba(188, 92, 255, 0)', 62 | shadowLineDarkBg: '#a695ff', 63 | }, 64 | 65 | bubbleMap: { 66 | titleColor: '#ffffff', 67 | areaColor: '#2c2961', 68 | areaHoverColor: '#a1a1e5', 69 | areaBorderColor: '#654ddb', 70 | }, 71 | 72 | echarts: { 73 | bg: '#3d3780', 74 | textColor: '#ffffff', 75 | axisLineColor: '#a1a1e5', 76 | splitLineColor: '#342e73', 77 | itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)', 78 | tooltipBackgroundColor: '#6a7985', 79 | areaOpacity: '1', 80 | }, 81 | 82 | chartjs: { 83 | axisLineColor: '#a1a1e5', 84 | textColor: '#ffffff', 85 | }, 86 | }, 87 | }; 88 | -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/theme.default.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_THEME = { 2 | name: 'default', 3 | base: null, 4 | variables: { 5 | 6 | // Safari fix 7 | temperature: [ 8 | '#42db7d', 9 | '#42db7d', 10 | '#42db7d', 11 | '#42db7d', 12 | '#42db7d', 13 | ], 14 | 15 | solar: { 16 | gradientLeft: '#42db7d', 17 | gradientRight: '#42db7d', 18 | shadowColor: 'rgba(0, 0, 0, 0)', 19 | radius: ['80%', '90%'], 20 | }, 21 | 22 | traffic: { 23 | colorBlack: '#000000', 24 | tooltipBg: '#ffffff', 25 | tooltipBorderColor: '#c0c8d1', 26 | tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;', 27 | tooltipTextColor: '#2a2a2a', 28 | tooltipFontWeight: 'bolder', 29 | 30 | lineBg: '#c0c8d1', 31 | lineShadowBlur: '1', 32 | itemColor: '#bcc3cc', 33 | itemBorderColor: '#bcc3cc', 34 | itemEmphasisBorderColor: '#42db7d', 35 | shadowLineDarkBg: 'rgba(0, 0, 0, 0)', 36 | shadowLineShadow: 'rgba(0, 0, 0, 0)', 37 | gradFrom: '#ebeef2', 38 | gradTo: '#ebeef2', 39 | }, 40 | 41 | electricity: { 42 | tooltipBg: '#ffffff', 43 | tooltipLineColor: 'rgba(0, 0, 0, 0)', 44 | tooltipLineWidth: '0', 45 | tooltipBorderColor: '#ebeef2', 46 | tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;', 47 | tooltipTextColor: '#2a2a2a', 48 | tooltipFontWeight: 'bolder', 49 | 50 | axisLineColor: 'rgba(0, 0, 0, 0)', 51 | xAxisTextColor: '#2a2a2a', 52 | yAxisSplitLine: '#ebeef2', 53 | 54 | itemBorderColor: '#42db7d', 55 | lineStyle: 'solid', 56 | lineWidth: '4', 57 | lineGradFrom: '#42db7d', 58 | lineGradTo: '#42db7d', 59 | lineShadow: 'rgba(0, 0, 0, 0)', 60 | 61 | areaGradFrom: 'rgba(235, 238, 242, 0.5)', 62 | areaGradTo: 'rgba(235, 238, 242, 0.5)', 63 | shadowLineDarkBg: 'rgba(0, 0, 0, 0)', 64 | }, 65 | 66 | bubbleMap: { 67 | titleColor: '#484848', 68 | areaColor: '#dddddd', 69 | areaHoverColor: '#cccccc', 70 | areaBorderColor: '#ebeef2', 71 | }, 72 | 73 | echarts: { 74 | bg: '#ffffff', 75 | textColor: '#484848', 76 | axisLineColor: '#bbbbbb', 77 | splitLineColor: '#ebeef2', 78 | itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)', 79 | tooltipBackgroundColor: '#6a7985', 80 | areaOpacity: '0.7', 81 | }, 82 | 83 | chartjs: { 84 | axisLineColor: '#cccccc', 85 | textColor: '#484848', 86 | }, 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /angular/src/assets/scss/theme/themes.scss: -------------------------------------------------------------------------------- 1 | // @nebular theming framework 2 | @import '~@nebular/theme/styles/theming'; 3 | // @nebular out of the box themes 4 | @import '~@nebular/theme/styles/themes'; 5 | 6 | // which themes you what to enable (empty to enable all) 7 | $nb-enabled-themes: (default, cosmic, corporate); 8 | 9 | $nb-themes: nb-register-theme(( 10 | // app wise variables for each theme 11 | sidebar-header-gap: 2rem, 12 | sidebar-header-height: initial, 13 | layout-content-width: 1400px, 14 | 15 | font-main: Roboto, 16 | font-secondary: Exo, 17 | 18 | switcher-background: #ebeff5, 19 | switcher-background-percentage: 50%, 20 | drops-icon-line-gadient: -webkit-linear-gradient(#01dbb5, #0bbb79), 21 | ), default, default); 22 | 23 | $nb-themes: nb-register-theme(( 24 | // app wise variables for each theme 25 | sidebar-header-gap: 2rem, 26 | sidebar-header-height: initial, 27 | layout-content-width: 1400px, 28 | 29 | font-main: Roboto, 30 | font-secondary: Exo, 31 | 32 | switcher-background: #4e41a5, 33 | switcher-background-percentage: 14%, 34 | drops-icon-line-gadient: -webkit-linear-gradient(#a258fe, #7958fa), 35 | ), cosmic, cosmic); 36 | 37 | $nb-themes: nb-register-theme(( 38 | // app wise variables for each theme 39 | sidebar-header-gap: 2rem, 40 | sidebar-header-height: initial, 41 | layout-content-width: 1400px, 42 | 43 | font-main: Roboto, 44 | font-secondary: Exo, 45 | 46 | switcher-background: #2b2d34, 47 | switcher-background-percentage: 14%, 48 | drops-icon-line-gadient: -webkit-linear-gradient(#e9e8eb, #a7a2be), 49 | ), corporate, corporate); 50 | -------------------------------------------------------------------------------- /angular/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /angular/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | host: 'http://localhost:3200', 4 | login: '/login', 5 | fotgotPassword: '/forgot-password', 6 | resetPassword: '/reset-password', 7 | countires: '/static/countries.json', 8 | fileupload: '/fileupload', 9 | deleteAttachment: '/filedelete' 10 | 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /angular/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | host: 'http://localhost:3000', 8 | login: '/login', 9 | fotgotPassword: '/forgot-password', 10 | resetPassword: '/reset-password', 11 | countires: '/static/countries.json', 12 | fileupload: '/fileupload', 13 | deleteAttachment: '/filedelete', 14 | clientSignup: '/client-signup' 15 | }; 16 | 17 | /* 18 | * In development mode, to ignore zone related error stack frames such as 19 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 20 | * import the following file, but please comment it out in production mode 21 | * because it will have performance impact when throw error 22 | */ 23 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 24 | -------------------------------------------------------------------------------- /angular/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajaramtt/angular6-realworld-highly-scalable-application/d1de8f535f47b0a3784c3c74f2f3fed4618a9348/angular/src/favicon.ico -------------------------------------------------------------------------------- /angular/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angular6-realworld-highly-scalable-application 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Loading... 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /angular/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /angular/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /angular/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular6-realworld-highly-scalable-application", 3 | "short_name": "angular6-realworld-highly-scalable-application", 4 | "theme_color": "#1976d2", 5 | "background_color": "#fafafa", 6 | "display": "standalone", 7 | "scope": "/", 8 | "start_url": "/", 9 | "icons": [ 10 | { 11 | "src": "assets/icons/icon-72x72.png", 12 | "sizes": "72x72", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "assets/icons/icon-96x96.png", 17 | "sizes": "96x96", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "assets/icons/icon-128x128.png", 22 | "sizes": "128x128", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "assets/icons/icon-144x144.png", 27 | "sizes": "144x144", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "assets/icons/icon-152x152.png", 32 | "sizes": "152x152", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "assets/icons/icon-192x192.png", 37 | "sizes": "192x192", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "assets/icons/icon-384x384.png", 42 | "sizes": "384x384", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "assets/icons/icon-512x512.png", 47 | "sizes": "512x512", 48 | "type": "image/png" 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /angular/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /angular/src/styles.scss: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes spin { 2 | 0% { 3 | transform: rotate(0) 4 | } 5 | 100% { 6 | transform: rotate(360deg) 7 | } 8 | } 9 | 10 | @-moz-keyframes spin { 11 | 0% { 12 | -moz-transform: rotate(0) 13 | } 14 | 100% { 15 | -moz-transform: rotate(360deg) 16 | } 17 | } 18 | 19 | @keyframes spin { 20 | 0% { 21 | transform: rotate(0) 22 | } 23 | 100% { 24 | transform: rotate(360deg) 25 | } 26 | } 27 | 28 | .spinner { 29 | position: fixed; 30 | top: 0; 31 | left: 0; 32 | width: 100%; 33 | height: 100%; 34 | z-index: 1003; 35 | background: #000000; 36 | overflow: hidden 37 | } 38 | 39 | .spinner div:first-child { 40 | display: block; 41 | position: relative; 42 | left: 50%; 43 | top: 50%; 44 | width: 150px; 45 | height: 150px; 46 | margin: -75px 0 0 -75px; 47 | border-radius: 50%; 48 | box-shadow: 0 3px 3px 0 rgba(255, 56, 106, 1); 49 | transform: translate3d(0, 0, 0); 50 | animation: spin 2s linear infinite 51 | } 52 | 53 | .spinner div:first-child:after, 54 | .spinner div:first-child:before { 55 | content: ''; 56 | position: absolute; 57 | border-radius: 50% 58 | } 59 | 60 | .spinner div:first-child:before { 61 | top: 5px; 62 | left: 5px; 63 | right: 5px; 64 | bottom: 5px; 65 | box-shadow: 0 3px 3px 0 rgb(255, 228, 32); 66 | -webkit-animation: spin 3s linear infinite; 67 | animation: spin 3s linear infinite 68 | } 69 | 70 | .spinner div:first-child:after { 71 | top: 15px; 72 | left: 15px; 73 | right: 15px; 74 | bottom: 15px; 75 | box-shadow: 0 3px 3px 0 rgba(61, 175, 255, 1); 76 | animation: spin 1.5s linear infinite 77 | } 78 | 79 | .error { 80 | color: red; 81 | } 82 | 83 | .form-group input.ng-invalid.ng-touched, 84 | .form-group input.ng-invalid:focus, 85 | .form-group select.ng-invalid.ng-touched, 86 | .form-group textarea.ng-invalid.ng-touched, 87 | .form-submit input.ng-invalid, 88 | .form-submit select.ng-invalid, 89 | .form-submit textarea.ng-invalid 90 | { 91 | border-color: #ff4c6a; 92 | } 93 | 94 | .invalid-feedback.form-error { 95 | display: block; 96 | } 97 | 98 | .iconsize { 99 | font-size: 1.5rem; 100 | } 101 | 102 | .c-pointer { 103 | cursor: pointer; 104 | } -------------------------------------------------------------------------------- /angular/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /angular/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /angular/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /angular/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /angular/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /angular/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /node/.env: -------------------------------------------------------------------------------- 1 | ORIGIN_URL=http://localhost:4200 -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | 62 | 63 | # parcel-bundler cache (https://parceljs.org/) 64 | .cache 65 | 66 | # next.js build output 67 | .next 68 | 69 | # nuxt.js build output 70 | .nuxt 71 | 72 | # vuepress build output 73 | .vuepress/dist 74 | 75 | # Serverless directories 76 | .serverless 77 | 78 | /public/upload 79 | 80 | # End of https://www.gitignore.io/api/node -------------------------------------------------------------------------------- /node/README.md: -------------------------------------------------------------------------------- 1 | 2 | # angular6 realworld highly scalable application 3 | 4 | The project is inspired by an article wrote on Medium called How to define a highly scalable folder structure for your Angular project. The project is based on best practices from the community, other github Angular projects and my own experiences from working on couple of Angular projects. 5 | 6 | 7 | ## Node.js Requirements: 8 | 9 | The node.js project needs to be a minimum version of listed below. 10 | 11 | 1. ExpressJs 4.x 12 | 2. Node.js 10.x and npm 6.x 13 | 3. MongoDB 4.x 14 | 15 | #Angular Project setup 16 | 17 | 1. git clone https://github.com/rajaramtt/angular6-realworld-highly-scalable-application.git 18 | 2. cd angular6-realworld-highly-scalable-application/node 19 | 3. 20 | ```sh 21 | npm install 22 | ``` 23 | 4. start MongoDB 24 | 5. 25 | ```sh 26 | npm start 27 | ``` 28 | 6. Open brower http://localhost:3000 29 | 30 | ## Help/Assistance 31 | 32 | Developer: Raja Rama Mohan Thavalam 33 | 34 | 35 | ## License 36 | 37 | 38 | (The MIT License) 39 | 40 | Copyright (c) 2018 Raja Rama Mohan T 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining 43 | a copy of this software and associated documentation files (the 44 | 'Software'), to deal in the Software without restriction, including 45 | without limitation the rights to use, copy, modify, merge, publish, 46 | distribute, sublicense, and/or sell copies of the Software, and to 47 | permit persons to whom the Software is furnished to do so, subject to 48 | the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be 51 | included in all copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 54 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 55 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 56 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 57 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 58 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 59 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /node/app.js: -------------------------------------------------------------------------------- 1 | let createError = require('http-errors'); 2 | let express = require('express'); 3 | let path = require('path'); 4 | let cookieParser = require('cookie-parser'); 5 | let logger = require('morgan'); 6 | let cors = require('cors') 7 | let bodyParser = require('body-parser') 8 | require('dotenv').config() 9 | 10 | let indexRouter = require('./routes/index'); 11 | let usersRouter = require('./routes/users'); 12 | let loginRouter = require('./routes/login'); 13 | let forgotPassword = require('./routes/forgot-password'); 14 | let resetPassword = require('./routes/reset-password'); 15 | let fileUpload = require('./routes/file-upload'); 16 | let fileDelete = require('./routes/file-delete'); 17 | let clientSignup = require('./routes/client-signup'); 18 | 19 | let app = express(); 20 | 21 | 22 | app.use(cors({ origin: process.env.ORIGIN_URL , credentials : true})); 23 | 24 | // view engine setup 25 | app.set('views', path.join(__dirname, 'views')); 26 | app.set('view engine', 'ejs'); 27 | 28 | app.use(logger('dev')); 29 | app.use(express.json()); 30 | app.use(express.urlencoded({ extended: false })); 31 | app.use(cookieParser()); 32 | app.use(express.static(path.join(__dirname, 'public'))); 33 | 34 | app.use(bodyParser.json()); 35 | app.use(bodyParser.urlencoded({extended: true})); 36 | 37 | app.use('/', indexRouter); 38 | app.use('/users', usersRouter); 39 | app.use('/login', loginRouter); 40 | app.use('/forgot-password', forgotPassword); 41 | app.use('/reset-password', resetPassword); 42 | app.use('/fileupload', fileUpload); 43 | app.use('/filedelete', fileDelete); 44 | app.use('/client-signup', clientSignup) 45 | 46 | // catch 404 and forward to error handler 47 | app.use(function(req, res, next) { 48 | next(createError(404)); 49 | }); 50 | 51 | // error handler 52 | app.use(function(err, req, res, next) { 53 | // set locals, only providing error in development 54 | res.locals.message = err.message; 55 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 56 | 57 | // render the error page 58 | res.status(err.status || 500); 59 | res.render('error'); 60 | }); 61 | 62 | module.exports = app; 63 | -------------------------------------------------------------------------------- /node/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('node:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "lint": "eslint ./" 8 | }, 9 | "dependencies": { 10 | "body-parser": "^1.18.3", 11 | "cookie-parser": "~1.4.3", 12 | "cors": "^2.8.4", 13 | "debug": "~2.6.9", 14 | "dotenv": "^6.0.0", 15 | "ejs": "~2.5.7", 16 | "express": "~4.16.0", 17 | "http-errors": "~1.6.2", 18 | "morgan": "~1.9.0", 19 | "multer": "^1.3.1", 20 | "path": "^0.12.7", 21 | "uniqid": "^5.0.3" 22 | }, 23 | "devDependencies": { 24 | "nodemon": "^1.18.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /node/public/static/countries.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "country": "Afghanistan" 3 | }, 4 | { 5 | "country": "Albania" 6 | }, 7 | { 8 | "country": "Algeria" 9 | }, 10 | { 11 | "country": "American Samoa" 12 | }, 13 | { 14 | "country": "Andorra" 15 | }, 16 | { 17 | "country": "Angola" 18 | }, 19 | { 20 | "country": "Anguilla" 21 | }, 22 | { 23 | "country": "Antarctica" 24 | }, 25 | { 26 | "country": "Antigua and Barbuda" 27 | }, 28 | { 29 | "country": "Argentina" 30 | }, 31 | { 32 | "country": "Armenia" 33 | }, 34 | { 35 | "country": "Aruba" 36 | }, 37 | { 38 | "country": "Australia" 39 | }, 40 | { 41 | "country": "Austria" 42 | }, 43 | { 44 | "country": "Azerbaijan" 45 | }, 46 | { 47 | "country": "Bahamas" 48 | }, 49 | { 50 | "country": "Bahrain" 51 | }, 52 | { 53 | "country": "Bangladesh" 54 | }, 55 | { 56 | "country": "Barbados" 57 | }, 58 | { 59 | "country": "Belarus" 60 | }, 61 | { 62 | "country": "Belgium" 63 | }, 64 | { 65 | "country": "Belize" 66 | }, 67 | { 68 | "country": "Benin" 69 | }, 70 | { 71 | "country": "Bermuda" 72 | }, 73 | { 74 | "country": "Bhutan" 75 | }, 76 | { 77 | "country": "Bolivia" 78 | }, 79 | { 80 | "country": "Bosnia and Herzegovina" 81 | }, 82 | { 83 | "country": "Botswana" 84 | }, 85 | { 86 | "country": "Bouvet Island" 87 | }, 88 | { 89 | "country": "Brazil" 90 | }, 91 | { 92 | "country": "British Indian Ocean Territory" 93 | }, 94 | { 95 | "country": "Brunei" 96 | }, 97 | { 98 | "country": "Bulgaria" 99 | }, 100 | { 101 | "country": "Burkina Faso" 102 | }, 103 | { 104 | "country": "Burundi" 105 | }, 106 | { 107 | "country": "Cambodia" 108 | }, 109 | { 110 | "country": "Cameroon" 111 | }, 112 | { 113 | "country": "Canada" 114 | }, 115 | { 116 | "country": "Cape Verde" 117 | }, 118 | { 119 | "country": "Cayman Islands" 120 | }, 121 | { 122 | "country": "Central African Republic" 123 | }, 124 | { 125 | "country": "Chad" 126 | }, 127 | { 128 | "country": "Chile" 129 | }, 130 | { 131 | "country": "China" 132 | }, 133 | { 134 | "country": "Christmas Island" 135 | }, 136 | { 137 | "country": "Cocos (Keeling) Islands" 138 | }, 139 | { 140 | "country": "Colombia" 141 | }, 142 | { 143 | "country": "Comoros" 144 | }, 145 | { 146 | "country": "Congo" 147 | }, 148 | { 149 | "country": "The Democratic Republic of Congo" 150 | }, 151 | { 152 | "country": "Cook Islands" 153 | }, 154 | { 155 | "country": "Costa Rica" 156 | }, 157 | { 158 | "country": "Ivory Coast" 159 | }, 160 | { 161 | "country": "Croatia" 162 | }, 163 | { 164 | "country": "Cuba" 165 | }, 166 | { 167 | "country": "Cyprus" 168 | }, 169 | { 170 | "country": "Czech Republic" 171 | }, 172 | { 173 | "country": "Denmark" 174 | }, 175 | { 176 | "country": "Djibouti" 177 | }, 178 | { 179 | "country": "Dominica" 180 | }, 181 | { 182 | "country": "Dominican Republic" 183 | }, 184 | { 185 | "country": "East Timor" 186 | }, 187 | { 188 | "country": "Ecuador" 189 | }, 190 | { 191 | "country": "Egypt" 192 | }, 193 | { 194 | "country": "England" 195 | }, 196 | { 197 | "country": "El Salvador" 198 | }, 199 | { 200 | "country": "Equatorial Guinea" 201 | }, 202 | { 203 | "country": "Eritrea" 204 | }, 205 | { 206 | "country": "Estonia" 207 | }, 208 | { 209 | "country": "Ethiopia" 210 | }, 211 | { 212 | "country": "Falkland Islands" 213 | }, 214 | { 215 | "country": "Faroe Islands" 216 | }, 217 | { 218 | "country": "Fiji Islands" 219 | }, 220 | { 221 | "country": "Finland" 222 | }, 223 | { 224 | "country": "France" 225 | }, 226 | { 227 | "country": "French Guiana" 228 | }, 229 | { 230 | "country": "French Polynesia" 231 | }, 232 | { 233 | "country": "French Southern territories" 234 | }, 235 | { 236 | "country": "Gabon" 237 | }, 238 | { 239 | "country": "Gambia" 240 | }, 241 | { 242 | "country": "Georgia" 243 | }, 244 | { 245 | "country": "Germany" 246 | }, 247 | { 248 | "country": "Ghana" 249 | }, 250 | { 251 | "country": "Gibraltar" 252 | }, 253 | { 254 | "country": "Greece" 255 | }, 256 | { 257 | "country": "Greenland" 258 | }, 259 | { 260 | "country": "Grenada" 261 | }, 262 | { 263 | "country": "Guadeloupe" 264 | }, 265 | { 266 | "country": "Guam" 267 | }, 268 | { 269 | "country": "Guatemala" 270 | }, 271 | { 272 | "country": "Guinea" 273 | }, 274 | { 275 | "country": "Guinea-Bissau" 276 | }, 277 | { 278 | "country": "Guyana" 279 | }, 280 | { 281 | "country": "Haiti" 282 | }, 283 | { 284 | "country": "Heard Island and McDonald Islands" 285 | }, 286 | { 287 | "country": "Holy See (Vatican City State)" 288 | }, 289 | { 290 | "country": "Honduras" 291 | }, 292 | { 293 | "country": "Hong Kong" 294 | }, 295 | { 296 | "country": "Hungary" 297 | }, 298 | { 299 | "country": "Iceland" 300 | }, 301 | { 302 | "country": "India" 303 | }, 304 | { 305 | "country": "Indonesia" 306 | }, 307 | { 308 | "country": "Iran" 309 | }, 310 | { 311 | "country": "Iraq" 312 | }, 313 | { 314 | "country": "Ireland" 315 | }, 316 | { 317 | "country": "Israel" 318 | }, 319 | { 320 | "country": "Italy" 321 | }, 322 | { 323 | "country": "Jamaica" 324 | }, 325 | { 326 | "country": "Japan" 327 | }, 328 | { 329 | "country": "Jordan" 330 | }, 331 | { 332 | "country": "Kazakstan" 333 | }, 334 | { 335 | "country": "Kenya" 336 | }, 337 | { 338 | "country": "Kiribati" 339 | }, 340 | { 341 | "country": "Kuwait" 342 | }, 343 | { 344 | "country": "Kyrgyzstan" 345 | }, 346 | { 347 | "country": "Laos" 348 | }, 349 | { 350 | "country": "Latvia" 351 | }, 352 | { 353 | "country": "Lebanon" 354 | }, 355 | { 356 | "country": "Lesotho" 357 | }, 358 | { 359 | "country": "Liberia" 360 | }, 361 | { 362 | "country": "Libyan Arab Jamahiriya" 363 | }, 364 | { 365 | "country": "Liechtenstein" 366 | }, 367 | { 368 | "country": "Lithuania" 369 | }, 370 | { 371 | "country": "Luxembourg" 372 | }, 373 | { 374 | "country": "Macao" 375 | }, 376 | { 377 | "country": "Macedonia" 378 | }, 379 | { 380 | "country": "Madagascar" 381 | }, 382 | { 383 | "country": "Malawi" 384 | }, 385 | { 386 | "country": "Malaysia" 387 | }, 388 | { 389 | "country": "Maldives" 390 | }, 391 | { 392 | "country": "Mali" 393 | }, 394 | { 395 | "country": "Malta" 396 | }, 397 | { 398 | "country": "Marshall Islands" 399 | }, 400 | { 401 | "country": "Martinique" 402 | }, 403 | { 404 | "country": "Mauritania" 405 | }, 406 | { 407 | "country": "Mauritius" 408 | }, 409 | { 410 | "country": "Mayotte" 411 | }, 412 | { 413 | "country": "Mexico" 414 | }, 415 | { 416 | "country": "Micronesia, Federated States of" 417 | }, 418 | { 419 | "country": "Moldova" 420 | }, 421 | { 422 | "country": "Monaco" 423 | }, 424 | { 425 | "country": "Mongolia" 426 | }, 427 | { 428 | "country": "Montserrat" 429 | }, 430 | { 431 | "country": "Morocco" 432 | }, 433 | { 434 | "country": "Mozambique" 435 | }, 436 | { 437 | "country": "Myanmar" 438 | }, 439 | { 440 | "country": "Namibia" 441 | }, 442 | { 443 | "country": "Nauru" 444 | }, 445 | { 446 | "country": "Nepal" 447 | }, 448 | { 449 | "country": "Netherlands" 450 | }, 451 | { 452 | "country": "Netherlands Antilles" 453 | }, 454 | { 455 | "country": "New Caledonia" 456 | }, 457 | { 458 | "country": "New Zealand" 459 | }, 460 | { 461 | "country": "Nicaragua" 462 | }, 463 | { 464 | "country": "Niger" 465 | }, 466 | { 467 | "country": "Nigeria" 468 | }, 469 | { 470 | "country": "Niue" 471 | }, 472 | { 473 | "country": "Norfolk Island" 474 | }, 475 | { 476 | "country": "North Korea" 477 | }, 478 | { 479 | "country": "Northern Ireland" 480 | }, 481 | { 482 | "country": "Northern Mariana Islands" 483 | }, 484 | { 485 | "country": "Norway" 486 | }, 487 | { 488 | "country": "Oman" 489 | }, 490 | { 491 | "country": "Pakistan" 492 | }, 493 | { 494 | "country": "Palau" 495 | }, 496 | { 497 | "country": "Palestine" 498 | }, 499 | { 500 | "country": "Panama" 501 | }, 502 | { 503 | "country": "Papua New Guinea" 504 | }, 505 | { 506 | "country": "Paraguay" 507 | }, 508 | { 509 | "country": "Peru" 510 | }, 511 | { 512 | "country": "Philippines" 513 | }, 514 | { 515 | "country": "Pitcairn" 516 | }, 517 | { 518 | "country": "Poland" 519 | }, 520 | { 521 | "country": "Portugal" 522 | }, 523 | { 524 | "country": "Puerto Rico" 525 | }, 526 | { 527 | "country": "Qatar" 528 | }, 529 | { 530 | "country": "Reunion" 531 | }, 532 | { 533 | "country": "Romania" 534 | }, 535 | { 536 | "country": "Russian Federation" 537 | }, 538 | { 539 | "country": "Rwanda" 540 | }, 541 | { 542 | "country": "Saint Helena" 543 | }, 544 | { 545 | "country": "Saint Kitts and Nevis" 546 | }, 547 | { 548 | "country": "Saint Lucia" 549 | }, 550 | { 551 | "country": "Saint Pierre and Miquelon" 552 | }, 553 | { 554 | "country": "Saint Vincent and the Grenadines" 555 | }, 556 | { 557 | "country": "Samoa" 558 | }, 559 | { 560 | "country": "San Marino" 561 | }, 562 | { 563 | "country": "Sao Tome and Principe" 564 | }, 565 | { 566 | "country": "Saudi Arabia" 567 | }, 568 | { 569 | "country": "Scotland" 570 | }, 571 | { 572 | "country": "Senegal" 573 | }, 574 | { 575 | "country": "Seychelles" 576 | }, 577 | { 578 | "country": "Sierra Leone" 579 | }, 580 | { 581 | "country": "Singapore" 582 | }, 583 | { 584 | "country": "Slovakia" 585 | }, 586 | { 587 | "country": "Slovenia" 588 | }, 589 | { 590 | "country": "Solomon Islands" 591 | }, 592 | { 593 | "country": "Somalia" 594 | }, 595 | { 596 | "country": "South Africa" 597 | }, 598 | { 599 | "country": "South Georgia and the South Sandwich Islands" 600 | }, 601 | { 602 | "country": "South Korea" 603 | }, 604 | { 605 | "country": "South Sudan" 606 | }, 607 | { 608 | "country": "Spain" 609 | }, 610 | { 611 | "country": "SriLanka" 612 | }, 613 | { 614 | "country": "Sudan" 615 | }, 616 | { 617 | "country": "Suriname" 618 | }, 619 | { 620 | "country": "Svalbard and Jan Mayen" 621 | }, 622 | { 623 | "country": "Swaziland" 624 | }, 625 | { 626 | "country": "Sweden" 627 | }, 628 | { 629 | "country": "Switzerland" 630 | }, 631 | { 632 | "country": "Syria" 633 | }, 634 | { 635 | "country": "Tajikistan" 636 | }, 637 | { 638 | "country": "Tanzania" 639 | }, 640 | { 641 | "country": "Thailand" 642 | }, 643 | { 644 | "country": "Togo" 645 | }, 646 | { 647 | "country": "Tokelau" 648 | }, 649 | { 650 | "country": "Tonga" 651 | }, 652 | { 653 | "country": "Trinidad and Tobago" 654 | }, 655 | { 656 | "country": "Tunisia" 657 | }, 658 | { 659 | "country": "Turkey" 660 | }, 661 | { 662 | "country": "Turkmenistan" 663 | }, 664 | { 665 | "country": "Turks and Caicos Islands" 666 | }, 667 | { 668 | "country": "Tuvalu" 669 | }, 670 | { 671 | "country": "Uganda" 672 | }, 673 | { 674 | "country": "Ukraine" 675 | }, 676 | { 677 | "country": "United Arab Emirates" 678 | }, 679 | { 680 | "country": "United Kingdom" 681 | }, 682 | { 683 | "country": "United States" 684 | }, 685 | { 686 | "country": "United States Minor Outlying Islands" 687 | }, 688 | { 689 | "country": "Uruguay" 690 | }, 691 | { 692 | "country": "Uzbekistan" 693 | }, 694 | { 695 | "country": "Vanuatu" 696 | }, 697 | { 698 | "country": "Venezuela" 699 | }, 700 | { 701 | "country": "Vietnam" 702 | }, 703 | { 704 | "country": "Virgin Islands, British" 705 | }, 706 | { 707 | "country": "Virgin Islands, U.S." 708 | }, 709 | { 710 | "country": "Wales" 711 | }, 712 | { 713 | "country": "Wallis and Futuna" 714 | }, 715 | { 716 | "country": "Western Sahara" 717 | }, 718 | { 719 | "country": "Yemen" 720 | }, 721 | { 722 | "country": "Yugoslavia" 723 | }, 724 | { 725 | "country": "Zambia" 726 | }, 727 | { 728 | "country": "Zimbabwe" 729 | } 730 | ] -------------------------------------------------------------------------------- /node/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /node/routes/client-signup.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | router.post('/', function (req, res, next) { 5 | res.json({ 6 | status: "success", 7 | message: `An email has been sent to ${req.body.userInfo.email }. It contains an activation link you must click to activate your account.` 8 | }); 9 | }); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /node/routes/file-delete.js: -------------------------------------------------------------------------------- 1 | 2 | let express = require('express'); 3 | let router = express.Router(); 4 | const DIR = './public/upload'; 5 | const fs = require('fs'); 6 | 7 | router.post('/', function (req, res, next) { 8 | fs.unlink(`${DIR}/${req.body.filename}`, (err) => { 9 | if (err) { 10 | res.json({ 11 | status: "error", 12 | message: err.message 13 | }); 14 | } else { 15 | res.json({ 16 | status: "success", 17 | message: "Attachment has been deleted successfully" 18 | }); 19 | } 20 | }); 21 | }); 22 | 23 | module.exports = router; 24 | 25 | -------------------------------------------------------------------------------- /node/routes/file-upload.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | let path = require('path'); 4 | const DIR = './public/upload'; 5 | const multer = require('multer'); 6 | let uniqid = require('uniqid'); 7 | 8 | let storage = multer.diskStorage({ 9 | destination: (req, file, cb) => { 10 | cb(null, DIR); 11 | }, 12 | filename: (req, file, cb) => { 13 | cb(null, path.parse(file.originalname).name + '-' + uniqid() + path.extname(file.originalname)); 14 | } 15 | }); 16 | let upload = multer({ storage: storage }); 17 | 18 | router.post('/', upload.single('attachment'), function (req, res) { 19 | if (!req.file) { 20 | res.json({ 21 | status: "failed", 22 | name: req.file.filename 23 | }); 24 | } else { 25 | res.json({ 26 | status: "success", 27 | name: req.file.filename 28 | }); 29 | 30 | } 31 | }); 32 | 33 | module.exports = router; 34 | -------------------------------------------------------------------------------- /node/routes/forgot-password.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | router.post('/', function(req, res, next) { 5 | 6 | res.json({ 7 | status:"success", 8 | message: "The password reset request has been successfully submitted. Please check your email for instructions" 9 | }); 10 | 11 | }); 12 | 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /node/routes/index.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /node/routes/login.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | router.post('/', function(req, res, next) { 5 | res.json({ 6 | status:"success", 7 | email: "raja@test.com", 8 | name: "raja", 9 | id: 1, 10 | token: "123456789", 11 | role: 'client' 12 | }); 13 | // res.send(403); 14 | }); 15 | 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /node/routes/reset-password.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | router.post('/', function (req, res, next) { 5 | 6 | res.json({ 7 | status: "success", 8 | message: "Your password has been changed successfully, Please login to continue application" 9 | }); 10 | }); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /node/routes/users.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function (req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /node/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /node/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | --------------------------------------------------------------------------------