├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.config.ts │ ├── app.routes.ts │ ├── pages │ │ ├── auth │ │ │ ├── auth.component-2.html │ │ │ ├── auth.component-2.scss │ │ │ ├── auth.component.html │ │ │ ├── auth.component.scss │ │ │ ├── auth.component.ts │ │ │ ├── auth.routes.ts │ │ │ ├── forgot-password │ │ │ │ ├── forgot-password.component.html │ │ │ │ ├── forgot-password.component.scss │ │ │ │ └── forgot-password.component.ts │ │ │ ├── login │ │ │ │ ├── login.component.html │ │ │ │ ├── login.component.scss │ │ │ │ └── login.component.ts │ │ │ └── validate-account │ │ │ │ ├── validate-account.component.html │ │ │ │ ├── validate-account.component.scss │ │ │ │ └── validate-account.component.ts │ │ ├── home │ │ │ ├── home.component.html │ │ │ ├── home.component.scss │ │ │ └── home.component.ts │ │ └── not-found │ │ │ ├── not-found.component.html │ │ │ ├── not-found.component.scss │ │ │ └── not-found.component.ts │ └── shared │ │ ├── components │ │ ├── blocks │ │ │ ├── progress-bar │ │ │ │ ├── progress-bar.component.html │ │ │ │ ├── progress-bar.component.scss │ │ │ │ └── progress-bar.component.ts │ │ │ └── toast │ │ │ │ ├── toast.component.html │ │ │ │ ├── toast.component.scss │ │ │ │ ├── toast.component.ts │ │ │ │ └── toast.manager.ts │ │ ├── forms │ │ │ └── form-confirm │ │ │ │ ├── form-confirm.component.html │ │ │ │ ├── form-confirm.component.scss │ │ │ │ └── form-confirm.component.ts │ │ ├── layouts │ │ │ ├── layout-header │ │ │ │ ├── layout-header.component.html │ │ │ │ ├── layout-header.component.scss │ │ │ │ └── layout-header.component.ts │ │ │ └── page-layout │ │ │ │ ├── page-layout.component.html │ │ │ │ ├── page-layout.component.scss │ │ │ │ └── page-layout.component.ts │ │ └── modals │ │ │ └── modal-wrapper │ │ │ ├── modal-wrapper.component.html │ │ │ ├── modal-wrapper.component.scss │ │ │ └── modal-wrapper.component.ts │ │ ├── directives │ │ └── modal-wrapper.directive.ts │ │ ├── enums │ │ ├── endpoint.enum.ts │ │ ├── environment.enum.ts │ │ └── storage-key.enum.ts │ │ ├── helpers │ │ ├── storage.helper.ts │ │ └── string.helper.ts │ │ └── services │ │ ├── app.service.ts │ │ └── store.service.ts ├── assets │ ├── .gitkeep │ ├── i18n │ │ └── en.json │ ├── img │ │ ├── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ ├── mstile-144x144.png │ │ │ ├── mstile-150x150.png │ │ │ ├── mstile-310x150.png │ │ │ ├── mstile-310x310.png │ │ │ ├── mstile-70x70.png │ │ │ └── safari-pinned-tab.svg │ │ └── project │ │ │ ├── folder-structure.png │ │ │ ├── login.png │ │ │ └── logo.svg │ └── scss │ │ ├── project.scss │ │ ├── styles.scss │ │ └── variables.scss ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── index.html ├── main.ts ├── polyfills.ts └── test.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.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 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ▶️ EasyAngular 2 | 3 | Welcome to the EasyAngular boilerplate! This project is designed to help you quickly start a new **Angular 18** project with **Bootstrap 5** and various useful libraries. It comes with pre-coded elements to streamline your development process. 4 | 5 | ## Getting started 6 | ### Prerequisites 7 | 8 | Make sure you have the following installed : 9 | - [Node.js](https://nodejs.org/) (version 20) 10 | - [Angular CLI](https://angular.dev/) (version 18) using `npm install -g @angular/cli` 11 | 12 | ### Installation 13 | Clone the repository : 14 | ```sh 15 | git clone https://github.com/NicolasRoehm/angular-boilerplate.git 16 | cd angular-boilerplate 17 | npm install 18 | ``` 19 | 20 | ### ✒️ Usage 21 | - Rename `EasyAngular` and `easy-angular` with your project name 22 | - Place favicon generated with [RealFavIconGenerator](https://realfavicongenerator.net/) into `src/assets/img/favicon` folder 23 | 24 | ### Development server 25 | 26 | Run the following command for a development server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files. 27 | ```sh 28 | ng-serve 29 | ``` 30 | 31 | ## Boilerplate content 32 | ### 🗂️ Source code structure 33 | 34 | 35 | 36 | 39 | 62 | 63 |
37 | Project Structure 38 | 40 |
    41 |
  • Pages 42 |
      43 |
    • Auth (login, forgot password, validate account) with 2 possible layouts
    • 44 |
    • Home
    • 45 |
    • 404
    • 46 |
    47 |
  • 48 |
  • Shared components 49 |
      50 |
    • Blocks : toast & progress bar
    • 51 |
    • Forms : confirm
    • 52 |
    • Layouts : page & header
    • 53 |
    • Modals : wrapper
    • 54 |
    55 |
  • 56 |
  • Enums : endpoints / environments / storage keys
  • 57 |
  • Helpers : storage / string
  • 58 |
  • Services : app (for requests) / store (for state management using signals)
  • 59 |
  • i18n : en.json (for internationalization)
  • 60 |
