├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode └── settings.json ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── img └── home.png ├── package-lock.json ├── package.json ├── src ├── .browserslistrc ├── app │ ├── about │ │ ├── about-page │ │ │ ├── about-page.component.html │ │ │ └── about-page.component.ts │ │ ├── feature-block │ │ │ ├── feature-block.component.html │ │ │ └── feature-block.component.ts │ │ └── models │ │ │ ├── feature.model.ts │ │ │ └── intro.model.ts │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── clients │ │ ├── clients-page │ │ │ ├── clients-page.component.html │ │ │ └── clients-page.component.ts │ │ ├── companies-block │ │ │ ├── companies-block.component.html │ │ │ └── companies-block.component.ts │ │ └── models │ │ │ ├── client.model.ts │ │ │ └── company.model.ts │ ├── footer │ │ ├── footer.component.css │ │ ├── footer.component.html │ │ ├── footer.component.ts │ │ └── footer.model.ts │ ├── gallery │ │ ├── gallery-page │ │ │ ├── gallery-page.component.html │ │ │ └── gallery-page.component.ts │ │ ├── image-block │ │ │ ├── image-block.component.html │ │ │ └── image-block.component.ts │ │ └── models │ │ │ └── image.model.ts │ ├── home │ │ ├── home-page │ │ │ ├── home-page.component.html │ │ │ └── home-page.component.ts │ │ └── models │ │ │ └── header.model.ts │ ├── navigation │ │ ├── navigation.component.css │ │ ├── navigation.component.html │ │ └── navigation.component.ts │ ├── navmenu │ │ ├── navmenu.component.html │ │ └── navmenu.component.ts │ ├── notfound │ │ └── notfound-page │ │ │ ├── notfound-page.component.css │ │ │ ├── notfound-page.component.html │ │ │ └── notfound-page.component.ts │ ├── pricing │ │ ├── models │ │ │ ├── plan.model.ts │ │ │ └── pricing.model.ts │ │ ├── pricing-block │ │ │ ├── pricing-block.component.html │ │ │ └── pricing-block.component.ts │ │ └── pricing-page │ │ │ ├── pricing-page.component.html │ │ │ └── pricing-page.component.ts │ ├── services │ │ ├── models │ │ │ └── service.model.ts │ │ └── services-page │ │ │ ├── services-page.component.html │ │ │ └── services-page.component.ts │ ├── shared │ │ └── services │ │ │ ├── config.service.ts │ │ │ ├── configuration.ts │ │ │ └── in-memory-data.service.ts │ ├── social │ │ ├── models │ │ │ └── site.model.ts │ │ ├── social.component.html │ │ └── social.component.ts │ └── testimonial │ │ ├── feedback-block │ │ ├── feedback-block.component.html │ │ └── feedback-block.component.ts │ │ ├── models │ │ ├── feedback.model.ts │ │ └── testimonial.model.ts │ │ └── testimonial-page │ │ ├── testimonial-page.component.html │ │ └── testimonial-page.component.ts ├── assets │ ├── .gitkeep │ ├── css │ │ ├── animate.css │ │ ├── font-awesome.css │ │ ├── font-awesome.css.map │ │ ├── font-awesome.min.css │ │ ├── ionicons.min.css │ │ └── namari-color.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── images │ │ ├── banner-images │ │ │ └── banner-image-1.jpg │ │ ├── company-images │ │ │ ├── company-logo1.png │ │ │ ├── company-logo2.png │ │ │ ├── company-logo3.png │ │ │ ├── company-logo4.png │ │ │ ├── company-logo5.png │ │ │ ├── company-logo6.png │ │ │ ├── company-logo7.png │ │ │ ├── company-logo8.png │ │ │ └── company-logo9.png │ │ ├── dancer.jpg │ │ ├── favicon.ico │ │ ├── gallery-images │ │ │ ├── gallery-image-1.jpg │ │ │ ├── gallery-image-2.jpg │ │ │ ├── gallery-image-3.jpg │ │ │ ├── gallery-image-4.jpg │ │ │ ├── gallery-image-5.jpg │ │ │ └── gallery-image-6.jpg │ │ ├── logo-2.png │ │ ├── logo.png │ │ └── user-images │ │ │ ├── user-1.jpg │ │ │ ├── user-2.jpg │ │ │ └── user-3.jpg │ └── js │ │ ├── images-loaded.min.js │ │ ├── pushy.min.js │ │ └── wow.min.js ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["projects/**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "plugin:@angular-eslint/recommended", 11 | "plugin:@angular-eslint/template/process-inline-templates", 12 | "plugin:prettier/recommended" 13 | ], 14 | "rules": { 15 | "@angular-eslint/directive-selector": [ 16 | "error", 17 | { 18 | "type": "attribute", 19 | "prefix": "app", 20 | "style": "camelCase" 21 | } 22 | ], 23 | "@angular-eslint/component-selector": [ 24 | "error", 25 | { 26 | "type": "element", 27 | "prefix": "app", 28 | "style": "kebab-case" 29 | } 30 | ] 31 | } 32 | }, 33 | { 34 | "files": ["*.html"], 35 | "excludedFiles": ["*inline-template-*.component.html"], 36 | "extends": [ 37 | "plugin:@angular-eslint/template/recommended", 38 | "plugin:@angular-eslint/template/accessibility", 39 | "plugin:prettier/recommended" 40 | ], 41 | "rules": { 42 | "prettier/prettier": [ 43 | "error", 44 | { 45 | "parser": "angular" 46 | } 47 | ] 48 | } 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .angular 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.angular/cache 30 | /.sass-cache 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | npm-debug.log 35 | yarn-error.log 36 | testem.log 37 | /typings 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": true, 4 | "singleQuote": true, 5 | "semi": true, 6 | "bracketSpacing": true, 7 | "arrowParens": "avoid", 8 | "trailingComma": "es5", 9 | "bracketSameLine": true, 10 | "printWidth": 80, 11 | "endOfLine": "auto" 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "angular.enable-strict-mode-prompt": false 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :zap: Angular Website Example 2 | 3 | * Converts a free Namari HTML template to an Angular app 4 | * **Note:** to open web links in a new window use: _ctrl+click on link_ 5 | 6 | ![GitHub repo size](https://img.shields.io/github/repo-size/AndrewJBateman/angular-website-example?style=plastic) 7 | ![GitHub pull requests](https://img.shields.io/github/issues-pr/AndrewJBateman/angular-website-example?style=plastic) 8 | ![GitHub Repo stars](https://img.shields.io/github/stars/AndrewJBateman/angular-website-example?style=plastic) 9 | ![GitHub last commit](https://img.shields.io/github/last-commit/AndrewJBateman/angular-website-example?style=plastic) 10 | 11 | ## :page_facing_up: Table of contents 12 | 13 | * [:zap: Angular Website Example](#zap-angular-website-example) 14 | * [:page\_facing\_up: Table of contents](#page_facing_up-table-of-contents) 15 | * [:books: General info](#books-general-info) 16 | * [:camera: Screenshots](#camera-screenshots) 17 | * [:signal\_strength: Technologies](#signal_strength-technologies) 18 | * [:floppy\_disk: Setup](#floppy_disk-setup) 19 | * [:computer: Code Examples](#computer-code-examples) 20 | * [:cool: Features](#cool-features) 21 | * [:clipboard: Status \& To-Do List](#clipboard-status--to-do-list) 22 | * [:clap: Inspiration](#clap-inspiration) 23 | * [:file\_folder: License](#file_folder-license) 24 | * [:envelope: Contact](#envelope-contact) 25 | 26 | ## :books: General info 27 | 28 | * Includes code from [AdMISTER STUDIOs](https://www.youtube.com/c/AdMISTERSTUDIOs/featured) - see [:clap: Inspiration](#clap-inspiration) below, but with changes to reduce lines of code and use latest Angular version. Some of the videos listed below are not applicable as I decided to just reproduce the Namari website and not include a blog, user login/logoff, auth guard, separate API etc. 29 | * [Angular feature modules](https://angular.io/guide/feature-modules) used for organisational best practise 30 | * Single service used to fetch data for all pages so this data can be accessed from an API. This meant I could not use custom interface classes. Each module should have its own service with interface class. 31 | 32 | ## :camera: Screenshots 33 | 34 | ![Example screenshot](./img/home.png). 35 | 36 | ## :signal_strength: Technologies 37 | 38 | * [Angular v17](https://angular.io/) JS framework 39 | * [Font Awesome v4](https://fontawesome.com/) icons 40 | * [RxJS Library v7](https://angular.io/guide/rx-library) used to [subscribe](http://reactivex.io/documentation/operators/subscribe.html) to the API data [observable](http://reactivex.io/documentation/observable.html). 41 | * [The HttpClient in @angular/common/http](https://angular.io/guide/http) offers a simplified client HTTP API for Angular applications that rests on the XMLHttpRequest interface exposed by browsers. 42 | 43 | ## :floppy_disk: Setup 44 | 45 | * Run `npm i` to install dependencies. 46 | * Run `ng serve` for a dev server. 47 | * Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 48 | 49 | ## :computer: Code Examples 50 | 51 | * `shared/services/config.service.ts` function to get data from API with inputs: database name and id 52 | 53 | ```typescript 54 | getSettings(database: string, id?: number): Observable { 55 | let url = id? `api/${database}/${id}` : `api/${database}`; 56 | return this.http.get(url).pipe( 57 | catchError(this.handleError(`Error getting data from ${database}`, [])) 58 | ); 59 | } 60 | ``` 61 | 62 | ## :cool: Features 63 | 64 | * Lazy-load modules used for most pages to speed up initial rendering of home page 65 | 66 | ## :clipboard: Status & To-Do List 67 | 68 | * Status: Working 69 | * To-Do: Remove unused CSS. Create separate service for each module? 70 | 71 | ## :clap: Inspiration 72 | 73 | * [Create your first website using angular 6 -- Part 1 -- HTML Template to Angular Component](https://www.youtube.com/watch?v=LYmJOdCuXrs&list=UUcfCHgDDBw65jdnd9DTKwgg&index=19) 74 | * [Create your first website using angular 6 -- Part 2-- Routing and Static data to configuration file](https://www.youtube.com/watch?v=JAb0vvr6foU) 75 | * [Create your first website using angular 6 -- Part 3 -- Configuration File](https://www.youtube.com/watch?v=qBjn8TrXyPY) 76 | * [Create your first website using angular 6 -- Part 4 -- Add BLOG Section](https://www.youtube.com/watch?v=0Nnm2rup5b8) 77 | * [Create your first website using angular 6 -- Part 5 -- 404 Component and Pagination for blog](https://www.youtube.com/watch?v=0Nnm2rup5b8) 78 | * [Create your first website using angular 6 -- Part 6-- Login Signup and Contact form using In Memory](https://www.youtube.com/watch?v=0Nnm2rup5b8) 79 | * [Create your first website using angular 6 -- Part 7-- Add JQUERY and existing JS](https://www.youtube.com/watch?v=0Nnm2rup5b8) 80 | * [Create your first website using angular 6 -- Part 8-- blog post Markdown Editor](https://www.youtube.com/watch?v=0Nnm2rup5b8) 81 | * [Create your first website using angular 6 -- Part 9 -- Static to Dynamic Menu populated from DB](https://www.youtube.com/watch?v=0Nnm2rup5b8) 82 | * [Create your first website using angular 6 -- Part 10 -- User Dashboard Module](https://www.youtube.com/watch?v=0Nnm2rup5b8) 83 | * [Create your first website using angular 6 -- Part 11 -- Named Router Outlets](https://www.youtube.com/watch?v=LKCjQqzc5Ys) 84 | * [Create your first website using angular 6 -- Part 12 -- Local Development SSL](https://www.youtube.com/watch?v=2vbWjzROtUc) 85 | * [Create your first website using angular 6 -- Part 13 -- Components to Feature Modules](https://www.youtube.com/watch?v=WDNV4iifHwM) 86 | * [Create your first website using angular 6 -- Part 14 -- Components to Feature Modules](https://www.youtube.com/watch?v=tj7V67-BY5U) 87 | * [Create your first website using angular 6 -- Part 15 -- Blog Component to Blog Feature Module](https://www.youtube.com/watch?v=By4tT-bNDXY) 88 | * [Create your first website using angular 6 -- Part 16 -- Creating email contact form API](https://www.youtube.com/watch?v=eNj9XbE9kdk&t=22s) 89 | * [Create your first website using angular 6 -- Part 17 -- send email from Angular APP](https://www.youtube.com/watch?v=QCnoamZnTsQ&t=23s) 90 | * [One Page Love website of html templates](https://onepagelove.com/namari) 91 | 92 | ## :file_folder: License 93 | 94 | * N/A 95 | 96 | ## :envelope: Contact 97 | 98 | * Repo created by [ABateman](https://github.com/AndrewJBateman), email: `gomezbateman@yahoo.com` 99 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-website-example": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "targets": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular-website-example", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": ["src/favicon.ico", "src/assets"], 22 | "styles": [ 23 | "src/styles.css", 24 | "src/assets/css/animate.css", 25 | "node_modules/font-awesome/css/font-awesome.css" 26 | ], 27 | "scripts": [ 28 | "node_modules/wowjs/dist/wow.js", 29 | "src/assets/js/wow.min.js", 30 | "src/assets/js/images-loaded.min.js" 31 | ], 32 | "aot": false, 33 | "vendorChunk": true, 34 | "extractLicenses": false, 35 | "buildOptimizer": false, 36 | "sourceMap": true, 37 | "optimization": false, 38 | "namedChunks": true 39 | }, 40 | "configurations": { 41 | "production": { 42 | "fileReplacements": [ 43 | { 44 | "replace": "src/environments/environment.ts", 45 | "with": "src/environments/environment.prod.ts" 46 | } 47 | ], 48 | "optimization": true, 49 | "outputHashing": "all", 50 | "sourceMap": false, 51 | "namedChunks": false, 52 | "aot": true, 53 | "extractLicenses": true, 54 | "vendorChunk": false, 55 | "buildOptimizer": true 56 | } 57 | }, 58 | "defaultConfiguration": "" 59 | }, 60 | "serve": { 61 | "builder": "@angular-devkit/build-angular:dev-server", 62 | "options": { 63 | "buildTarget": "angular-website-example:build" 64 | }, 65 | "configurations": { 66 | "production": { 67 | "buildTarget": "angular-website-example:build:production" 68 | } 69 | } 70 | }, 71 | "extract-i18n": { 72 | "builder": "@angular-devkit/build-angular:extract-i18n", 73 | "options": { 74 | "buildTarget": "angular-website-example:build" 75 | } 76 | }, 77 | "test": { 78 | "builder": "@angular-devkit/build-angular:karma", 79 | "options": { 80 | "main": "src/test.ts", 81 | "polyfills": "src/polyfills.ts", 82 | "tsConfig": "tsconfig.spec.json", 83 | "karmaConfig": "src/karma.conf.js", 84 | "styles": ["src/styles.css"], 85 | "scripts": [], 86 | "assets": ["src/favicon.ico", "src/assets"] 87 | } 88 | }, 89 | "lint": { 90 | "builder": "@angular-eslint/builder:lint", 91 | "options": { 92 | "lintFilePatterns": [ 93 | "src/**/*.ts", 94 | "src/**/*.html" 95 | ] 96 | } 97 | } 98 | } 99 | }, 100 | "angular-website-example-e2e": { 101 | "root": "e2e/", 102 | "projectType": "application", 103 | "targets": { 104 | "e2e": { 105 | "builder": "@angular-devkit/build-angular:protractor", 106 | "options": { 107 | "protractorConfig": "e2e/protractor.conf.js", 108 | "devServerTarget": "angular-website-example:serve" 109 | }, 110 | "configurations": { 111 | "production": { 112 | "devServerTarget": "angular-website-example:serve:production" 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular-website-example!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /img/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewJBateman/angular-website-example/6cae24f1bd75e6fb69b155b039941bf9ed0c8b15/img/home.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website-practise", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build --configuration=production", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "lint:fix": "ng lint --fix", 11 | "prettier": "prettier --write \"src/**/*.{js,ts,html,scss,json}", 12 | "e2e": "ng e2e" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "^17.2.3", 17 | "@angular/common": "^17.2.3", 18 | "@angular/compiler": "^17.2.3", 19 | "@angular/core": "^17.2.3", 20 | "@angular/forms": "^17.2.3", 21 | "@angular/platform-browser": "^17.2.3", 22 | "@angular/platform-browser-dynamic": "^17.2.3", 23 | "@angular/router": "^17.2.3", 24 | "angular-in-memory-web-api": "^0.17.0", 25 | "core-js": "^3.36.0", 26 | "font-awesome": "^4.7.0", 27 | "rxjs": "~7.8.1", 28 | "tslib": "^2.6.2", 29 | "wowjs": "^1.1.3", 30 | "zone.js": "~0.14.4" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "^17.2.2", 34 | "@angular-eslint/builder": "17.2.1", 35 | "@angular-eslint/eslint-plugin": "17.2.1", 36 | "@angular-eslint/eslint-plugin-template": "17.2.1", 37 | "@angular-eslint/schematics": "17.2.1", 38 | "@angular-eslint/template-parser": "17.2.1", 39 | "@angular/cli": "^17.2.2", 40 | "@angular/compiler-cli": "^17.2.3", 41 | "@angular/language-service": "^17.2.3", 42 | "@types/flickity": "^2.2.11", 43 | "@types/jasmine": "~5.1.4", 44 | "@types/jasminewd2": "~2.0.13", 45 | "@types/node": "^20.11.24", 46 | "@typescript-eslint/eslint-plugin": "^7.1.0", 47 | "@typescript-eslint/parser": "^7.1.0", 48 | "codelyzer": "^6.0.2", 49 | "eslint": "^8.57.0", 50 | "eslint-config-prettier": "^9.1.0", 51 | "eslint-plugin-prettier": "^5.1.3", 52 | "jasmine-core": "~5.1.2", 53 | "jasmine-spec-reporter": "~7.0.0", 54 | "karma": "~6.4.3", 55 | "karma-chrome-launcher": "~3.2.0", 56 | "karma-coverage-istanbul-reporter": "~3.0.3", 57 | "karma-jasmine": "~5.1.0", 58 | "karma-jasmine-html-reporter": "^2.1.0", 59 | "prettier": "^3.2.5", 60 | "prettier-eslint": "^16.3.0", 61 | "protractor": "~7.0.0", 62 | "ts-node": "~10.9.2", 63 | "typescript": "5.2.2" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/app/about/about-page/about-page.component.html: -------------------------------------------------------------------------------- 1 | @if (intro$ | async; as intro) { 2 |
3 |
4 |
5 |
6 |

{{ intro.tagline }}

7 |

{{ intro.title }}

8 |

{{ intro.description }}

9 |
10 |
11 | @if (features$) { 12 |
13 | @for (feature of features$ | async; track feature; let i = $index) { 14 |
15 | 16 |
17 | } 18 |
19 | } 20 |
21 |
22 | } 23 | -------------------------------------------------------------------------------- /src/app/about/about-page/about-page.component.ts: -------------------------------------------------------------------------------- 1 | import { Observable, throwError, catchError } from 'rxjs'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { ConfigService } from 'src/app/shared/services/config.service'; 4 | import { Intro } from '../models/intro.model'; 5 | import { Feature } from '../models/feature.model'; 6 | import { AsyncPipe } from '@angular/common'; 7 | import { FeatureBlockComponent } from '../feature-block/feature-block.component'; 8 | 9 | @Component({ 10 | selector: 'app-about-page', 11 | templateUrl: './about-page.component.html', 12 | standalone: true, 13 | imports: [FeatureBlockComponent, AsyncPipe], 14 | }) 15 | export class AboutPageComponent implements OnInit { 16 | intro$: Observable; 17 | features$: Observable; 18 | 19 | constructor(private config: ConfigService) {} 20 | 21 | ngOnInit(): void { 22 | this.getPageData('pages', 1); 23 | this.getBlockData('features'); 24 | } 25 | 26 | getPageData(database: string, id?: number): void { 27 | this.intro$ = this.config.getSettings(database, id).pipe( 28 | catchError(error => { 29 | console.error('Error fetching intro data:', error); 30 | return throwError(error); 31 | }) 32 | ); 33 | } 34 | 35 | getBlockData(database: string) { 36 | this.features$ = this.config.getSettings(database).pipe( 37 | catchError(error => { 38 | console.error('Error fetching feature data:', error); 39 | return throwError(error); 40 | }) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/about/feature-block/feature-block.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 |

{{ feature.title }}

9 |

{{ feature.description }}

10 |
11 |
12 | -------------------------------------------------------------------------------- /src/app/about/feature-block/feature-block.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { Feature } from '../models/feature.model'; 3 | 4 | @Component({ 5 | selector: 'app-feature-block', 6 | templateUrl: './feature-block.component.html', 7 | standalone: true, 8 | }) 9 | export class FeatureBlockComponent { 10 | @Input() feature: Feature; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/about/models/feature.model.ts: -------------------------------------------------------------------------------- 1 | export class Feature { 2 | id: number; 3 | icon: string; 4 | title: string; 5 | description: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/about/models/intro.model.ts: -------------------------------------------------------------------------------- 1 | export class Intro { 2 | id: number; 3 | name: string; 4 | tagline: string; 5 | title: string; 6 | description: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule, Routes } from '@angular/router'; 4 | 5 | const routes: Routes = [ 6 | { path: '', redirectTo: 'home', pathMatch: 'full' }, 7 | { 8 | path: 'home', 9 | loadComponent: () => 10 | import('./home/home-page/home-page.component').then( 11 | mod => mod.HomePageComponent 12 | ), 13 | }, 14 | { 15 | path: 'about', 16 | loadComponent: () => 17 | import('./about/about-page/about-page.component').then( 18 | mod => mod.AboutPageComponent 19 | ), 20 | }, 21 | { 22 | path: 'services', 23 | loadComponent: () => 24 | import('./services/services-page/services-page.component').then( 25 | mod => mod.ServicesPageComponent 26 | ), 27 | }, 28 | { 29 | path: 'testimonials', 30 | loadComponent: () => 31 | import('./testimonial/testimonial-page/testimonial-page.component').then( 32 | mod => mod.TestimonialPageComponent 33 | ), 34 | }, 35 | { 36 | path: 'gallery', 37 | loadComponent: () => 38 | import('./gallery/gallery-page/gallery-page.component').then( 39 | mod => mod.GalleryPageComponent 40 | ), 41 | }, 42 | { 43 | path: 'clients', 44 | loadComponent: () => 45 | import('./clients/clients-page/clients-page.component').then( 46 | mod => mod.ClientsPageComponent 47 | ), 48 | }, 49 | { 50 | path: 'pricing', 51 | loadComponent: () => 52 | import('./pricing/pricing-page/pricing-page.component').then( 53 | mod => mod.PricingPageComponent 54 | ), 55 | }, 56 | { 57 | path: '404', 58 | loadChildren: () => 59 | import('./notfound/notfound-page/notfound-page.component').then( 60 | mod => mod.NotfoundPageComponent 61 | ), 62 | }, 63 | { path: '**', redirectTo: '/404' }, 64 | ]; 65 | 66 | @NgModule({ 67 | imports: [CommonModule, RouterModule.forRoot(routes)], 68 | exports: [RouterModule], 69 | declarations: [], 70 | }) 71 | export class AppRoutingModule {} 72 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
5 |
8 |
11 |
14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | }) 7 | export class AppComponent { 8 | title = 'Namari Website'; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpClientModule } from '@angular/common/http'; 4 | import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | 7 | // Components 8 | import { AppComponent } from './app.component'; 9 | import { FooterComponent } from './footer/footer.component'; 10 | import { NavmenuComponent } from './navmenu/navmenu.component'; 11 | import { NavigationComponent } from './navigation/navigation.component'; 12 | import { SocialComponent } from './social/social.component'; 13 | 14 | // Modules 15 | import { AppRoutingModule } from './app-routing.module'; 16 | 17 | // Services 18 | import { ConfigService } from './shared/services/config.service'; 19 | import { InMemoryDataService } from './shared/services/in-memory-data.service'; 20 | 21 | @NgModule({ 22 | declarations: [AppComponent], 23 | imports: [ 24 | AppRoutingModule, 25 | BrowserModule, 26 | HttpClientModule, 27 | ReactiveFormsModule, 28 | 29 | // The HttpClientInMemoryWebApiModule module intercepts HTTP requests 30 | // and returns simulated server responses. 31 | // Remove it when a real server is ready to receive requests. 32 | HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { 33 | dataEncapsulation: false, 34 | passThruUnknownUrl: true, 35 | }), 36 | FooterComponent, 37 | NavigationComponent, 38 | NavmenuComponent, 39 | SocialComponent, 40 | ], 41 | providers: [ConfigService], 42 | bootstrap: [AppComponent], 43 | }) 44 | export class AppModule {} 45 | -------------------------------------------------------------------------------- /src/app/clients/clients-page/clients-page.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | @if (clients$ | async; as clients) { 4 |
5 |
6 |

{{ clients.tagline }}

7 |

{{ clients.title }}

8 |

{{ clients.description }}

9 |
10 |
11 | } 12 | 13 |
14 | @for (company of companies$ | async; track company; let i = $index) { 15 |
16 | 17 |
18 | } 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/app/clients/clients-page/clients-page.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable, throwError, catchError } from 'rxjs'; 3 | import { ConfigService } from 'src/app/shared/services/config.service'; 4 | import { Client } from '../models/client.model'; 5 | import { Company } from '../models/company.model'; 6 | import { AsyncPipe } from '@angular/common'; 7 | import { CompaniesBlockComponent } from '../companies-block/companies-block.component'; 8 | 9 | @Component({ 10 | selector: 'app-clients-page', 11 | templateUrl: './clients-page.component.html', 12 | standalone: true, 13 | imports: [CompaniesBlockComponent, AsyncPipe], 14 | }) 15 | export class ClientsPageComponent implements OnInit { 16 | clients$: Observable = new Observable(); 17 | companies$: Observable = new Observable(); 18 | constructor(private config: ConfigService) {} 19 | 20 | ngOnInit() { 21 | this.getPageData('pages', 2); 22 | this.getBlockData('companies'); 23 | } 24 | 25 | getPageData(database: string, id?: number) { 26 | this.clients$ = this.config.getSettings(database, id).pipe( 27 | catchError(error => { 28 | console.error('Error fetching feature data:', error); 29 | return throwError(error); 30 | }) 31 | ); 32 | } 33 | 34 | getBlockData(database: string) { 35 | this.companies$ = this.config.getSettings(database).pipe( 36 | catchError(error => { 37 | console.error('Error fetching feature data:', error); 38 | return throwError(error); 39 | }) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/clients/companies-block/companies-block.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{ company.name }} 5 |
6 | {{ company.name }} 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/clients/companies-block/companies-block.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { Company } from '../models/company.model'; 3 | 4 | @Component({ 5 | selector: 'app-companies-block', 6 | templateUrl: './companies-block.component.html', 7 | standalone: true, 8 | }) 9 | export class CompaniesBlockComponent { 10 | @Input() company: Company; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/clients/models/client.model.ts: -------------------------------------------------------------------------------- 1 | export class Client { 2 | id: number; 3 | name: string; 4 | tagline: string; 5 | title: string; 6 | description: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/clients/models/company.model.ts: -------------------------------------------------------------------------------- 1 | export class Company { 2 | id: number; 3 | name: string; 4 | weblink: string; 5 | logo: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | .footer-social app-social { 2 | float: right; 3 | right: 10px; 4 | margin-right: 0; 5 | margin-left: 15px; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | @if (footer$ | async; as footer) { 2 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { Component, OnInit } from '@angular/core'; 3 | 4 | import { ConfigService } from '../shared/services/config.service'; 5 | import { Footer } from './footer.model'; 6 | import { AsyncPipe } from '@angular/common'; 7 | import { SocialComponent } from '../social/social.component'; 8 | import { RouterLink } from '@angular/router'; 9 | 10 | @Component({ 11 | selector: 'app-footer', 12 | templateUrl: './footer.component.html', 13 | styleUrls: ['./footer.component.css'], 14 | standalone: true, 15 | imports: [ 16 | RouterLink, 17 | SocialComponent, 18 | AsyncPipe, 19 | ], 20 | }) 21 | export class FooterComponent implements OnInit { 22 | // footer$: Observable = new Observable(); 23 | footer$: Observable