61 |
64 | 65 | ### 🌐 Included packages 66 | - [Bootstrap 5](https://getbootstrap.com/) : SCSS style & [ng-bootstrap](https://ng-bootstrap.github.io/) components 67 | - [Axios](https://github.com/axios/axios) : HTTP client 68 | - [ArrayTyper](https://github.com/FranzStrudel/-caliatys-array-typer) : Utility for type-safe array operations 69 | - [angular-svg-icon](https://github.com/czeckd/angular-svg-icon) : SVG icon support 70 | - [ngx-translate](https://github.com/ngx-translate/core) : Internationalization library 71 | 72 | ## 🛠️ Tools 73 | - Generate models from JSON - https://app.quicktype.io/ 74 | - Generate favicon from SVG - https://realfavicongenerator.net/ 75 | 76 | ## Angular CLI commands 77 | ### Code scaffolding 78 | Generate a new component : 79 | ```sh 80 | ng generate component component-name 81 | ``` 82 | You can also use `ng generate` for directives, pipes, services, classes, guards, interfaces, enums, and modules. 83 | 84 | ### Build 85 | Build the project : 86 | ```sh 87 | ng build 88 | ``` 89 | The build artifacts will be stored in the `dist/` directory. 90 | 91 | ### Running tests 92 | #### Unit tests 93 | Run unit tests via Karma : 94 | ```sh 95 | ng test 96 | ``` 97 | 98 | #### End-to-End tests 99 | Run end-to-end tests via a platform of your choice. You need to add a package that implements end-to-end testing capabilities : 100 | ```sh 101 | ng e2e 102 | ``` 103 | 104 | ## Further help 105 | To get more help on the Angular CLI, use `ng help` or visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "EasyAngular": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | }, 12 | "@schematics/angular:application": { 13 | "strict": true 14 | } 15 | }, 16 | "root": "", 17 | "sourceRoot": "src", 18 | "prefix": "app", 19 | "architect": { 20 | "build": { 21 | "builder": "@angular-devkit/build-angular:browser", 22 | "options": { 23 | "outputPath": "dist/easy-angular", 24 | "index": "src/index.html", 25 | "main": "src/main.ts", 26 | "polyfills": "src/polyfills.ts", 27 | "tsConfig": "tsconfig.app.json", 28 | "inlineStyleLanguage": "scss", 29 | "assets": [ 30 | "src/assets/img/favicon/favicon.ico", 31 | "src/assets" 32 | ], 33 | "styles": [ 34 | "src/assets/scss/styles.scss" 35 | ], 36 | "scripts": [] 37 | }, 38 | "configurations": { 39 | "production": { 40 | "budgets": [ 41 | { 42 | "type": "initial", 43 | "maximumWarning": "500kb", 44 | "maximumError": "1mb" 45 | }, 46 | { 47 | "type": "anyComponentStyle", 48 | "maximumWarning": "2kb", 49 | "maximumError": "4kb" 50 | } 51 | ], 52 | "fileReplacements": [ 53 | { 54 | "replace": "src/environments/environment.ts", 55 | "with": "src/environments/environment.prod.ts" 56 | } 57 | ], 58 | "outputHashing": "all" 59 | }, 60 | "development": { 61 | "buildOptimizer": false, 62 | "optimization": false, 63 | "vendorChunk": true, 64 | "extractLicenses": false, 65 | "sourceMap": true, 66 | "namedChunks": true 67 | } 68 | }, 69 | "defaultConfiguration": "production" 70 | }, 71 | "serve": { 72 | "builder": "@angular-devkit/build-angular:dev-server", 73 | "configurations": { 74 | "production": { 75 | "buildTarget": "EasyAngular:build:production" 76 | }, 77 | "development": { 78 | "buildTarget": "EasyAngular:build:development" 79 | } 80 | }, 81 | "defaultConfiguration": "development" 82 | }, 83 | "extract-i18n": { 84 | "builder": "@angular-devkit/build-angular:extract-i18n", 85 | "options": { 86 | "buildTarget": "EasyAngular:build" 87 | } 88 | }, 89 | "test": { 90 | "builder": "@angular-devkit/build-angular:karma", 91 | "options": { 92 | "main": "src/test.ts", 93 | "polyfills": "src/polyfills.ts", 94 | "tsConfig": "tsconfig.spec.json", 95 | "karmaConfig": "karma.conf.js", 96 | "inlineStyleLanguage": "scss", 97 | "assets": [ 98 | "src/assets/img/favicon/favicon.ico", 99 | "src/assets" 100 | ], 101 | "styles": [ 102 | "src/assets/scss/styles.scss" 103 | ], 104 | "scripts": [] 105 | } 106 | } 107 | } 108 | } 109 | }, 110 | "cli": { 111 | "analytics": false 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/easy-angular'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easy-angular", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "add-page": "ng g m pages/page-name --routing && ng g component pages/page-name --skip-tests" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^18.0.1", 15 | "@angular/common": "^18.0.1", 16 | "@angular/compiler": "^18.0.1", 17 | "@angular/core": "^18.0.1", 18 | "@angular/forms": "^18.0.1", 19 | "@angular/localize": "^18.0.1", 20 | "@angular/platform-browser": "^18.0.1", 21 | "@angular/platform-browser-dynamic": "^18.0.1", 22 | "@angular/router": "^18.0.1", 23 | "@caliatys/array-typer": "^1.0.0", 24 | "@ng-bootstrap/ng-bootstrap": "^17.0.0", 25 | "@ngx-translate/core": "^15.0.0", 26 | "@ngx-translate/http-loader": "^8.0.0", 27 | "@popperjs/core": "^2.11.8", 28 | "angular-svg-icon": "^18.0.0", 29 | "axios": "^1.6.7", 30 | "bootstrap": "^5.3.3", 31 | "rxjs": "~7.8.1", 32 | "tslib": "^2.3.0", 33 | "zone.js": "~0.14.4" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "^18.0.2", 37 | "@angular/cli": "^18.0.2", 38 | "@angular/compiler-cli": "^18.0.1", 39 | "@types/jasmine": "~3.10.0", 40 | "@types/node": "^12.11.1", 41 | "jasmine-core": "~4.0.0", 42 | "karma": "~6.3.0", 43 | "karma-chrome-launcher": "~3.1.0", 44 | "karma-coverage": "~2.1.0", 45 | "karma-jasmine": "~4.0.0", 46 | "karma-jasmine-html-reporter": "~1.7.0", 47 | "typescript": "~5.4.5" 48 | } 49 | } -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | .scrollable-container { 2 | height: 100vh; 3 | display: block; 4 | .layout { 5 | display: flex; 6 | flex-direction: column; 7 | min-height: 100vh; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgIf } from '@angular/common'; 3 | import { Component } from '@angular/core'; 4 | import { OnInit } from '@angular/core'; 5 | import { RouterOutlet } from '@angular/router'; 6 | 7 | // Services 8 | import { StoreService } from '@services/store.service'; 9 | 10 | // Components 11 | import { ToastComponent } from '@blocks/toast/toast.component'; 12 | 13 | @Component({ 14 | selector : 'app-root', 15 | templateUrl : './app.component.html', 16 | styleUrls : ['./app.component.scss'], 17 | standalone : true, 18 | imports : [RouterOutlet, ToastComponent, NgIf] 19 | }) 20 | export class AppComponent implements OnInit 21 | { 22 | constructor 23 | ( 24 | public storeService : StoreService, 25 | ) 26 | { 27 | } 28 | 29 | // ------------------------------------------------------------------------------- 30 | // NOTE Init --------------------------------------------------------------------- 31 | // ------------------------------------------------------------------------------- 32 | 33 | public ngOnInit() : void 34 | { 35 | } 36 | 37 | // ------------------------------------------------------------------------------- 38 | // NOTE Actions ------------------------------------------------------------------ 39 | // ------------------------------------------------------------------------------- 40 | 41 | // ------------------------------------------------------------------------------- 42 | // NOTE Computed props ----------------------------------------------------------- 43 | // ------------------------------------------------------------------------------- 44 | 45 | // ------------------------------------------------------------------------------- 46 | // NOTE Helpers ------------------------------------------------------------------ 47 | // ------------------------------------------------------------------------------- 48 | 49 | // ------------------------------------------------------------------------------- 50 | // NOTE Requests ----------------------------------------------------------------- 51 | // ------------------------------------------------------------------------------- 52 | 53 | // ------------------------------------------------------------------------------- 54 | // NOTE Subscriptions ------------------------------------------------------------ 55 | // ------------------------------------------------------------------------------- 56 | } 57 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { DatePipe } from '@angular/common'; 3 | import { withFetch } from '@angular/common/http'; 4 | import { withInterceptorsFromDi } from '@angular/common/http'; 5 | import { provideHttpClient } from '@angular/common/http'; 6 | import { HttpClient } from '@angular/common/http'; 7 | import { ApplicationConfig } from '@angular/core'; 8 | import { importProvidersFrom } from '@angular/core'; 9 | import { BrowserModule } from '@angular/platform-browser'; 10 | import { provideClientHydration } from '@angular/platform-browser'; 11 | import { provideAnimations } from '@angular/platform-browser/animations'; 12 | import { provideRouter } from '@angular/router'; 13 | import { withInMemoryScrolling } from '@angular/router'; 14 | import { withRouterConfig } from '@angular/router'; 15 | 16 | // External modules 17 | import { TranslateModule } from '@ngx-translate/core'; 18 | import { TranslateLoader } from '@ngx-translate/core'; 19 | import { TranslateHttpLoader } from '@ngx-translate/http-loader'; 20 | import { AngularSvgIconModule } from 'angular-svg-icon'; 21 | 22 | // Internal modules 23 | import { environment } from '@env/environment'; 24 | import { routes } from './app.routes'; 25 | 26 | // Services 27 | import { AppService } from '@services/app.service'; 28 | import { StoreService } from '@services/store.service'; 29 | 30 | export function createTranslateLoader(http : HttpClient) 31 | { 32 | return new TranslateHttpLoader(http, './assets/i18n/', '.json'); 33 | } 34 | 35 | export const appConfig : ApplicationConfig = { 36 | providers : [ 37 | 38 | // Routing 39 | provideRouter( 40 | routes, 41 | withRouterConfig({ 42 | onSameUrlNavigation : 'reload', 43 | }), 44 | withInMemoryScrolling({ 45 | scrollPositionRestoration : 'enabled' 46 | }), 47 | ), 48 | 49 | importProvidersFrom( 50 | // Angular modules 51 | BrowserModule, 52 | 53 | // External modules 54 | TranslateModule.forRoot({ 55 | defaultLanguage : environment.defaultLanguage, 56 | loader : { 57 | provide : TranslateLoader, 58 | useFactory : (createTranslateLoader), 59 | deps : [HttpClient] 60 | } 61 | }), 62 | AngularSvgIconModule.forRoot(), 63 | 64 | // Internal modules 65 | ), 66 | 67 | // External modules 68 | 69 | // Services 70 | StoreService, 71 | AppService, 72 | 73 | // Pipes 74 | DatePipe, 75 | 76 | // Guards 77 | 78 | // Resolvers 79 | 80 | provideHttpClient(withFetch(), withInterceptorsFromDi()), 81 | provideAnimations(), 82 | provideClientHydration(), 83 | ] 84 | }; -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Routes } from '@angular/router'; 3 | 4 | export const routes : Routes = [ 5 | { 6 | path : 'auth', 7 | loadChildren : () => import('./pages/auth/auth.routes').then(m => m.routes), 8 | }, 9 | { 10 | path : 'home', 11 | loadComponent : () => import('./pages/home/home.component').then(m => m.HomeComponent), 12 | }, 13 | { path : '', redirectTo : '/home', pathMatch : 'full' }, 14 | { 15 | path : '**', 16 | loadComponent : () => import('./pages/not-found/not-found.component').then(m => m.NotFoundComponent), 17 | }, 18 | ]; -------------------------------------------------------------------------------- /src/app/pages/auth/auth.component-2.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /src/app/pages/auth/auth.component-2.scss: -------------------------------------------------------------------------------- 1 | // NOTE Header / Loading / Footer 2 | 3 | header, app-progress-bar, footer { 4 | z-index: 10; 5 | } 6 | 7 | header { 8 | top: 0; 9 | height: 56px; 10 | } 11 | 12 | app-progress-bar { 13 | top: 56px; 14 | } 15 | 16 | footer { 17 | bottom: 0; 18 | height: 40px; 19 | } 20 | 21 | // NOTE Layout 22 | 23 | .auth-component-container { 24 | display: flex; 25 | align-items: center; 26 | min-height: 100vh; 27 | padding-top: 60px; 28 | padding-bottom: 40px; 29 | .auth-component { 30 | width: 100%; 31 | max-width: 350px; 32 | padding: 15px; 33 | margin: auto; 34 | } 35 | } 36 | 37 | // NOTE Images 38 | 39 | #auth-svg { 40 | height: 40px; 41 | } 42 | -------------------------------------------------------------------------------- /src/app/pages/auth/auth.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | Login image 5 |
6 |
7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/app/pages/auth/auth.component.scss: -------------------------------------------------------------------------------- 1 | // NOTE Layout 2 | .row { 3 | min-height: 100vh; 4 | .col.img-wrapper { 5 | min-height: 100vh; 6 | overflow: hidden; 7 | img { 8 | object-fit: cover; 9 | width: 100%; 10 | height: 100%; 11 | position: relative; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/app/pages/auth/auth.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgIf } from '@angular/common'; 3 | import { AsyncPipe } from '@angular/common'; 4 | import { Component } from '@angular/core'; 5 | import { OnInit } from '@angular/core'; 6 | import { RouterOutlet } from '@angular/router'; 7 | 8 | // Internal modules 9 | import { environment } from '@env/environment'; 10 | 11 | // Services 12 | import { StoreService } from '@services/store.service'; 13 | 14 | // Components 15 | import { ProgressBarComponent } from '@blocks/progress-bar/progress-bar.component'; 16 | 17 | @Component({ 18 | selector : 'app-auth', 19 | templateUrl : './auth.component.html', 20 | styleUrls : ['./auth.component.scss'], 21 | standalone : true, 22 | imports : [NgIf, ProgressBarComponent, RouterOutlet, AsyncPipe] 23 | }) 24 | export class AuthComponent implements OnInit 25 | { 26 | // NOTE Component properties 27 | public appName : string = environment.appName; 28 | public appVersion : string = environment.version; 29 | 30 | constructor 31 | ( 32 | public storeService : StoreService, 33 | ) 34 | { 35 | 36 | } 37 | 38 | public ngOnInit() : void 39 | { 40 | } 41 | 42 | // ------------------------------------------------------------------------------- 43 | // NOTE Init --------------------------------------------------------------------- 44 | // ------------------------------------------------------------------------------- 45 | 46 | // ------------------------------------------------------------------------------- 47 | // NOTE Actions ------------------------------------------------------------------ 48 | // ------------------------------------------------------------------------------- 49 | 50 | // ------------------------------------------------------------------------------- 51 | // NOTE Computed props ----------------------------------------------------------- 52 | // ------------------------------------------------------------------------------- 53 | 54 | // ------------------------------------------------------------------------------- 55 | // NOTE Helpers ------------------------------------------------------------------ 56 | // ------------------------------------------------------------------------------- 57 | 58 | // ------------------------------------------------------------------------------- 59 | // NOTE Requests ----------------------------------------------------------------- 60 | // ------------------------------------------------------------------------------- 61 | 62 | // ------------------------------------------------------------------------------- 63 | // NOTE Subscriptions ------------------------------------------------------------ 64 | // ------------------------------------------------------------------------------- 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/app/pages/auth/auth.routes.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Routes } from '@angular/router'; 3 | 4 | export const routes : Routes = [ 5 | { 6 | path : '', 7 | children : [ 8 | { 9 | path : '', 10 | redirectTo : 'login', 11 | pathMatch : 'full', 12 | }, 13 | { 14 | path : 'login', 15 | loadComponent : () => import('./login/login.component').then(m => m.LoginComponent), 16 | }, 17 | { 18 | path : 'forgot-password', 19 | loadComponent : () => import('./forgot-password/forgot-password.component').then(m => m.ForgotPasswordComponent), 20 | }, 21 | { 22 | path : 'validate-account', 23 | loadComponent : () => import('./validate-account/validate-account.component').then(m => m.ValidateAccountComponent), 24 | }, 25 | ] 26 | } 27 | ]; -------------------------------------------------------------------------------- /src/app/pages/auth/forgot-password/forgot-password.component.html: -------------------------------------------------------------------------------- 1 |
2 |
{{ 'FORGOT_YOUR_PWD' | translate }}
3 |
4 | 5 |
6 |
7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | 17 | 18 |
19 | {{ 'FIELD_REQUIRED' | translate }} 20 |
21 |
22 | {{ 'FIELD_EMAIL' | translate }} 23 |
24 |
25 | 26 |
27 | 30 |
31 | 32 | 35 | 36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /src/app/pages/auth/forgot-password/forgot-password.component.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | max-width: 350px; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/pages/auth/forgot-password/forgot-password.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgClass } from '@angular/common'; 3 | import { NgIf } from '@angular/common'; 4 | import { Component } from '@angular/core'; 5 | import { FormGroup } from '@angular/forms'; 6 | import { FormsModule } from '@angular/forms'; 7 | import { ReactiveFormsModule } from '@angular/forms'; 8 | import { FormControl } from '@angular/forms'; 9 | import { Validators } from '@angular/forms'; 10 | import { Router } from '@angular/router'; 11 | import { RouterLink } from '@angular/router'; 12 | 13 | // External modules 14 | import { TranslateModule } from '@ngx-translate/core'; 15 | 16 | // Services 17 | import { AppService } from '@services/app.service'; 18 | import { StoreService } from '@services/store.service'; 19 | 20 | @Component({ 21 | selector : 'app-forgot-password', 22 | templateUrl : './forgot-password.component.html', 23 | styleUrls : ['./forgot-password.component.scss'], 24 | standalone : true, 25 | imports : [FormsModule, ReactiveFormsModule, NgClass, NgIf, RouterLink, TranslateModule] 26 | }) 27 | export class ForgotPasswordComponent 28 | { 29 | public formGroup !: FormGroup<{ 30 | email : FormControl 31 | }>; 32 | 33 | constructor 34 | ( 35 | public router : Router, 36 | private storeService : StoreService, 37 | private appService : AppService, 38 | ) 39 | { 40 | this.initFormGroup(); 41 | } 42 | 43 | // ------------------------------------------------------------------------------- 44 | // NOTE Init --------------------------------------------------------------------- 45 | // ------------------------------------------------------------------------------- 46 | 47 | private initFormGroup() : void 48 | { 49 | this.formGroup = new FormGroup({ 50 | email : new FormControl({ 51 | value : '', 52 | disabled : false 53 | }, { validators : [Validators.required, Validators.email], nonNullable : true }), 54 | }); 55 | } 56 | 57 | // ------------------------------------------------------------------------------- 58 | // NOTE Actions ------------------------------------------------------------------ 59 | // ------------------------------------------------------------------------------- 60 | 61 | public async onClickSubmit() : Promise 62 | { 63 | await this.forgotPassword(); 64 | } 65 | 66 | // ------------------------------------------------------------------------------- 67 | // NOTE Requests ----------------------------------------------------------------- 68 | // ------------------------------------------------------------------------------- 69 | 70 | private async forgotPassword() : Promise 71 | { 72 | this.storeService.isLoading.set(true); 73 | 74 | const email = this.formGroup.controls.email.getRawValue(); 75 | const success = await this.appService.forgotPassword(email); 76 | 77 | this.storeService.isLoading.set(false); 78 | 79 | if (!success) 80 | return; 81 | 82 | // NOTE Redirect to validate account 83 | this.router.navigate(['/auth/validate-account']); 84 | } 85 | 86 | // ------------------------------------------------------------------------------- 87 | // NOTE Helpers ------------------------------------------------------------------ 88 | // ------------------------------------------------------------------------------- 89 | } 90 | -------------------------------------------------------------------------------- /src/app/pages/auth/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 |
{{ 'WELCOME_TO' | translate }}
3 |
{{ appName }}
4 |
{{ 'PROJECT_DESC' | translate }}
5 |
6 | 7 |
8 |
9 | 10 | 11 |
12 | 13 | 14 |
15 | 16 | 19 | 20 |
21 | {{ 'FIELD_REQUIRED' | translate }} 22 |
23 |
24 | {{ 'FIELD_EMAIL' | translate }} 25 |
26 |
27 | 28 | 29 |
30 | 31 | 34 | 35 |
36 | {{ 'FIELD_REQUIRED' | translate }} 37 |
38 |
39 | 40 | 43 | 44 |
45 | 48 |
49 | 50 |
51 |
52 |
53 | -------------------------------------------------------------------------------- /src/app/pages/auth/login/login.component.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | max-width: 350px; 3 | width: 100%; 4 | } -------------------------------------------------------------------------------- /src/app/pages/auth/login/login.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgClass } from '@angular/common'; 3 | import { NgIf } from '@angular/common'; 4 | import { Component } from '@angular/core'; 5 | import { FormGroup } from '@angular/forms'; 6 | import { FormsModule } from '@angular/forms'; 7 | import { ReactiveFormsModule } from '@angular/forms'; 8 | import { FormControl } from '@angular/forms'; 9 | import { Validators } from '@angular/forms'; 10 | import { Router } from '@angular/router'; 11 | import { RouterLink } from '@angular/router'; 12 | 13 | // External modules 14 | import { TranslateModule } from '@ngx-translate/core'; 15 | 16 | // Internal modules 17 | import { environment } from '@env/environment'; 18 | 19 | // Services 20 | import { AppService } from '@services/app.service'; 21 | import { StoreService } from '@services/store.service'; 22 | 23 | @Component({ 24 | selector : 'app-login', 25 | templateUrl : './login.component.html', 26 | styleUrls : ['./login.component.scss'], 27 | standalone : true, 28 | imports : [FormsModule, ReactiveFormsModule, NgClass, NgIf, RouterLink, TranslateModule] 29 | }) 30 | export class LoginComponent 31 | { 32 | public appName : string = environment.appName; 33 | public formGroup !: FormGroup<{ 34 | email : FormControl, 35 | password : FormControl, 36 | }>; 37 | 38 | constructor 39 | ( 40 | private router : Router, 41 | private storeService : StoreService, 42 | private appService : AppService, 43 | ) 44 | { 45 | this.initFormGroup(); 46 | } 47 | 48 | // ------------------------------------------------------------------------------- 49 | // NOTE Init --------------------------------------------------------------------- 50 | // ------------------------------------------------------------------------------- 51 | 52 | private initFormGroup() : void 53 | { 54 | this.formGroup = new FormGroup({ 55 | email : new FormControl({ 56 | value : '', 57 | disabled : false 58 | }, { validators : [Validators.required, Validators.email], nonNullable : true }), 59 | password : new FormControl({ 60 | value : '', 61 | disabled : false 62 | }, { validators : [Validators.required], nonNullable : true }) 63 | }); 64 | } 65 | 66 | // ------------------------------------------------------------------------------- 67 | // NOTE Actions ------------------------------------------------------------------ 68 | // ------------------------------------------------------------------------------- 69 | 70 | public async onClickSubmit() : Promise 71 | { 72 | await this.authenticate(); 73 | } 74 | 75 | // ------------------------------------------------------------------------------- 76 | // NOTE Requests ----------------------------------------------------------------- 77 | // ------------------------------------------------------------------------------- 78 | 79 | private async authenticate() : Promise 80 | { 81 | this.storeService.isLoading.set(true); 82 | 83 | const email = this.formGroup.controls.email.getRawValue(); 84 | const password = this.formGroup.controls.password.getRawValue(); 85 | const success = await this.appService.authenticate(email, password); 86 | 87 | this.storeService.isLoading.set(false); 88 | 89 | if (!success) 90 | return; 91 | 92 | // NOTE Redirect to home 93 | this.router.navigate(['/home']); 94 | } 95 | 96 | // ------------------------------------------------------------------------------- 97 | // NOTE Helpers ------------------------------------------------------------------ 98 | // ------------------------------------------------------------------------------- 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/app/pages/auth/validate-account/validate-account.component.html: -------------------------------------------------------------------------------- 1 |
2 |
{{ 'VALIDATE_ACCOUNT' | translate }}
3 |
4 | 5 |
6 |
7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | 17 | 18 |
19 | {{ 'FIELD_REQUIRED' | translate }} 20 |
21 |
22 | 23 |
24 | 27 |
28 | 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /src/app/pages/auth/validate-account/validate-account.component.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | max-width: 350px; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/pages/auth/validate-account/validate-account.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgClass } from '@angular/common'; 3 | import { NgIf } from '@angular/common'; 4 | import { OnInit } from '@angular/core'; 5 | import { Component } from '@angular/core'; 6 | import { FormGroup } from '@angular/forms'; 7 | import { FormsModule } from '@angular/forms'; 8 | import { ReactiveFormsModule } from '@angular/forms'; 9 | import { FormControl } from '@angular/forms'; 10 | import { Validators } from '@angular/forms'; 11 | import { ActivatedRoute } from '@angular/router'; 12 | import { Router } from '@angular/router'; 13 | import { Params } from '@angular/router'; 14 | 15 | // External modules 16 | import { TranslateModule } from '@ngx-translate/core'; 17 | 18 | // Internal modules 19 | import { environment } from '@env/environment'; 20 | 21 | // Services 22 | import { AppService } from '@services/app.service'; 23 | import { StoreService } from '@services/store.service'; 24 | 25 | @Component({ 26 | selector : 'app-validate-account', 27 | templateUrl : './validate-account.component.html', 28 | styleUrls : ['./validate-account.component.scss'], 29 | standalone : true, 30 | imports : [FormsModule, ReactiveFormsModule, NgClass, NgIf, TranslateModule] 31 | }) 32 | export class ValidateAccountComponent implements OnInit 33 | { 34 | public formGroup !: FormGroup<{ 35 | password : FormControl 36 | }>; 37 | private tokenFromUrl : string = ''; 38 | 39 | constructor 40 | ( 41 | private router : Router, 42 | private storeService : StoreService, 43 | private activatedRoute : ActivatedRoute, 44 | private appService : AppService, 45 | ) 46 | { 47 | this.initFormGroup(); 48 | } 49 | 50 | public async ngOnInit() : Promise 51 | { 52 | // NOTE Get token from URL 53 | this.activatedRoute.queryParams.subscribe((params : Params) => 54 | { 55 | this.tokenFromUrl = params['token']; 56 | if (!environment.production) 57 | console.log('ValidateAccountComponent : ngOnInit -> Token : ', this.tokenFromUrl); 58 | }); 59 | } 60 | 61 | // ------------------------------------------------------------------------------- 62 | // NOTE Init --------------------------------------------------------------------- 63 | // ------------------------------------------------------------------------------- 64 | 65 | private initFormGroup() : void 66 | { 67 | this.formGroup = new FormGroup({ 68 | password : new FormControl({ 69 | value : '', 70 | disabled : false 71 | }, { validators : [Validators.required], nonNullable : true }), 72 | }); 73 | } 74 | 75 | // ------------------------------------------------------------------------------- 76 | // NOTE Actions ------------------------------------------------------------------ 77 | // ------------------------------------------------------------------------------- 78 | 79 | public async onClickSubmit() : Promise 80 | { 81 | if (!this.tokenFromUrl) 82 | return; 83 | 84 | await this.validateNewAccount(); 85 | } 86 | 87 | // ------------------------------------------------------------------------------- 88 | // NOTE Requests ----------------------------------------------------------------- 89 | // ------------------------------------------------------------------------------- 90 | 91 | private async validateNewAccount() : Promise 92 | { 93 | this.storeService.isLoading.set(true); 94 | 95 | const password = this.formGroup.controls.password.getRawValue(); 96 | const success = await this.appService.validateAccount(this.tokenFromUrl, password); 97 | 98 | this.storeService.isLoading.set(false); 99 | 100 | if (!success) 101 | return; 102 | 103 | // NOTE Redirect to home 104 | this.router.navigate(['/home']); 105 | } 106 | 107 | // ------------------------------------------------------------------------------- 108 | // NOTE Helpers ------------------------------------------------------------------ 109 | // ------------------------------------------------------------------------------- 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/app/pages/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 |
8 | Let's start the project! 9 |
10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /src/app/pages/home/home.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/app/pages/home/home.component.scss -------------------------------------------------------------------------------- /src/app/pages/home/home.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgIf } from '@angular/common'; 3 | import { Component } from '@angular/core'; 4 | import { OnInit } from '@angular/core'; 5 | 6 | // Services 7 | import { StoreService } from '@services/store.service'; 8 | 9 | // Components 10 | import { ProgressBarComponent } from '@blocks/progress-bar/progress-bar.component'; 11 | import { PageLayoutComponent } from '@layouts/page-layout/page-layout.component'; 12 | 13 | @Component({ 14 | selector : 'app-home', 15 | templateUrl : './home.component.html', 16 | styleUrls : ['./home.component.scss'], 17 | standalone : true, 18 | imports : [PageLayoutComponent, NgIf, ProgressBarComponent] 19 | }) 20 | export class HomeComponent implements OnInit 21 | { 22 | constructor 23 | ( 24 | public storeService : StoreService 25 | ) 26 | { } 27 | 28 | // ------------------------------------------------------------------------------- 29 | // NOTE Init --------------------------------------------------------------------- 30 | // ------------------------------------------------------------------------------- 31 | 32 | public ngOnInit() : void 33 | { 34 | setTimeout(_ => 35 | { 36 | this.storeService.isLoading.set(false); 37 | }, 2000); 38 | } 39 | 40 | // ------------------------------------------------------------------------------- 41 | // NOTE Actions ------------------------------------------------------------------ 42 | // ------------------------------------------------------------------------------- 43 | 44 | // ------------------------------------------------------------------------------- 45 | // NOTE Computed props ----------------------------------------------------------- 46 | // ------------------------------------------------------------------------------- 47 | 48 | // ------------------------------------------------------------------------------- 49 | // NOTE Helpers ------------------------------------------------------------------ 50 | // ------------------------------------------------------------------------------- 51 | 52 | // ------------------------------------------------------------------------------- 53 | // NOTE Requests ----------------------------------------------------------------- 54 | // ------------------------------------------------------------------------------- 55 | 56 | // ------------------------------------------------------------------------------- 57 | // NOTE Subscriptions ------------------------------------------------------------ 58 | // ------------------------------------------------------------------------------- 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/app/pages/not-found/not-found.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ 'NOT_FOUND' | translate }}

4 | 5 | {{ 'GO_TO_HOMEPAGE' | translate }} 6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/pages/not-found/not-found.component.scss: -------------------------------------------------------------------------------- 1 | .not-found-container { 2 | display: flex; 3 | align-items: center; 4 | min-height: 100vh; 5 | position: relative; 6 | .not-found { 7 | width: 100%; 8 | } 9 | } -------------------------------------------------------------------------------- /src/app/pages/not-found/not-found.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Component } from '@angular/core'; 3 | import { RouterLink } from '@angular/router'; 4 | 5 | // External modules 6 | import { TranslateModule } from '@ngx-translate/core'; 7 | 8 | @Component({ 9 | selector : 'app-not-found', 10 | templateUrl : './not-found.component.html', 11 | styleUrls : ['./not-found.component.scss'], 12 | standalone : true, 13 | imports : [RouterLink, TranslateModule] 14 | }) 15 | export class NotFoundComponent 16 | { 17 | constructor() { } 18 | 19 | // ------------------------------------------------------------------------------- 20 | // NOTE Init --------------------------------------------------------------------- 21 | // ------------------------------------------------------------------------------- 22 | 23 | // ------------------------------------------------------------------------------- 24 | // NOTE Actions ------------------------------------------------------------------ 25 | // ------------------------------------------------------------------------------- 26 | 27 | // ------------------------------------------------------------------------------- 28 | // NOTE Computed props ----------------------------------------------------------- 29 | // ------------------------------------------------------------------------------- 30 | 31 | // ------------------------------------------------------------------------------- 32 | // NOTE Helpers ------------------------------------------------------------------ 33 | // ------------------------------------------------------------------------------- 34 | 35 | // ------------------------------------------------------------------------------- 36 | // NOTE Requests ----------------------------------------------------------------- 37 | // ------------------------------------------------------------------------------- 38 | 39 | // ------------------------------------------------------------------------------- 40 | // NOTE Subscriptions ------------------------------------------------------------ 41 | // ------------------------------------------------------------------------------- 42 | } 43 | -------------------------------------------------------------------------------- /src/app/shared/components/blocks/progress-bar/progress-bar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |
7 |
8 |
9 | 10 |
{{ 'LOADING' | translate }}...
-------------------------------------------------------------------------------- /src/app/shared/components/blocks/progress-bar/progress-bar.component.scss: -------------------------------------------------------------------------------- 1 | .slider { 2 | position: relative; 3 | width: 100%; 4 | height: 5px; 5 | overflow-x: hidden; 6 | .line { 7 | position: absolute; 8 | opacity: 0.4; 9 | background: #4a8df8; 10 | width: 150%; 11 | height: 5px; 12 | } 13 | .subline { 14 | position: absolute; 15 | background: #4a8df8; 16 | height: 5px; 17 | &.inc { 18 | animation: increase 2s infinite; 19 | } 20 | &.dec { 21 | animation: decrease 2s 0.5s infinite; 22 | } 23 | } 24 | } 25 | 26 | @keyframes increase { 27 | from { left: -5%; width: 5%; } 28 | to { left: 130%; width: 100%;} 29 | } 30 | @keyframes decrease { 31 | from { left: -80%; width: 80%; } 32 | to { left: 110%; width: 10%;} 33 | } -------------------------------------------------------------------------------- /src/app/shared/components/blocks/progress-bar/progress-bar.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgIf } from '@angular/common'; 3 | import { Component } from '@angular/core'; 4 | import { Input } from '@angular/core'; 5 | import { OnInit } from '@angular/core'; 6 | 7 | // External modules 8 | import { TranslateModule } from '@ngx-translate/core'; 9 | 10 | @Component({ 11 | selector : 'app-progress-bar', 12 | templateUrl : './progress-bar.component.html', 13 | styleUrls : ['./progress-bar.component.scss'], 14 | standalone : true, 15 | imports : [NgIf, TranslateModule] 16 | }) 17 | export class ProgressBarComponent implements OnInit 18 | { 19 | @Input() withLabel : boolean = false; 20 | 21 | constructor() { } 22 | 23 | public ngOnInit() : void 24 | { 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/app/shared/components/blocks/toast/toast.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 |
{{ toast.headerKey | translate }}
13 |
14 |
{{ toast.body }}
15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 26 |
{{ toast.body }}
27 |
28 | 29 |
30 | 31 |
32 | -------------------------------------------------------------------------------- /src/app/shared/components/blocks/toast/toast.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | position: fixed; 3 | bottom: 0; 4 | right: 0; 5 | margin: 0.5em; 6 | z-index: 1200; 7 | } -------------------------------------------------------------------------------- /src/app/shared/components/blocks/toast/toast.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgFor } from '@angular/common'; 3 | import { NgIf } from '@angular/common'; 4 | import { Component } from '@angular/core'; 5 | 6 | // External modules 7 | import { NgbToast } from '@ng-bootstrap/ng-bootstrap'; 8 | import { NgbToastHeader } from '@ng-bootstrap/ng-bootstrap'; 9 | import { TranslateModule } from '@ngx-translate/core'; 10 | 11 | // Internal modules 12 | import { ToastManager } from './toast.manager'; 13 | 14 | @Component({ 15 | selector : 'app-toast', 16 | templateUrl : './toast.component.html', 17 | styleUrls : ['./toast.component.scss'], 18 | standalone : true, 19 | imports : [NgFor, NgIf, NgbToast, NgbToastHeader, TranslateModule] 20 | }) 21 | export class ToastComponent 22 | { 23 | 24 | constructor(public toastManager : ToastManager) {} 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/app/shared/components/blocks/toast/toast.manager.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Injectable } from '@angular/core'; 3 | 4 | export type ToastType = 'success' | 'info' | 'warning' | 'danger'; 5 | 6 | export class Toast 7 | { 8 | public id !: number 9 | readonly headerKey ?: string; 10 | public withHeader : boolean; 11 | public body : string; 12 | public type : ToastType; 13 | public autoHide : boolean; 14 | public delay : number; 15 | 16 | constructor(body : string, type ?: ToastType, autoHide : boolean = false) 17 | { 18 | this.withHeader = true; 19 | this.body = body; 20 | this.type = type ?? 'danger'; 21 | this.autoHide = autoHide; 22 | this.delay = 10000; // 10 sec 23 | 24 | this.headerKey = this.type.toUpperCase(); 25 | } 26 | } 27 | 28 | @Injectable({ providedIn : 'root' }) 29 | export class ToastManager 30 | { 31 | public toasts : Toast[] = []; 32 | private counter : number = 0; 33 | 34 | constructor() {} 35 | 36 | public show(toast : Toast) : void 37 | { 38 | toast.id = this.counter++; 39 | this.toasts.push(toast); 40 | } 41 | 42 | public quickShow(body : string, type ?: ToastType, autoHide : boolean = false) : void 43 | { 44 | const toast = new Toast(body, type, autoHide); 45 | this.show(toast); 46 | } 47 | 48 | public remove(id : number) : void 49 | { 50 | this.toasts = this.toasts.filter(t => t.id !== id); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/shared/components/forms/form-confirm/form-confirm.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |

{{ 'ALERT_DELETE_ENTRY' | translate }}

6 |
7 | 8 | 9 |
10 | 13 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /src/app/shared/components/forms/form-confirm/form-confirm.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/app/shared/components/forms/form-confirm/form-confirm.component.scss -------------------------------------------------------------------------------- /src/app/shared/components/forms/form-confirm/form-confirm.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Component } from '@angular/core'; 3 | import { OnInit } from '@angular/core'; 4 | import { Input } from '@angular/core'; 5 | import { Output } from '@angular/core'; 6 | import { EventEmitter } from '@angular/core'; 7 | import { FormsModule } from '@angular/forms'; 8 | 9 | // External modules 10 | import { TranslateModule } from '@ngx-translate/core'; 11 | 12 | @Component({ 13 | selector : 'app-form-confirm', 14 | templateUrl : './form-confirm.component.html', 15 | styleUrls : ['./form-confirm.component.scss'], 16 | standalone : true, 17 | imports : [FormsModule, TranslateModule] 18 | }) 19 | export class FormConfirmComponent implements OnInit 20 | { 21 | @Input() data : any; 22 | @Output() submitData : EventEmitter = new EventEmitter(); 23 | @Output() submitClose : EventEmitter = new EventEmitter(); 24 | 25 | constructor() { } 26 | 27 | public ngOnInit() : void 28 | { 29 | } 30 | 31 | // ------------------------------------------------------------------------------- 32 | // NOTE Action ------------------------------------------------------------------- 33 | // ------------------------------------------------------------------------------- 34 | 35 | public async onClickSubmit() : Promise 36 | { 37 | this.submitData.emit(true); 38 | } 39 | 40 | public onClickClose() : void 41 | { 42 | this.submitClose.emit(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/app/shared/components/layouts/layout-header/layout-header.component.html: -------------------------------------------------------------------------------- 1 | 2 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/app/shared/components/layouts/layout-header/layout-header.component.scss: -------------------------------------------------------------------------------- 1 | .navbar-brand { 2 | img { 3 | width: 38px; 4 | display: inline-block; 5 | vertical-align: middle; 6 | } 7 | .title-wrapper { 8 | vertical-align: middle; 9 | display: inline-block; 10 | border-left: 1px solid #adadad; 11 | padding-left: 15px; 12 | margin-left: 15px; 13 | span { 14 | display: block; 15 | &.subtitle { 16 | font-size: 71%; 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/app/shared/components/layouts/layout-header/layout-header.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Component } from '@angular/core'; 3 | import { OnInit } from '@angular/core'; 4 | import { Router } from '@angular/router'; 5 | import { RouterLink } from '@angular/router'; 6 | import { RouterLinkActive } from '@angular/router'; 7 | 8 | // External modules 9 | import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; 10 | import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; 11 | import { NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'; 12 | import { NgbDropdownMenu } from '@ng-bootstrap/ng-bootstrap'; 13 | import { TranslateModule } from '@ngx-translate/core'; 14 | 15 | // Internal modules 16 | import { environment } from '@env/environment'; 17 | 18 | @Component({ 19 | selector : 'app-layout-header', 20 | templateUrl : './layout-header.component.html', 21 | styleUrls : ['./layout-header.component.scss'], 22 | standalone : true, 23 | imports : [RouterLink, NgbCollapse, RouterLinkActive, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, TranslateModule] 24 | }) 25 | export class LayoutHeaderComponent implements OnInit 26 | { 27 | public appName : string = environment.appName; 28 | public isMenuCollapsed : boolean = true; 29 | 30 | constructor 31 | ( 32 | private router : Router, 33 | ) 34 | { 35 | 36 | } 37 | 38 | public ngOnInit() : void 39 | { 40 | 41 | } 42 | 43 | // ------------------------------------------------------------------------------- 44 | // NOTE Init --------------------------------------------------------------------- 45 | // ------------------------------------------------------------------------------- 46 | 47 | // ------------------------------------------------------------------------------- 48 | // NOTE Actions ------------------------------------------------------------------ 49 | // ------------------------------------------------------------------------------- 50 | 51 | public async onClickLogout() : Promise 52 | { 53 | // NOTE Redirect to login 54 | this.router.navigate(['/auth/login']); 55 | } 56 | 57 | // ------------------------------------------------------------------------------- 58 | // NOTE Computed props ----------------------------------------------------------- 59 | // ------------------------------------------------------------------------------- 60 | 61 | // ------------------------------------------------------------------------------- 62 | // NOTE Helpers ------------------------------------------------------------------ 63 | // ------------------------------------------------------------------------------- 64 | 65 | // ------------------------------------------------------------------------------- 66 | // NOTE Requests ----------------------------------------------------------------- 67 | // ------------------------------------------------------------------------------- 68 | 69 | // ------------------------------------------------------------------------------- 70 | // NOTE Subscriptions ------------------------------------------------------------ 71 | // ------------------------------------------------------------------------------- 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/app/shared/components/layouts/page-layout/page-layout.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/shared/components/layouts/page-layout/page-layout.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/app/shared/components/layouts/page-layout/page-layout.component.scss -------------------------------------------------------------------------------- /src/app/shared/components/layouts/page-layout/page-layout.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Component } from '@angular/core'; 3 | import { OnInit } from '@angular/core'; 4 | 5 | // Components 6 | import { LayoutHeaderComponent } from '../layout-header/layout-header.component'; 7 | 8 | @Component({ 9 | selector : 'app-page-layout', 10 | templateUrl : './page-layout.component.html', 11 | styleUrls : ['./page-layout.component.scss'], 12 | standalone : true, 13 | imports : [LayoutHeaderComponent] 14 | }) 15 | export class PageLayoutComponent implements OnInit 16 | { 17 | constructor() 18 | { 19 | 20 | } 21 | 22 | public ngOnInit() : void 23 | { 24 | 25 | } 26 | 27 | // ------------------------------------------------------------------------------- 28 | // NOTE Init --------------------------------------------------------------------- 29 | // ------------------------------------------------------------------------------- 30 | 31 | // ------------------------------------------------------------------------------- 32 | // NOTE Actions ------------------------------------------------------------------ 33 | // ------------------------------------------------------------------------------- 34 | 35 | // ------------------------------------------------------------------------------- 36 | // NOTE Computed props ----------------------------------------------------------- 37 | // ------------------------------------------------------------------------------- 38 | 39 | // ------------------------------------------------------------------------------- 40 | // NOTE Helpers ------------------------------------------------------------------ 41 | // ------------------------------------------------------------------------------- 42 | 43 | // ------------------------------------------------------------------------------- 44 | // NOTE Requests ----------------------------------------------------------------- 45 | // ------------------------------------------------------------------------------- 46 | 47 | // ------------------------------------------------------------------------------- 48 | // NOTE Subscriptions ------------------------------------------------------------ 49 | // ------------------------------------------------------------------------------- 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/app/shared/components/modals/modal-wrapper/modal-wrapper.component.html: -------------------------------------------------------------------------------- 1 | 7 | 13 | -------------------------------------------------------------------------------- /src/app/shared/components/modals/modal-wrapper/modal-wrapper.component.scss: -------------------------------------------------------------------------------- 1 | .modal-header { 2 | background: var(--bs-info); 3 | &, button { 4 | color: white; 5 | } 6 | .modal-title { 7 | text-transform: uppercase; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/shared/components/modals/modal-wrapper/modal-wrapper.component.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { NgIf } from '@angular/common'; 3 | import { Component } from '@angular/core'; 4 | import { Input } from '@angular/core'; 5 | import { OnInit } from '@angular/core'; 6 | import { ViewChild } from '@angular/core'; 7 | import { EventEmitter } from '@angular/core'; 8 | import { Type } from '@angular/core'; 9 | 10 | // External modules 11 | import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; 12 | 13 | // Directives 14 | import { ModalWrapperDirective as ModalWrapperDirective_1 } from '../../../directives/modal-wrapper.directive'; 15 | import { ModalWrapperDirective } from '@directives/modal-wrapper.directive'; 16 | 17 | // Dynamic component loader - Angular 18 | // https://angular.io/guide/dynamic-component-loader 19 | // https://stackblitz.com/angular/bydvgpxaabj?file=src%2Fapp%2Fad-banner.component.ts 20 | 21 | export class ModalForm 22 | { 23 | constructor(public component : Type, public data : any) {} 24 | } 25 | 26 | export interface FormComponent 27 | { 28 | data : any; 29 | submitData : EventEmitter; 30 | submitClose : EventEmitter; 31 | } 32 | 33 | @Component({ 34 | selector : 'app-modal-wrapper', 35 | templateUrl : 'modal-wrapper.component.html', 36 | styleUrls : ['modal-wrapper.component.scss'], 37 | standalone : true, 38 | imports : [NgIf, ModalWrapperDirective_1] 39 | }) 40 | export class ModalWrapperComponent implements OnInit 41 | { 42 | @Input() component : any; 43 | @Input() componentData : any; 44 | @Input() modalData : { 45 | title : string, 46 | headerClasses : string, 47 | closable : boolean, 48 | } = { 49 | title : '', 50 | headerClasses : '', 51 | closable : true, 52 | }; 53 | 54 | @ViewChild(ModalWrapperDirective, { static : true }) modalWrapperHost !: ModalWrapperDirective; 55 | 56 | constructor(public activeModal : NgbActiveModal) 57 | { 58 | } 59 | 60 | public ngOnInit() : void 61 | { 62 | this.loadComponent(); 63 | } 64 | 65 | // ------------------------------------------------------------------------------- 66 | // NOTE Init --------------------------------------------------------------------- 67 | // ------------------------------------------------------------------------------- 68 | 69 | private loadComponent() : void 70 | { 71 | const modalForm = new ModalForm(this.component, this.componentData); 72 | 73 | const viewContainerRef = this.modalWrapperHost.viewContainerRef; 74 | viewContainerRef.clear(); 75 | 76 | const componentRef = viewContainerRef.createComponent(modalForm.component); 77 | 78 | (componentRef.instance).data = modalForm.data; 79 | (componentRef.instance).submitClose.subscribe(() => 80 | { 81 | this.submitClose(); 82 | }); 83 | (componentRef.instance).submitData.subscribe(event => 84 | { 85 | this.submitData(event); 86 | }); 87 | } 88 | 89 | // ------------------------------------------------------------------------------- 90 | // NOTE Helpers ------------------------------------------------------------------ 91 | // ------------------------------------------------------------------------------- 92 | 93 | private submitData($event : any) : void 94 | { 95 | this.activeModal.close($event); 96 | } 97 | 98 | private submitClose() : void 99 | { 100 | this.activeModal.close(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/app/shared/directives/modal-wrapper.directive.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Directive } from '@angular/core'; 3 | import { ViewContainerRef } from '@angular/core'; 4 | 5 | @Directive({ 6 | selector : '[modal-wrapper-host]', 7 | standalone : true, 8 | }) 9 | export class ModalWrapperDirective 10 | { 11 | constructor(public viewContainerRef : ViewContainerRef) { } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/shared/enums/endpoint.enum.ts: -------------------------------------------------------------------------------- 1 | export enum Endpoint 2 | { 3 | AUTHENTICATE = 'authenticate', 4 | FORGOT_PASSWORD = 'forgot-password', 5 | VALIDATE_ACCOUNT = 'validate-account', 6 | } 7 | -------------------------------------------------------------------------------- /src/app/shared/enums/environment.enum.ts: -------------------------------------------------------------------------------- 1 | export enum EnvName 2 | { 3 | LOCAL = 'local', 4 | PROD = 'production', 5 | } 6 | -------------------------------------------------------------------------------- /src/app/shared/enums/storage-key.enum.ts: -------------------------------------------------------------------------------- 1 | export enum StorageKey 2 | { 3 | TOKEN = 'Token', 4 | } 5 | -------------------------------------------------------------------------------- /src/app/shared/helpers/storage.helper.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Injectable } from '@angular/core'; 3 | 4 | // Internal modules 5 | import { environment } from '@env/environment'; 6 | 7 | // Enums 8 | import { StorageKey } from '@enums/storage-key.enum'; 9 | 10 | // Models 11 | // import { AuthResponse } from '@models/auth-response.model'; 12 | 13 | @Injectable() 14 | export class StorageHelper 15 | { 16 | private static storagePrefix : string = environment.appName + '_' + environment.version + '_'; 17 | 18 | // ---------------------------------------------------------------------------------------------- 19 | // SECTION Methods ------------------------------------------------------------------------------ 20 | // ---------------------------------------------------------------------------------------------- 21 | 22 | // NOTE Token 23 | 24 | // public static setToken(user : AuthResponse) : void 25 | // { 26 | // StorageHelper.setItem(StorageKey.TOKEN, user); 27 | // } 28 | 29 | // public static removeToken() : void 30 | // { 31 | // StorageHelper.removeItem(StorageKey.TOKEN); 32 | // } 33 | 34 | // public static getToken() : AuthResponse | null 35 | // { 36 | // // Prevent SSR error 37 | // if (typeof localStorage === 'undefined') 38 | // return undefined; 39 | // const data = StorageHelper.getItem(StorageKey.TOKEN); 40 | // return data ? new AuthResponse(data) : null; 41 | // } 42 | 43 | // !SECTION Methods 44 | 45 | // ---------------------------------------------------------------------------------------------- 46 | // SECTION LocalStorage ------------------------------------------------------------------------- 47 | // ---------------------------------------------------------------------------------------------- 48 | 49 | public static setItem(key : string, value : any, prefix : boolean = true) : void 50 | { 51 | const itemKey = this.prefixer(key, prefix); 52 | localStorage.setItem(itemKey, JSON.stringify(value)); 53 | } 54 | 55 | public static getItem(key : string, prefix : boolean = true) : any 56 | { 57 | const itemKey = this.prefixer(key, prefix); 58 | const res = localStorage.getItem(itemKey); 59 | if (res !== 'undefined') 60 | return JSON.parse(res as any); 61 | console.error('StorageHelper : getItem -> undefined key'); 62 | return null; 63 | } 64 | 65 | public static removeItem(key : string, prefix : boolean = true) : void 66 | { 67 | const itemKey = this.prefixer(key, prefix); 68 | localStorage.removeItem(itemKey); 69 | } 70 | 71 | public static getKeys(all : boolean = false) : string[] 72 | { 73 | const keys : string[] = []; 74 | // NOTE Keys 75 | for (const key in localStorage) 76 | keys.push(key); 77 | if (all) 78 | return keys; 79 | // NOTE Prefixed keys 80 | return keys.filter((item) => item.startsWith(this.storagePrefix)); 81 | } 82 | 83 | public static clearItems(all : boolean = false) : void 84 | { 85 | // NOTE Keys 86 | if (all) 87 | { 88 | localStorage.clear(); 89 | return; 90 | } 91 | // NOTE Prefixed keys 92 | const prefixedKeys = this.getKeys(); 93 | for (const prefixedKey of prefixedKeys) 94 | this.removeItem(prefixedKey, false); 95 | } 96 | 97 | public static clearItemsWithoutCurrentPrefix() : void 98 | { 99 | const allKeys = this.getKeys(true); 100 | for (const key of allKeys) 101 | if (!key.startsWith(this.storagePrefix)) 102 | this.removeItem(key, false); 103 | } 104 | 105 | // !SECTION LocalStorage 106 | 107 | // NOTE Private 108 | 109 | private static prefixer(key : string, autoPrefix : boolean) : string 110 | { 111 | let itemKey = key; 112 | if (autoPrefix) 113 | itemKey = this.storagePrefix + key; 114 | return itemKey; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/app/shared/helpers/string.helper.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Injectable } from '@angular/core'; 3 | 4 | @Injectable() 5 | export class StringHelper 6 | { 7 | /** JavaScript equivalent to printf/String.Format 8 | * https://stackoverflow.com/a/31007976/7462178 9 | */ 10 | public static interpolate(theString : string, argumentArray : string[]) : string 11 | { 12 | let regex = /%s/; 13 | let _r = function(p : string, c : string) { return p.replace(regex, c); }; 14 | return argumentArray.reduce(_r, theString); 15 | } 16 | 17 | public static buildURIParams(parameters : object) : string 18 | { 19 | const parts = new URLSearchParams() 20 | 21 | for (const [key, value] of Object.entries(parameters)) 22 | { 23 | if (key === undefined || value === undefined) 24 | continue; 25 | parts.append(key, value) 26 | } 27 | 28 | return '?' + parts.toString() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/shared/services/app.service.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { Injectable } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | 5 | // External modules 6 | import { ArrayTyper } from '@caliatys/array-typer'; 7 | import { TranslateService } from '@ngx-translate/core'; 8 | import axios from 'axios'; 9 | import { AxiosResponse } from 'axios'; 10 | import { AxiosError } from 'axios'; 11 | import { AxiosInstance } from 'axios'; 12 | import { CreateAxiosDefaults } from 'axios'; 13 | 14 | // Internal modules 15 | import { ToastManager } from '@blocks/toast/toast.manager'; 16 | import { environment } from '@env/environment'; 17 | 18 | // Helpers 19 | import { StorageHelper } from '@helpers/storage.helper'; 20 | 21 | // Enums 22 | import { Endpoint } from '@enums/endpoint.enum'; 23 | 24 | // Models 25 | 26 | // Services 27 | import { StoreService } from './store.service'; 28 | 29 | @Injectable() 30 | export class AppService 31 | { 32 | // NOTE Default configuration 33 | private default : CreateAxiosDefaults = { 34 | withCredentials : true, 35 | timeout : 990000, 36 | headers : { 37 | 'Content-Type' : 'application/json', 38 | 'Accept' : 'application/json', 39 | }, 40 | }; 41 | 42 | // NOTE Instances 43 | private api : AxiosInstance = axios.create({ 44 | baseURL : environment.apiBaseUrl, 45 | ...this.default, 46 | }); 47 | 48 | // NOTE Controller 49 | private controller : AbortController = new AbortController(); 50 | 51 | constructor 52 | ( 53 | private storeService : StoreService, 54 | private toastManager : ToastManager, 55 | private router : Router, 56 | private translateService : TranslateService, 57 | ) 58 | { 59 | this.initRequestInterceptor(this.api); 60 | this.initResponseInterceptor(this.api); 61 | 62 | this.initAuthHeader(); 63 | } 64 | 65 | // ---------------------------------------------------------------------------------------------- 66 | // SECTION Methods ------------------------------------------------------------------------------ 67 | // ---------------------------------------------------------------------------------------------- 68 | 69 | public async authenticate(email : string, password : string) : Promise 70 | { 71 | return Promise.resolve(true); 72 | 73 | // StorageHelper.removeToken(); 74 | 75 | // const url = Endpoint.AUTHENTICATE; 76 | // const { data } = await this.api.post(url, { email, password }); 77 | 78 | // if (!data) 79 | // return false; 80 | 81 | // const authResponse = new AuthResponse(data); 82 | // StorageHelper.setToken(authResponse); 83 | // this.initAuthHeader(); 84 | // return true; 85 | } 86 | 87 | public async forgotPassword(email : string) : Promise 88 | { 89 | return Promise.resolve(true); 90 | 91 | // const url = Endpoint.FORGOT_PASSWORD; 92 | // const { data } = await this.api.post(url, { email }); 93 | 94 | // return !!data; 95 | } 96 | 97 | public async validateAccount(token : string, password : string) : Promise 98 | { 99 | return Promise.resolve(true); 100 | 101 | // const url = Endpoint.VALIDATE_ACCOUNT; 102 | // const { data } = await this.api.post(url, { token, password }); 103 | 104 | // return !!data; 105 | } 106 | 107 | // public async getLastLines(siteId : string) : Promise 108 | // { 109 | // const url = StringHelper.interpolate(Endpoint.GET_LAST_LINES, [ siteId ]); 110 | // const { data } = await this.api.get(url); 111 | 112 | // if (!data) 113 | // return []; 114 | 115 | // return ArrayTyper.asArray(Line, data); 116 | // } 117 | 118 | // !SECTION Methods 119 | 120 | // ---------------------------------------------------------------------------------------------- 121 | // SECTION Helpers ------------------------------------------------------------------------------ 122 | // ---------------------------------------------------------------------------------------------- 123 | 124 | private initAuthHeader() : void 125 | { 126 | // const token = StorageHelper.getToken(); 127 | // if (!token) 128 | // return; 129 | 130 | // this.api.defaults.headers.common['Authorization'] = `Bearer ${token.jwtToken}`; 131 | // this.api.defaults.headers.common['Token'] = token.jwtToken; 132 | } 133 | 134 | public initRequestInterceptor(instance : AxiosInstance) : void 135 | { 136 | instance.interceptors.request.use((config) => 137 | { 138 | console.log('interceptors.request.config', config); 139 | this.storeService.isLoading.set(true); 140 | 141 | return config; 142 | }, 143 | (error) => 144 | { 145 | console.log('interceptors.request.error', error); 146 | this.storeService.isLoading.set(false); 147 | 148 | this.toastManager.quickShow(error); 149 | return Promise.reject(error); 150 | }); 151 | } 152 | 153 | public initResponseInterceptor(instance : AxiosInstance) : void 154 | { 155 | instance.interceptors.response.use((response) => 156 | { 157 | console.log('interceptors.response.response', response); 158 | this.storeService.isLoading.set(false); 159 | 160 | return response; 161 | }, 162 | async (error : AxiosError) => 163 | { 164 | console.log('interceptors.response.error', error); 165 | this.storeService.isLoading.set(false); 166 | 167 | // NOTE Prevent request canceled error 168 | if (error.code === 'ERR_CANCELED') 169 | return Promise.resolve(error); 170 | 171 | this.toastManager.quickShow(error.message); 172 | return Promise.reject(error); 173 | }); 174 | } 175 | 176 | // !SECTION Helpers 177 | } -------------------------------------------------------------------------------- /src/app/shared/services/store.service.ts: -------------------------------------------------------------------------------- 1 | // Angular modules 2 | import { isPlatformServer } from '@angular/common'; 3 | import { Inject } from '@angular/core'; 4 | import { signal } from '@angular/core'; 5 | import { Injectable } from '@angular/core'; 6 | import { PLATFORM_ID } from '@angular/core'; 7 | 8 | // External modules 9 | import { TranslateService } from '@ngx-translate/core'; 10 | 11 | // Internal modules 12 | import { environment } from '@env/environment'; 13 | 14 | @Injectable() 15 | export class StoreService 16 | { 17 | public isServer = signal(isPlatformServer(this.platformId)); 18 | public isLoading = signal(true); 19 | public pageTitle = signal(environment.appName); 20 | 21 | constructor 22 | ( 23 | @Inject(PLATFORM_ID) private platformId : Object, 24 | private translateService : TranslateService 25 | ) 26 | { 27 | } 28 | 29 | // ------------------------------------------------------------------------------- 30 | // NOTE Page title --------------------------------------------------------------- 31 | // ------------------------------------------------------------------------------- 32 | 33 | public setPageTitle(title : string, translate : boolean = true) : void 34 | { 35 | const pageTitle = translate ? this.translateService.instant(title) : title; 36 | this.pageTitle.set(pageTitle); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "NOT_FOUND" : "404 - Page not found", 3 | "GO_TO_HOMEPAGE" : "Go to homepage", 4 | "WELCOME_TO" : "Welcome to", 5 | "PROJECT_DESC" : "A Centurio boilerplate solution", 6 | "SIGN_IN" : "Sign in", 7 | "LOGOUT" : "Logout", 8 | "HOME" : "Home", 9 | "VERSION" : "Version", 10 | "USER" : "User", 11 | "EMAIL" : "E-mail", 12 | "FORGOT_YOUR_PWD" : "Forgot your password?", 13 | "BACK_TO_LOGIN" : "Back to login", 14 | "VALIDATE_ACCOUNT" : "Validate the account", 15 | "VALIDATE" : "Validate", 16 | "PASSWORD" : "Password", 17 | "LOADING" : "Loading", 18 | "CLOSE" : "Close", 19 | "DELETE" : "Delete", 20 | "ALERT_DELETE_ENTRY" : "Are you sure you want to delete this entry?", 21 | "RESET_PASSWORD" : "Reset password", 22 | "FIELD_REQUIRED" : "This field is required", 23 | "FIELD_EMAIL" : "This field must be an email" 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/img/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/assets/img/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/assets/img/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/img/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #2d89ef 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/img/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/img/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/favicon.ico -------------------------------------------------------------------------------- /src/assets/img/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EasyAngular", 3 | "short_name": "EasyAngular", 4 | "theme_color": "#2b46c7", 5 | "background_color": "#2b46c7", 6 | "display": "standalone", 7 | "scope": "/", 8 | "start_url": "", 9 | "icons": [ 10 | { 11 | "src": "android-chrome-192x192.png", 12 | "sizes": "192x192", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "android-chrome-512x512.png", 17 | "sizes": "512x512", 18 | "type": "image/png" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/img/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /src/assets/img/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/img/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /src/assets/img/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /src/assets/img/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /src/assets/img/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 27 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/assets/img/project/folder-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/project/folder-structure.png -------------------------------------------------------------------------------- /src/assets/img/project/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicolasRoehm/angular-boilerplate/c800d9d8412768f14b98cf29f1621850191a8be5/src/assets/img/project/login.png -------------------------------------------------------------------------------- /src/assets/img/project/logo.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /src/assets/scss/project.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // NOTE Browser style : Reset --------------------------------------------------------- Overwrite 3 | // ---------------------------------------------------------------------------------------------- 4 | 5 | // ---------------------------------------------------------------------------------------------- 6 | // NOTE Project --------------------------------------------------------------------------------- 7 | // ---------------------------------------------------------------------------------------------- 8 | 9 | // NOTE Link 10 | 11 | // NOTE Scrollbar 12 | 13 | // NOTE Sortable table -------------------------------------------------------------------------- 14 | 15 | // NOTE Icons ----------------------------------------------------------------------------------- 16 | 17 | // ---------------------------------------------------------------------------------------------- 18 | // NOTE Bootstrap --------------------------------------------------------------------- Overwrite 19 | // ---------------------------------------------------------------------------------------------- 20 | 21 | // NOTE Pagination ------------------------------------------------------------------------------ 22 | 23 | // NOTE Button ---------------------------------------------------------------------------------- 24 | 25 | // NOTE Modal ----------------------------------------------------------------------------------- 26 | 27 | // NOTE Toast ----------------------------------------------------------------------------------- 28 | 29 | .toast-header { 30 | background-color: transparent; 31 | } 32 | .toast-body { 33 | background-color: rgba(255, 255, 255, 0.85); 34 | } 35 | 36 | // NOTE Nav ------------------------------------------------------------------------------------- 37 | 38 | // NOTE Table ----------------------------------------------------------------------------------- 39 | 40 | // NOTE Select ---------------------------------------------------------------------------------- 41 | 42 | // NOTE Form ------------------------------------------------------------------------------------ 43 | 44 | // Checkbox 45 | 46 | // Radio 47 | 48 | // NOTE Sortable table -------------------------------------------------------------------------- 49 | -------------------------------------------------------------------------------- /src/assets/scss/styles.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // NOTE Variables ------------------------------------------------------------------------------- 3 | // ---------------------------------------------------------------------------------------------- 4 | 5 | @import 'variables.scss'; 6 | 7 | // ---------------------------------------------------------------------------------------------- 8 | // NOTE Bootstrap ------------------------------------------------------------------------------- 9 | // ---------------------------------------------------------------------------------------------- 10 | 11 | // NOTE Complete bootstrap 12 | @import '../../../node_modules/bootstrap/scss/bootstrap.scss'; 13 | 14 | // NOTE Bootstrap configuration 15 | // @import '../../../node_modules/bootstrap/scss/functions'; 16 | // @import '../../../node_modules/bootstrap/scss/variables'; 17 | // @import '../../../node_modules/bootstrap/scss/mixins'; 18 | 19 | // // NOTE Merge maps 20 | // $theme-colors: map-merge($theme-colors, $custom-colors); 21 | 22 | // @import '../../../node_modules/bootstrap/scss/utilities'; 23 | 24 | // // NOTE Bootstrap layout & components 25 | // @import '../../../node_modules/bootstrap/scss/root'; 26 | // @import '../../../node_modules/bootstrap/scss/reboot'; 27 | // @import '../../../node_modules/bootstrap/scss/type'; 28 | // // @import '../../../node_modules/bootstrap/scss/images'; 29 | // @import '../../../node_modules/bootstrap/scss/containers'; 30 | // @import '../../../node_modules/bootstrap/scss/grid'; 31 | // @import '../../../node_modules/bootstrap/scss/tables'; 32 | // @import '../../../node_modules/bootstrap/scss/forms'; 33 | // @import '../../../node_modules/bootstrap/scss/buttons'; 34 | // @import '../../../node_modules/bootstrap/scss/transitions'; 35 | // @import '../../../node_modules/bootstrap/scss/dropdown'; 36 | // // @import '../../../node_modules/bootstrap/scss/button-group'; 37 | // @import '../../../node_modules/bootstrap/scss/nav'; 38 | // @import '../../../node_modules/bootstrap/scss/navbar'; 39 | // @import '../../../node_modules/bootstrap/scss/card'; 40 | // // @import '../../../node_modules/bootstrap/scss/accordion'; 41 | // // @import '../../../node_modules/bootstrap/scss/breadcrumb'; 42 | // // @import '../../../node_modules/bootstrap/scss/pagination'; 43 | // @import '../../../node_modules/bootstrap/scss/badge'; 44 | // // @import '../../../node_modules/bootstrap/scss/alert'; 45 | // // @import '../../../node_modules/bootstrap/scss/progress'; 46 | // // @import '../../../node_modules/bootstrap/scss/list-group'; 47 | // // @import '../../../node_modules/bootstrap/scss/close'; 48 | // @import '../../../node_modules/bootstrap/scss/toasts'; 49 | // @import '../../../node_modules/bootstrap/scss/modal'; 50 | // @import '../../../node_modules/bootstrap/scss/tooltip'; 51 | // @import '../../../node_modules/bootstrap/scss/popover'; 52 | // // @import '../../../node_modules/bootstrap/scss/carousel'; 53 | // @import '../../../node_modules/bootstrap/scss/spinners'; 54 | // // @import '../../../node_modules/bootstrap/scss/offcanvas'; 55 | 56 | // // NOTE Boostrap helpers 57 | // @import '../../../node_modules/bootstrap/scss/helpers'; 58 | 59 | // // NOTE Boostrap utilities 60 | // @import '../../../node_modules/bootstrap/scss/utilities/api'; 61 | 62 | // ---------------------------------------------------------------------------------------------- 63 | // NOTE External components --------------------------------------------------------------------- 64 | // ---------------------------------------------------------------------------------------------- 65 | 66 | // $roboto-font-path: "../../../node_modules/roboto-fontface/fonts" !default; 67 | // @import "../../../node_modules/roboto-fontface/css/roboto/sass/roboto-fontface"; 68 | // @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;400;700&display=swap'); 69 | 70 | // ---------------------------------------------------------------------------------------------- 71 | // NOTE Project style --------------------------------------------------------------------------- 72 | // ---------------------------------------------------------------------------------------------- 73 | 74 | @import 'project.scss'; 75 | 76 | // ---------------------------------------------------------------------------------------------- 77 | // NOTE Overwrite styles ------------------------------------------------------------------------ 78 | // ---------------------------------------------------------------------------------------------- 79 | 80 | // @import 'modules/_bootstrap.scss'; 81 | -------------------------------------------------------------------------------- /src/assets/scss/variables.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // SECTION Variables ---------------------------------------------------------------------------- 3 | // ---------------------------------------------------------------------------------------------- 4 | 5 | // ---------------------------------------------------------------------------------------------- 6 | // NOTE Project --------------------------------------------------------------------------------- 7 | // ---------------------------------------------------------------------------------------------- 8 | 9 | // NOTE Colors ---------------------------------------------------------------------------------- 10 | 11 | // ---------------------------------------------------------------------------------------------- 12 | // NOTE Bootstrap ------------------------------------------------------------------------------- 13 | // ---------------------------------------------------------------------------------------------- 14 | 15 | // NOTE Utilities ------------------------------------------------------------------------------- 16 | 17 | $utilities: ( 18 | "cursor": ( 19 | property: cursor, 20 | class: cursor, 21 | responsive: false, 22 | values: pointer, 23 | ) 24 | ); 25 | 26 | // NOTE Color system ---------------------------------------------------------------------------- 27 | 28 | // $primary: #375F9B; 29 | $secondary: #8492A6; 30 | $text-muted: #ACA7AA; 31 | $info: #375F9B; // #3C78DA; 32 | $danger: #D0021B; 33 | $success: #12a366; 34 | $min-contrast-ratio: 2.5; 35 | 36 | // NOTE Body ------------------------------------------------------------------------------------ 37 | 38 | // NOTE Grid ------------------------------------------------------------------------------------ 39 | 40 | // NOTE Borders --------------------------------------------------------------------------------- 41 | 42 | // NOTE Typography ----------------------------------------------------------------------------- 43 | 44 | // NOTE Links ----------------------------------------------------------------------------------- 45 | // NOTE Badges ---------------------------------------------------------------------------------- 46 | // NOTE Buttons --------------------------------------------------------------------------------- 47 | 48 | $btn-close-color: white; 49 | 50 | // NOTE Navbar ---------------------------------------------------------------------------------- 51 | // NOTE Navs ------------------------------------------------------------------------------------ 52 | 53 | // NOTE Modal ----------------------------------------------------------------------------------- 54 | 55 | // NOTE Dropdowns ------------------------------------------------------------------------------- 56 | // NOTE Cards ----------------------------------------------------------------------------------- 57 | 58 | // NOTE List group ------------------------------------------------------------------------------ 59 | // NOTE Pagination ------------------------------------------------------------------------------ 60 | // NOTE Popovers -------------------------------------------------------------------------------- 61 | // NOTE Typography ------------------------------------------------------------------------------ 62 | // NOTE Forms ----------------------------------------------------------------------------------- 63 | 64 | // NOTE Tables ---------------------------------------------------------------------------------- 65 | 66 | // !SECTION Variables 67 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | // Enums 2 | import { EnvName } from '@enums/environment.enum'; 3 | 4 | // Packages 5 | import packageInfo from '../../package.json'; 6 | 7 | const scheme = 'http://'; 8 | const host = 'localhost'; 9 | const port = ':5000'; 10 | const path = '/api/'; 11 | 12 | const baseUrl = scheme + host + port + path; 13 | 14 | export const environment = { 15 | production : true, 16 | version : packageInfo.version, 17 | appName : 'EasyAngular', 18 | envName : EnvName.PROD, 19 | defaultLanguage : 'en', 20 | apiBaseUrl : baseUrl, 21 | }; 22 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | // Enums 6 | import { EnvName } from '@enums/environment.enum'; 7 | 8 | // Packages 9 | import packageInfo from '../../package.json'; 10 | 11 | const scheme = 'http://'; 12 | const host = 'localhost'; 13 | const port = ':5000'; 14 | const path = '/api/'; 15 | 16 | const baseUrl = scheme + host + port + path; 17 | 18 | export const environment = { 19 | production : false, 20 | version : packageInfo.version, 21 | appName : 'EasyAngular', 22 | envName : EnvName.LOCAL, 23 | defaultLanguage : 'en', 24 | apiBaseUrl : baseUrl, 25 | }; 26 | 27 | /* 28 | * For easier debugging in development mode, you can import the following file 29 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 30 | * 31 | * This import should be commented out in production mode because it will have a negative impact 32 | * on performance if an error is thrown. 33 | */ 34 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 35 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EasyAngular 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // Angular modules 4 | import { enableProdMode } from '@angular/core'; 5 | import { bootstrapApplication } from '@angular/platform-browser'; 6 | 7 | // External modules 8 | import { appConfig } from './app/app.config'; 9 | 10 | // Internal modules 11 | import { environment } from './environments/environment'; 12 | 13 | // Components 14 | import { AppComponent } from './app/app.component'; 15 | 16 | if (environment.production) { 17 | enableProdMode(); 18 | } 19 | 20 | bootstrapApplication( 21 | AppComponent, 22 | appConfig 23 | ) 24 | .catch(err => console.error(err)); -------------------------------------------------------------------------------- /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 recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | 54 | import '@angular/localize/init'; 55 | -------------------------------------------------------------------------------- /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/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment( 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting(), 14 | ); 15 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [ 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/main.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": false, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "allowSyntheticDefaultImports": true, 13 | "resolveJsonModule": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "sourceMap": true, 16 | "declaration": false, 17 | "downlevelIteration": true, 18 | "experimentalDecorators": true, 19 | "moduleResolution": "node", 20 | "importHelpers": true, 21 | "target": "ES2022", 22 | "module": "es2020", 23 | "lib": [ 24 | "es2020", 25 | "dom", 26 | "ES2021.String" 27 | ], 28 | "paths": { 29 | "@blocks/*": ["src/app/shared/components/blocks/*"], 30 | "@forms/*": ["src/app/shared/components/forms/*"], 31 | "@layouts/*": ["src/app/shared/components/layouts/*"], 32 | "@modals/*": ["src/app/shared/components/modals/*"], 33 | "@enums/*": ["src/app/shared/enums/*"], 34 | "@directives/*": ["src/app/shared/directives/*"], 35 | "@pipes/*": ["src/app/shared/pipes/*"], 36 | "@validators/*": ["src/app/shared/validators/*"], 37 | "@factories/*": ["src/app/shared/factories/*"], 38 | "@guards/*": ["src/app/shared/guards/*"], 39 | "@helpers/*": ["src/app/shared/helpers/*"], 40 | "@models/*": ["src/app/shared/models/*"], 41 | "@interfaces/*": ["src/app/shared/interfaces/*"], 42 | "@services/*": ["src/app/shared/services/*"], 43 | "@env/*": ["src/environments/*"] 44 | }, 45 | "useDefineForClassFields": false 46 | }, 47 | "angularCompilerOptions": { 48 | "enableI18nLegacyMessageIdFormat": false, 49 | "strictInjectionParameters": true, 50 | "strictInputAccessModifiers": true, 51 | "strictTemplates": true 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------