initial release
1
bizmatch/.angular/cache/17.2.1/bizmatch/.tsbuildinfo
vendored
Normal file
331
bizmatch/.angular/cache/17.2.1/vite/deps/_metadata.json
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"hash": "67af9537",
|
||||
"configHash": "34f84833",
|
||||
"lockfileHash": "c6a666e0",
|
||||
"browserHash": "ac877f75",
|
||||
"optimized": {
|
||||
"@angular/common": {
|
||||
"src": "../../../../../node_modules/@angular/common/fesm2022/common.mjs",
|
||||
"file": "@angular_common.js",
|
||||
"fileHash": "28f818f0",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/common/http": {
|
||||
"src": "../../../../../node_modules/@angular/common/fesm2022/http.mjs",
|
||||
"file": "@angular_common_http.js",
|
||||
"fileHash": "3e7d92bd",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/core": {
|
||||
"src": "../../../../../node_modules/@angular/core/fesm2022/core.mjs",
|
||||
"file": "@angular_core.js",
|
||||
"fileHash": "accc9b8c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/forms": {
|
||||
"src": "../../../../../node_modules/@angular/forms/fesm2022/forms.mjs",
|
||||
"file": "@angular_forms.js",
|
||||
"fileHash": "928647a5",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"src": "../../../../../node_modules/@angular/platform-browser/fesm2022/platform-browser.mjs",
|
||||
"file": "@angular_platform-browser.js",
|
||||
"fileHash": "9b924e7c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/platform-browser/animations": {
|
||||
"src": "../../../../../node_modules/@angular/platform-browser/fesm2022/animations.mjs",
|
||||
"file": "@angular_platform-browser_animations.js",
|
||||
"fileHash": "7495a616",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@angular/router": {
|
||||
"src": "../../../../../node_modules/@angular/router/fesm2022/router.mjs",
|
||||
"file": "@angular_router.js",
|
||||
"fileHash": "735c693a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@fortawesome/angular-fontawesome": {
|
||||
"src": "../../../../../node_modules/@fortawesome/angular-fontawesome/fesm2022/angular-fontawesome.mjs",
|
||||
"file": "@fortawesome_angular-fontawesome.js",
|
||||
"fileHash": "0cf8f8a4",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@fortawesome/free-regular-svg-icons": {
|
||||
"src": "../../../../../node_modules/@fortawesome/free-regular-svg-icons/index.mjs",
|
||||
"file": "@fortawesome_free-regular-svg-icons.js",
|
||||
"fileHash": "a6d77e90",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@fortawesome/free-solid-svg-icons": {
|
||||
"src": "../../../../../node_modules/@fortawesome/free-solid-svg-icons/index.mjs",
|
||||
"file": "@fortawesome_free-solid-svg-icons.js",
|
||||
"fileHash": "248c3041",
|
||||
"needsInterop": false
|
||||
},
|
||||
"browser-bunyan": {
|
||||
"src": "../../../../../node_modules/browser-bunyan/lib/index.m.js",
|
||||
"file": "browser-bunyan.js",
|
||||
"fileHash": "3556293f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"jwt-decode": {
|
||||
"src": "../../../../../node_modules/jwt-decode/build/esm/index.js",
|
||||
"file": "jwt-decode.js",
|
||||
"fileHash": "31be6951",
|
||||
"needsInterop": false
|
||||
},
|
||||
"keycloak-js": {
|
||||
"src": "../../../../../node_modules/keycloak-js/dist/keycloak.mjs",
|
||||
"file": "keycloak-js.js",
|
||||
"fileHash": "a0ad62ab",
|
||||
"needsInterop": false
|
||||
},
|
||||
"on-change": {
|
||||
"src": "../../../../../node_modules/on-change/index.js",
|
||||
"file": "on-change.js",
|
||||
"fileHash": "78a0866f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/api": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-api.mjs",
|
||||
"file": "primeng_api.js",
|
||||
"fileHash": "4db75e0f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/button": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-button.mjs",
|
||||
"file": "primeng_button.js",
|
||||
"fileHash": "1190de19",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/checkbox": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-checkbox.mjs",
|
||||
"file": "primeng_checkbox.js",
|
||||
"fileHash": "9193d0d3",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/chip": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-chip.mjs",
|
||||
"file": "primeng_chip.js",
|
||||
"fileHash": "d481740a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/confirmdialog": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-confirmdialog.mjs",
|
||||
"file": "primeng_confirmdialog.js",
|
||||
"fileHash": "27802851",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/confirmpopup": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-confirmpopup.mjs",
|
||||
"file": "primeng_confirmpopup.js",
|
||||
"fileHash": "07fa7368",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/divider": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-divider.mjs",
|
||||
"file": "primeng_divider.js",
|
||||
"fileHash": "d9ac7514",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/dropdown": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-dropdown.mjs",
|
||||
"file": "primeng_dropdown.js",
|
||||
"fileHash": "a52c3507",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/fileupload": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-fileupload.mjs",
|
||||
"file": "primeng_fileupload.js",
|
||||
"fileHash": "57b31a6c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/inputnumber": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-inputnumber.mjs",
|
||||
"file": "primeng_inputnumber.js",
|
||||
"fileHash": "2c9bde1c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/inputtext": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-inputtext.mjs",
|
||||
"file": "primeng_inputtext.js",
|
||||
"fileHash": "b03c8c78",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/inputtextarea": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-inputtextarea.mjs",
|
||||
"file": "primeng_inputtextarea.js",
|
||||
"fileHash": "890a3704",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/menubar": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-menubar.mjs",
|
||||
"file": "primeng_menubar.js",
|
||||
"fileHash": "573b8558",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/overlaypanel": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-overlaypanel.mjs",
|
||||
"file": "primeng_overlaypanel.js",
|
||||
"fileHash": "cbfd82db",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/paginator": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-paginator.mjs",
|
||||
"file": "primeng_paginator.js",
|
||||
"fileHash": "f1a55a1a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/progressspinner": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-progressspinner.mjs",
|
||||
"file": "primeng_progressspinner.js",
|
||||
"fileHash": "f063f0c5",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/ripple": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-ripple.mjs",
|
||||
"file": "primeng_ripple.js",
|
||||
"fileHash": "97e2f125",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/styleclass": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-styleclass.mjs",
|
||||
"file": "primeng_styleclass.js",
|
||||
"fileHash": "37c3b954",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/table": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-table.mjs",
|
||||
"file": "primeng_table.js",
|
||||
"fileHash": "7aeb3809",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/tabmenu": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-tabmenu.mjs",
|
||||
"file": "primeng_tabmenu.js",
|
||||
"fileHash": "19fb3789",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/toast": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-toast.mjs",
|
||||
"file": "primeng_toast.js",
|
||||
"fileHash": "68ec89d2",
|
||||
"needsInterop": false
|
||||
},
|
||||
"primeng/togglebutton": {
|
||||
"src": "../../../../../node_modules/primeng/fesm2022/primeng-togglebutton.mjs",
|
||||
"file": "primeng_togglebutton.js",
|
||||
"fileHash": "522ccb3c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"rxjs": {
|
||||
"src": "../../../../../node_modules/rxjs/dist/esm5/index.js",
|
||||
"file": "rxjs.js",
|
||||
"fileHash": "823e3e71",
|
||||
"needsInterop": false
|
||||
},
|
||||
"rxjs/operators": {
|
||||
"src": "../../../../../node_modules/rxjs/dist/esm5/operators/index.js",
|
||||
"file": "rxjs_operators.js",
|
||||
"fileHash": "b5e6296c",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-I7D7PQME": {
|
||||
"file": "chunk-I7D7PQME.js"
|
||||
},
|
||||
"chunk-KMR4QVQU": {
|
||||
"file": "chunk-KMR4QVQU.js"
|
||||
},
|
||||
"chunk-IU2G34K3": {
|
||||
"file": "chunk-IU2G34K3.js"
|
||||
},
|
||||
"chunk-VYYSRRQP": {
|
||||
"file": "chunk-VYYSRRQP.js"
|
||||
},
|
||||
"chunk-SRY2GXMH": {
|
||||
"file": "chunk-SRY2GXMH.js"
|
||||
},
|
||||
"chunk-2HEVCKUT": {
|
||||
"file": "chunk-2HEVCKUT.js"
|
||||
},
|
||||
"chunk-FLWDL4KY": {
|
||||
"file": "chunk-FLWDL4KY.js"
|
||||
},
|
||||
"chunk-B3BEBK2H": {
|
||||
"file": "chunk-B3BEBK2H.js"
|
||||
},
|
||||
"chunk-7NEKFKVF": {
|
||||
"file": "chunk-7NEKFKVF.js"
|
||||
},
|
||||
"chunk-RKMPS52T": {
|
||||
"file": "chunk-RKMPS52T.js"
|
||||
},
|
||||
"chunk-2QWPUIZJ": {
|
||||
"file": "chunk-2QWPUIZJ.js"
|
||||
},
|
||||
"chunk-XGK4THRG": {
|
||||
"file": "chunk-XGK4THRG.js"
|
||||
},
|
||||
"chunk-DFUOJE3F": {
|
||||
"file": "chunk-DFUOJE3F.js"
|
||||
},
|
||||
"chunk-NBIWWQDH": {
|
||||
"file": "chunk-NBIWWQDH.js"
|
||||
},
|
||||
"chunk-6M7HYOYM": {
|
||||
"file": "chunk-6M7HYOYM.js"
|
||||
},
|
||||
"chunk-3SQF7L7O": {
|
||||
"file": "chunk-3SQF7L7O.js"
|
||||
},
|
||||
"chunk-MI5BQO2G": {
|
||||
"file": "chunk-MI5BQO2G.js"
|
||||
},
|
||||
"chunk-BH46VVEZ": {
|
||||
"file": "chunk-BH46VVEZ.js"
|
||||
},
|
||||
"chunk-KAUMDUK6": {
|
||||
"file": "chunk-KAUMDUK6.js"
|
||||
},
|
||||
"chunk-6ZBHNYAL": {
|
||||
"file": "chunk-6ZBHNYAL.js"
|
||||
},
|
||||
"chunk-SLXWKEKP": {
|
||||
"file": "chunk-SLXWKEKP.js"
|
||||
},
|
||||
"chunk-O5UWSVSE": {
|
||||
"file": "chunk-O5UWSVSE.js"
|
||||
},
|
||||
"chunk-ESXUKNGR": {
|
||||
"file": "chunk-ESXUKNGR.js"
|
||||
},
|
||||
"chunk-SVLVPO4L": {
|
||||
"file": "chunk-SVLVPO4L.js"
|
||||
},
|
||||
"chunk-MDVT4WFW": {
|
||||
"file": "chunk-MDVT4WFW.js"
|
||||
},
|
||||
"chunk-UANYWW5J": {
|
||||
"file": "chunk-UANYWW5J.js"
|
||||
},
|
||||
"chunk-3Q7TKPWY": {
|
||||
"file": "chunk-3Q7TKPWY.js"
|
||||
},
|
||||
"chunk-SG3BCSKH": {
|
||||
"file": "chunk-SG3BCSKH.js"
|
||||
},
|
||||
"chunk-SAVXX6OM": {
|
||||
"file": "chunk-SAVXX6OM.js"
|
||||
},
|
||||
"chunk-PQ7O3X3G": {
|
||||
"file": "chunk-PQ7O3X3G.js"
|
||||
},
|
||||
"chunk-ASLTLD6L": {
|
||||
"file": "chunk-ASLTLD6L.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
bizmatch/.angular/cache/17.2.1/vite/deps/package.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
16
bizmatch/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
45
bizmatch/.eslintrc.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"browser": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"airbnb-typescript",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"eslint-config-prettier",
|
||||
"plugin:cypress/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module",
|
||||
"project": ["./tsconfig.json"]
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"import/no-unresolved": ["off"],
|
||||
"import/prefer-default-export": ["off"],
|
||||
"no-useless-constructor": "off",
|
||||
"@typescript-eslint/no-useless-constructor": ["error"],
|
||||
"@typescript-eslint/lines-between-class-members": ["off"],
|
||||
"no-param-reassign": ["off"],
|
||||
"max-classes-per-file": ["off"],
|
||||
"no-shadow": ["off"],
|
||||
"class-methods-use-this": ["off"],
|
||||
"react/jsx-filename-extension": ["off"],
|
||||
"import/no-cycle": ["off"],
|
||||
"radix": ["off"],
|
||||
"no-promise-executor-return": ["off"],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "enumMember",
|
||||
"format": ["UPPER_CASE", "PascalCase"]
|
||||
}
|
||||
],
|
||||
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"],
|
||||
"spaced-comment": ["off"],
|
||||
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }]
|
||||
}
|
||||
}
|
||||
4
bizmatch/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
bizmatch/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
bizmatch/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
27
bizmatch/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Bizmatch
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.1.2.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
116
bizmatch/angular.json
Normal file
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"bizmatch": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss",
|
||||
"skipTests": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/bizmatch",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"dev": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.dev.ts"
|
||||
}
|
||||
],
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "bizmatch:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "bizmatch:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development",
|
||||
"options": {"proxyConfig": "proxy.conf.json"}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "bizmatch:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
108
bizmatch/angular_copy.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"bizmatch": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss",
|
||||
"skipTests": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/bizmatch",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"server": "src/main.server.ts",
|
||||
"prerender": true,
|
||||
"ssr": {
|
||||
"entry": "server.ts"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "bizmatch:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "bizmatch:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development",
|
||||
"options": {"proxyConfig": "proxy.conf.json"}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "bizmatch:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1164
bizmatch/dist/bizmatch/3rdpartylicenses.txt
vendored
Normal file
70
bizmatch/dist/bizmatch/browser/assets/data/listings.json
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
[
|
||||
{
|
||||
"id":"1",
|
||||
"userId":"14a05316-cb85-4c67-86bc-4a2083ff6af7",
|
||||
"listingsCategory": "business",
|
||||
"title": "Industrial Service Company In Corpus Christi For Sale - 1954",
|
||||
"summary": ["Asking price: $5,500,000","Sales revenue: $1,200,000","Net profit: $650,000"],
|
||||
"description": ["This company services a wide variety of industries. Asking price includes Business and the Real Estate and is approx 30,000 sq ft with room for expansion including approx 5 acres. Absentee run business."],
|
||||
"type": "Industrial Services",
|
||||
"location": "Texas",
|
||||
"price":5500000,
|
||||
"salesRevenue":1200000,
|
||||
"cashFlow":650000,
|
||||
"brokerLicencing":"TREC Broker #516788",
|
||||
"established":1954,
|
||||
"realEstateIncluded":false,
|
||||
"favoritesForUser":["e0811669-c7eb-4e5e-a699-e8334d5c5b01"]
|
||||
},
|
||||
{
|
||||
"id":"2",
|
||||
"userId":"e0811669-c7eb-4e5e-a699-e8334d5c5b01",
|
||||
"listingsCategory": "business",
|
||||
"title": "Coastal Bend Manufacturing Business Plastic Injection For Sale - 1950",
|
||||
"summary": ["Asking price: $165,000","Sales revenue: Undisclosed","Net profit: Undisclosed"],
|
||||
"description": [""],
|
||||
"type": "Manufacturing",
|
||||
"location": "Texas",
|
||||
"price":165000,
|
||||
"salesRevenue":null,
|
||||
"cashFlow":null,
|
||||
"brokerLicencing":"TREC Broker #516788",
|
||||
"established":1950,
|
||||
"realEstateIncluded":false,
|
||||
"favoritesForUser":["828cc120-51e9-4baa-9a33-a82608fe66b4"]
|
||||
},
|
||||
{
|
||||
"id":"3",
|
||||
"userId":"e0811669-c7eb-4e5e-a699-e8334d5c5b01",
|
||||
"listingsCategory": "business",
|
||||
"title": "Corner Property On Everhart South-side Corpus Christi For Sale - 1944",
|
||||
"summary": ["Asking price: $830,000","Sales revenue: Undisclosed","Net profit: Undisclosed"],
|
||||
"description": [""],
|
||||
"type": "Real Estate",
|
||||
"location": "Texas",
|
||||
"price":830000,
|
||||
"salesRevenue":null,
|
||||
"cashFlow":null,
|
||||
"brokerLicencing":"TREC Broker #516788",
|
||||
"established":1944,
|
||||
"realEstateIncluded":false,
|
||||
"favoritesForUser":[]
|
||||
},
|
||||
{
|
||||
"id":"4",
|
||||
"userId":"828cc120-51e9-4baa-9a33-a82608fe66b4",
|
||||
"listingsCategory": "business",
|
||||
"title": "Corpus Christi Dessert Business For Sale - 1941",
|
||||
"summary": ["Asking price: $124,900","Sales revenue: $225,000","Net profit: $50,000"],
|
||||
"description": [""],
|
||||
"type": "Food and Restaurant",
|
||||
"location": "Texas",
|
||||
"price":830000,
|
||||
"salesRevenue":225000,
|
||||
"cashFlow":50000,
|
||||
"brokerLicencing":"TREC Broker #516788",
|
||||
"established":1941,
|
||||
"realEstateIncluded":false,
|
||||
"favoritesForUser":[]
|
||||
}
|
||||
]
|
||||
21
bizmatch/dist/bizmatch/browser/assets/data/user.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"id":"1",
|
||||
"firstname":"Andreas",
|
||||
"lastname":"Knuth",
|
||||
"email":"andreas.knuth@gmail.com",
|
||||
"nickname":"aknuth",
|
||||
"displayName":"Andreas Knuth",
|
||||
"subscriptions":[{
|
||||
"id":"1",
|
||||
"level":"Business Broker",
|
||||
"start":"2024-02-12T21:54:20.603Z",
|
||||
"modified":"2024-02-12T21:54:20.603Z",
|
||||
"end":"9999-02-12T21:54:20.603Z",
|
||||
"status":"active",
|
||||
"invoices":[{
|
||||
"date":"2024-02-12T21:54:20.603Z",
|
||||
"id":"C991853B99",
|
||||
"price":0
|
||||
}]
|
||||
}]
|
||||
}
|
||||
BIN
bizmatch/dist/bizmatch/browser/assets/images/avatar-f-3.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/bw-sky.jpg
vendored
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/corpusChristiSkyline.jpg
vendored
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/header-logo.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/index-bg.jpg
vendored
Normal file
|
After Width: | Height: | Size: 662 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/index-bg.webp
vendored
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
bizmatch/dist/bizmatch/browser/assets/images/placeholder.png
vendored
Normal file
|
After Width: | Height: | Size: 667 B |
9
bizmatch/dist/bizmatch/browser/assets/images/pricing-4.svg
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
7
bizmatch/dist/bizmatch/browser/assets/silent-check-sso.html
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
parent.postMessage(location.href, location.origin);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
bizmatch/dist/bizmatch/browser/favicon.ico
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
17
bizmatch/dist/bizmatch/browser/index.html
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/Inter-italic.var-SWFAXF2C.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/Inter-roman.var-WIJJYAE4.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/bw-sky-5AJIF4XQ.jpg
vendored
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
bizmatch/dist/bizmatch/browser/media/color-3LUHUBGQ.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
bizmatch/dist/bizmatch/browser/media/fa-brands-400-4RSXLDQT.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-brands-400-RP3MZ4AX.ttf
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-regular-400-6ODLNN6G.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-regular-400-VE33OVPX.ttf
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-solid-900-BALFL4QR.ttf
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-solid-900-ZZETRIYD.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-v4compatibility-5WGRUVBC.woff2
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/fa-v4compatibility-7DCIFHD7.ttf
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/hue-RMMBQOAC.png
vendored
Normal file
|
After Width: | Height: | Size: 293 B |
BIN
bizmatch/dist/bizmatch/browser/media/index-bg-6QTG77KN.jpg
vendored
Normal file
|
After Width: | Height: | Size: 662 KiB |
BIN
bizmatch/dist/bizmatch/browser/media/index-bg-M7JGHGUV.webp
vendored
Normal file
|
After Width: | Height: | Size: 60 KiB |
9
bizmatch/dist/bizmatch/browser/media/pricing-4-AFBB6HYW.svg
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
bizmatch/dist/bizmatch/browser/media/primeicons-77WLEVE2.eot
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/primeicons-7C46RJHE.woff
vendored
Normal file
BIN
bizmatch/dist/bizmatch/browser/media/primeicons-AEJFRHCW.ttf
vendored
Normal file
292
bizmatch/dist/bizmatch/browser/media/primeicons-SQ5LETCD.svg
vendored
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
bizmatch/dist/bizmatch/browser/media/primeicons-XI7ZC3P3.woff2
vendored
Normal file
1
bizmatch/dist/bizmatch/browser/styles-THR3JRNJ.css
vendored
Normal file
61
bizmatch/package.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "bizmatch",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"build.dev": "ng build --configuration dev",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"serve:ssr:bizmatch": "node dist/bizmatch/server/server.mjs"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.2.2",
|
||||
"@angular/common": "^17.2.2",
|
||||
"@angular/compiler": "^17.2.2",
|
||||
"@angular/core": "^17.2.2",
|
||||
"@angular/forms": "^17.2.2",
|
||||
"@angular/platform-browser": "^17.2.2",
|
||||
"@angular/platform-browser-dynamic": "^17.2.2",
|
||||
"@angular/platform-server": "^17.2.2",
|
||||
"@angular/router": "^17.2.2",
|
||||
"@fortawesome/angular-fontawesome": "^0.14.1",
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"browser-bunyan": "^1.8.0",
|
||||
"express": "^4.18.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"keycloak-js": "^23.0.7",
|
||||
"memoize-one": "^6.0.0",
|
||||
"on-change": "^5.0.1",
|
||||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^17.6.0",
|
||||
"rxjs": "~7.8.1",
|
||||
"tslib": "^2.3.0",
|
||||
"urlcat": "^3.1.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.2.1",
|
||||
"@angular/cli": "^17.2.1",
|
||||
"@angular/compiler-cli": "^17.2.2",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jasmine": "~5.1.4",
|
||||
"@types/node": "^20.11.20",
|
||||
"jasmine-core": "~5.1.2",
|
||||
"karma": "~6.4.2",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.1",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.3.3"
|
||||
}
|
||||
}
|
||||
6
bizmatch/proxy.conf.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
56
bizmatch/server.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
// import { APP_BASE_HREF } from '@angular/common';
|
||||
// import { CommonEngine } from '@angular/ssr';
|
||||
// import express from 'express';
|
||||
// import { fileURLToPath } from 'node:url';
|
||||
// import { dirname, join, resolve } from 'node:path';
|
||||
// import bootstrap from './src/main.server';
|
||||
|
||||
// // The Express app is exported so that it can be used by serverless Functions.
|
||||
// export function app(): express.Express {
|
||||
// const server = express();
|
||||
// const serverDistFolder = dirname(fileURLToPath(import.meta.url));
|
||||
// const browserDistFolder = resolve(serverDistFolder, '../browser');
|
||||
// const indexHtml = join(serverDistFolder, 'index.server.html');
|
||||
|
||||
// const commonEngine = new CommonEngine();
|
||||
|
||||
// server.set('view engine', 'html');
|
||||
// server.set('views', browserDistFolder);
|
||||
|
||||
// // Example Express Rest API endpoints
|
||||
// // server.get('/api/**', (req, res) => { });
|
||||
// // Serve static files from /browser
|
||||
// server.get('*.*', express.static(browserDistFolder, {
|
||||
// maxAge: '1y'
|
||||
// }));
|
||||
|
||||
// // All regular routes use the Angular engine
|
||||
// server.get('*', (req, res, next) => {
|
||||
// const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
// commonEngine
|
||||
// .render({
|
||||
// bootstrap,
|
||||
// documentFilePath: indexHtml,
|
||||
// url: `${protocol}://${headers.host}${originalUrl}`,
|
||||
// publicPath: browserDistFolder,
|
||||
// providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
|
||||
// })
|
||||
// .then((html) => res.send(html))
|
||||
// .catch((err) => next(err));
|
||||
// });
|
||||
|
||||
// return server;
|
||||
// }
|
||||
|
||||
// function run(): void {
|
||||
// const port = process.env['PORT'] || 4000;
|
||||
|
||||
// // Start up the Node server
|
||||
// const server = app();
|
||||
// server.listen(port, () => {
|
||||
// console.log(`Node Express server listening on http://localhost:${port}`);
|
||||
// });
|
||||
// }
|
||||
|
||||
// run();
|
||||
14
bizmatch/src/app/app.component.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
@if (actualRoute !=='home' && actualRoute !=='pricing'){
|
||||
<header></header>
|
||||
}
|
||||
<router-outlet></router-outlet>
|
||||
@if (loadingService.isLoading$ | async) {
|
||||
<div class="progress-spinner flex h-full align-items-center justify-content-center">
|
||||
<p-progressSpinner></p-progressSpinner>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<footer></footer>
|
||||
</div>
|
||||
10
bizmatch/src/app/app.component.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
/* Optional: Padding für den Inhalt, um sicherzustellen, dass er nicht direkt am Footer klebt */
|
||||
// padding-bottom: 20px;
|
||||
}
|
||||
59
bizmatch/src/app/app.component.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { LoadingService } from './services/loading.service';
|
||||
import { HomeComponent } from './pages/home/home.component';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { KeycloakService } from './services/keycloak.service';
|
||||
import { KeycloakEventType } from './models/keycloak-event';
|
||||
import { ListingCriteria, User } from './models/main.model';
|
||||
import { createGenericObject } from './utils/utils';
|
||||
import onChange from 'on-change';
|
||||
import { UserService } from './services/user.service';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterOutlet, HeaderComponent, ProgressSpinnerModule, FooterComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'bizmatch';
|
||||
actualRoute ='';
|
||||
user:User;
|
||||
listingCriteria:ListingCriteria = onChange(createGenericObject<ListingCriteria>(),(path, value, previousValue, applyData)=>{
|
||||
sessionStorage.setItem('criteria',JSON.stringify(value));
|
||||
});
|
||||
public constructor(public loadingService: LoadingService, private router: Router,private activatedRoute: ActivatedRoute, private keycloakService:KeycloakService,private userService:UserService) {
|
||||
this.router.events.pipe(
|
||||
filter(event => event instanceof NavigationEnd)
|
||||
).subscribe(() => {
|
||||
let currentRoute = this.activatedRoute.root;
|
||||
while (currentRoute.children[0] !== undefined) {
|
||||
currentRoute = currentRoute.children[0];
|
||||
}
|
||||
// Hier haben Sie Zugriff auf den aktuellen Route-Pfad
|
||||
this.actualRoute=currentRoute.snapshot.url[0].path
|
||||
});
|
||||
|
||||
// keycloakService.keycloakEvents$.subscribe({
|
||||
// next(event) {
|
||||
// if (event.type == KeycloakEventType.OnTokenExpired) {
|
||||
// keycloakService.updateToken(20);
|
||||
// }
|
||||
// if (event.type == KeycloakEventType.OnActionUpdate) {
|
||||
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
ngOnInit(){
|
||||
this.user = this.userService.getUser();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
11
bizmatch/src/app/app.config.server.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
||||
import { provideServerRendering } from '@angular/platform-server';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
const serverConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideServerRendering()
|
||||
]
|
||||
};
|
||||
|
||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||
56
bizmatch/src/app/app.config.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { APP_INITIALIZER, ApplicationConfig, importProvidersFrom } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||
import { HTTP_INTERCEPTORS, provideHttpClient, withFetch, withInterceptorsFromDi } from '@angular/common/http';
|
||||
import { environment } from '../environments/environment';
|
||||
import { SelectOptionsService } from './services/select-options.service';
|
||||
import { KeycloakService } from './services/keycloak.service';
|
||||
import { UserService } from './services/user.service';
|
||||
// provideClientHydration()
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
{provide:KeycloakService},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initializeKeycloak,
|
||||
multi: true,
|
||||
deps: [KeycloakService],
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initServices,
|
||||
multi: true,
|
||||
deps: [SelectOptionsService],
|
||||
},
|
||||
provideRouter(routes),provideAnimations()
|
||||
]
|
||||
};
|
||||
function initUserService(userService:UserService) {
|
||||
return () => {
|
||||
//selectOptions.init();
|
||||
}
|
||||
}
|
||||
function initServices(selectOptions:SelectOptionsService) {
|
||||
return () => {
|
||||
selectOptions.init();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeKeycloak(keycloak: KeycloakService) {
|
||||
return () =>
|
||||
keycloak.init({
|
||||
config: {
|
||||
url: environment.keycloak.url,
|
||||
realm: environment.keycloak.realm,
|
||||
clientId: environment.keycloak.clientId,
|
||||
},
|
||||
initOptions: {
|
||||
onLoad: 'check-sso',
|
||||
silentCheckSsoRedirectUri: (<any>window).location.origin + '/assets/silent-check-sso.html'
|
||||
},
|
||||
});
|
||||
}
|
||||
75
bizmatch/src/app/app.routes.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { ListingsComponent } from './pages/listings/listings.component';
|
||||
import { HomeComponent } from './pages/home/home.component';
|
||||
import { DetailsComponent } from './pages/details/details.component';
|
||||
import { AccountComponent } from './pages/subscription/account/account.component';
|
||||
import { EditListingComponent } from './pages/subscription/edit-listing/edit-listing.component';
|
||||
import { MyListingComponent } from './pages/subscription/my-listing/my-listing.component';
|
||||
import { FavoritesComponent } from './pages/subscription/favorites/favorites.component';
|
||||
import { EmailUsComponent } from './pages/subscription/email-us/email-us.component';
|
||||
import { authGuard } from './guards/auth.guard';
|
||||
import { PricingComponent } from './pages/pricing/pricing.component';
|
||||
import { LogoutComponent } from './components/logout/logout.component';
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'listings/:type',
|
||||
component: ListingsComponent,
|
||||
},
|
||||
// Umleitung von /listing zu /listing/business
|
||||
{
|
||||
path: 'listings',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'listings/business',
|
||||
runGuardsAndResolvers:'always'
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
component: HomeComponent,
|
||||
},
|
||||
{
|
||||
path: 'details/:id',
|
||||
component: DetailsComponent,
|
||||
},
|
||||
{
|
||||
path: 'account',
|
||||
component: AccountComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'editListing/:id',
|
||||
component: EditListingComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'createListing',
|
||||
component: EditListingComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'myListings',
|
||||
component: MyListingComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'myFavorites',
|
||||
component: FavoritesComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'emailUs',
|
||||
component: EmailUsComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'logout',
|
||||
component: LogoutComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'pricing',
|
||||
component: PricingComponent
|
||||
},
|
||||
{ path: '**', redirectTo: 'home' },
|
||||
];
|
||||
27
bizmatch/src/app/components/footer/footer.component.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="surface-0 px-4 py-4 md:px-6 lg:px-8">
|
||||
<div class="surface-0">
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-3 md:mb-0 mb-3">
|
||||
<img src="assets/images/header-logo.png" alt="footer sections" height="30" class="mr-3">
|
||||
<div class="text-500">© 2024 Bizmatch All rights reserved.</div>
|
||||
<!-- <div class="text-gray-300 font-bold text-5xl">Bastion</div> -->
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="text-black mb-4 flex flex-wrap" style="max-width: 290px">BizMatch, Inc., 1001 Blucher Street, Corpus Christi, Texas 78401</div>
|
||||
<div class="text-black mb-3"><i class="text-white pi pi-phone surface-800 border-round p-1 mr-2"></i>1-800-840-6025</div>
|
||||
<div class="text-black mb-3"><i class="text-white pi pi-inbox surface-800 border-round p-1 mr-2"></i>bizmatch@biz-match.com</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-3 text-500">
|
||||
<div class="text-black font-bold line-height-3 mb-3">Legal</div>
|
||||
<a class="line-height-3 block cursor-pointer mb-2">Terms of use</a>
|
||||
<a class="line-height-3 block cursor-pointer mb-2">Privacy statement</a>
|
||||
</div>
|
||||
<div class="col-12 md:col-3 text-500">
|
||||
<div class="text-black font-bold line-height-3 mb-3">Actions</div>
|
||||
<a *ngIf="!userService.isLoggedIn()" (click)="login()" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline">Login</a>
|
||||
<a *ngIf="userService.isLoggedIn()" [routerLink]="['/account']" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline">Account</a>
|
||||
<a *ngIf="userService.isLoggedIn()" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline" (click)="userService.logout()">Log Out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
6
bizmatch/src/app/components/footer/footer.component.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
:host{
|
||||
height: 192px;
|
||||
}
|
||||
div {
|
||||
font-size: small;
|
||||
}
|
||||
26
bizmatch/src/app/components/footer/footer.component.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import {StyleClassModule} from 'primeng/styleclass';
|
||||
import { KeyValue } from '../../models/main.model';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { SharedModule } from '../../shared/shared/shared.module';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'footer',
|
||||
standalone: true,
|
||||
imports: [SharedModule],
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrl: './footer.component.scss'
|
||||
})
|
||||
export class FooterComponent {
|
||||
constructor(public userService:UserService){}
|
||||
login(){
|
||||
this.userService.login(window.location.href);
|
||||
}
|
||||
}
|
||||
11
bizmatch/src/app/components/header/header.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<div class="wrapper">
|
||||
<div class="pl-3 flex align-items-center gap-2">
|
||||
<a routerLink="/home"><img src="assets/images/header-logo.png" height="40" alt="bizmatch" /></a>
|
||||
<p-tabMenu [model]="tabItems" ariaLabelledBy="label" styleClass="flex" [activeItem]="activeItem">
|
||||
</p-tabMenu>
|
||||
<p-menubar [model]="menuItems"></p-menubar>
|
||||
<div *ngIf="user$ | async as user else empty">Welcome, {{user.firstname}}</div>
|
||||
<ng-template #empty>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
13
bizmatch/src/app/components/header/header.component.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
::ng-deep p-menubarsub{
|
||||
margin-left: auto;
|
||||
}
|
||||
::ng-deep .p-tabmenu .p-tabmenu-nav .p-tabmenuitem .p-menuitem-link{
|
||||
border:1px solid #ffffff;
|
||||
}
|
||||
::ng-deep .p-tabmenu .p-tabmenu-nav .p-tabmenuitem.p-highlight .p-menuitem-link {
|
||||
border-bottom: 2px solid #3B82F6 !important;
|
||||
}
|
||||
::ng-deep .p-menubar{
|
||||
border:unset;
|
||||
background: unset;
|
||||
}
|
||||
115
bizmatch/src/app/components/header/header.component.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { MenuItem } from 'primeng/api';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { MenubarModule } from 'primeng/menubar';
|
||||
import { OverlayPanelModule } from 'primeng/overlaypanel';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { User } from '../../models/main.model';
|
||||
import { TabMenuModule } from 'primeng/tabmenu';
|
||||
import { Observable } from 'rxjs';
|
||||
import { faUserGear } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'header',
|
||||
standalone: true,
|
||||
imports: [CommonModule, MenubarModule, ButtonModule, OverlayPanelModule, TabMenuModule ],
|
||||
templateUrl: './header.component.html',
|
||||
styleUrl: './header.component.scss'
|
||||
})
|
||||
export class HeaderComponent {
|
||||
public buildVersion = environment.buildVersion;
|
||||
user:User;
|
||||
user$:Observable<User>
|
||||
public tabItems: MenuItem[];
|
||||
public menuItems: MenuItem[];
|
||||
activeItem
|
||||
faUserGear=faUserGear
|
||||
constructor(public userService: UserService,private router: Router) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(){
|
||||
this.user$=this.userService.getUserObservable();
|
||||
this.tabItems = [
|
||||
{
|
||||
label: 'Businesses for Sale',
|
||||
routerLink: '/listings/business',
|
||||
fragment:''
|
||||
},
|
||||
{
|
||||
label: 'Professionals/Brokers Directory',
|
||||
routerLink: '/listings/professionals_brokers',
|
||||
fragment:''
|
||||
},
|
||||
{
|
||||
label: 'Investment Property',
|
||||
routerLink: '/listings/investment',
|
||||
fragment:''
|
||||
}
|
||||
];
|
||||
this.menuItems = [
|
||||
{
|
||||
label: 'User Actions',
|
||||
icon: 'fas fa-cog',
|
||||
items: [
|
||||
{
|
||||
label: 'Account',
|
||||
icon: 'pi pi-user',
|
||||
routerLink: '/account',
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Create Listing',
|
||||
icon: 'pi pi-plus-circle',
|
||||
routerLink: "/createListing",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Listings',
|
||||
icon: 'pi pi-list',
|
||||
routerLink:"/myListings",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Favorites',
|
||||
icon: 'pi pi-star',
|
||||
routerLink:"/myFavorites",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'EMail Us',
|
||||
icon: 'fa-regular fa-envelope',
|
||||
routerLink:"/emailUs",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Logout',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
routerLink:"/logout",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Login',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
//routerLink:"/account",
|
||||
command: () => this.login(),
|
||||
visible: !this.isUserLoogedIn()
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
this.activeItem=this.tabItems[0];
|
||||
}
|
||||
navigateWithState(dest: string, state: any) {
|
||||
this.router.navigate([dest], { state: state });
|
||||
}
|
||||
isUserLoogedIn(){
|
||||
return this.userService?.isLoggedIn();
|
||||
}
|
||||
login(){
|
||||
this.userService.login(window.location.href);
|
||||
}
|
||||
}
|
||||
1
bizmatch/src/app/components/logout/logout.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<p>logout works!</p>
|
||||
16
bizmatch/src/app/components/logout/logout.component.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'logout',
|
||||
standalone: true,
|
||||
imports: [CommonModule,RouterModule],
|
||||
template:``
|
||||
})
|
||||
export class LogoutComponent {
|
||||
constructor(private userService:UserService){
|
||||
userService.logout();
|
||||
}
|
||||
}
|
||||
38
bizmatch/src/app/guards/auth.guard.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { CanMatchFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import { inject } from '@angular/core';
|
||||
|
||||
// Services
|
||||
import { UserService } from '../services/user.service';
|
||||
|
||||
export const authGuard: CanMatchFn = async (route, segments): Promise<boolean | UrlTree> => {
|
||||
const router = inject(Router);
|
||||
const userService = inject(UserService);
|
||||
|
||||
const authenticated: boolean = userService.isLoggedIn();
|
||||
if (!authenticated) {
|
||||
console.log(window.location.origin)
|
||||
console.log(window.location.href)
|
||||
await userService.login(`${window.location.origin}${segments['url']}`);
|
||||
}
|
||||
|
||||
// Get the user Keycloak roles and the required from the route
|
||||
const roles: string[] = userService.getUserRoles();//keycloakService.getUserRoles(true);
|
||||
const requiredRoles = route.data?.['roles'];
|
||||
|
||||
// Allow the user to proceed if no additional roles are required to access the route
|
||||
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow the user to proceed if ALL of the required roles are present
|
||||
const authorized = requiredRoles.every((role) => roles.includes(role));
|
||||
// Allow the user to proceed if ONE of the required roles is present
|
||||
//const authorized = requiredRoles.some((role) => roles.includes(role));
|
||||
|
||||
if (authorized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Display my custom HTTP 403 access denied page
|
||||
return router.createUrlTree(['/access']);
|
||||
};
|
||||
77
bizmatch/src/app/interceptors/keycloak-bearer.interceptor.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import {
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptorFn,
|
||||
HttpHandlerFn,
|
||||
} from '@angular/common/http';
|
||||
|
||||
import { Observable, combineLatest, from, of } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
|
||||
import { KeycloakService } from '../services/keycloak.service';
|
||||
import { ExcludedUrlRegex } from '../models/keycloak-options';
|
||||
|
||||
export const keycloakBearerInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
//return next(req);
|
||||
const keycloak = inject(KeycloakService);
|
||||
const { enableBearerInterceptor, excludedUrls } = keycloak;
|
||||
if (!enableBearerInterceptor) {
|
||||
return next(req);
|
||||
}
|
||||
|
||||
const shallPass: boolean =
|
||||
!keycloak.shouldAddToken(req) ||
|
||||
excludedUrls.findIndex((item) => isUrlExcluded(req, item)) > -1;
|
||||
if (shallPass) {
|
||||
return next(req);
|
||||
}
|
||||
|
||||
return combineLatest([
|
||||
from(conditionallyUpdateToken(req)),
|
||||
of(keycloak.isLoggedIn()),
|
||||
]).pipe(
|
||||
mergeMap(([_, isLoggedIn]) =>
|
||||
isLoggedIn ? handleRequestWithTokenHeader(req, next) : next(req)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
function isUrlExcluded(
|
||||
{ method, url }: HttpRequest<unknown>,
|
||||
{ urlPattern, httpMethods }: ExcludedUrlRegex
|
||||
): boolean {
|
||||
const httpTest =
|
||||
httpMethods.length === 0 ||
|
||||
httpMethods.join().indexOf(method.toUpperCase()) > -1;
|
||||
|
||||
const urlTest = urlPattern.test(url);
|
||||
|
||||
return httpTest && urlTest;
|
||||
}
|
||||
|
||||
function handleRequestWithTokenHeader(
|
||||
req: HttpRequest<unknown>,
|
||||
next: HttpHandlerFn
|
||||
): Observable<HttpEvent<unknown>> {
|
||||
return this.keycloak.addTokenToHeader(req.headers).pipe(
|
||||
mergeMap((headersWithBearer:string) => {
|
||||
const kcReq = req.clone({
|
||||
headers: req.headers.set('Authorization', headersWithBearer)
|
||||
});//req.clone({ headers: headersWithBearer });
|
||||
return next(kcReq);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function conditionallyUpdateToken(
|
||||
req: HttpRequest<unknown>
|
||||
): Promise<boolean> {
|
||||
if (this.keycloak.shouldUpdateToken(req)) {
|
||||
return await this.keycloak.updateToken();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
21
bizmatch/src/app/interceptors/loading.interceptor.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { HttpInterceptorFn } from '@angular/common/http';
|
||||
import { inject } from '@angular/core';
|
||||
import { tap } from 'rxjs';
|
||||
import { v4 } from 'uuid';
|
||||
import { LoadingService } from '../services/loading.service';
|
||||
|
||||
export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const loadingService = inject(LoadingService);
|
||||
|
||||
const requestId = `HTTP-${v4()}`;
|
||||
|
||||
loadingService.startLoading(requestId);
|
||||
|
||||
return next(req).pipe(
|
||||
tap({
|
||||
finalize: () => loadingService.stopLoading(requestId),
|
||||
error: () => loadingService.stopLoading(requestId),
|
||||
complete: () => loadingService.stopLoading(requestId),
|
||||
})
|
||||
);
|
||||
};
|
||||
52
bizmatch/src/app/models/keycloak-event.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
export enum KeycloakEventType {
|
||||
/**
|
||||
* Called if there was an error during authentication.
|
||||
*/
|
||||
OnAuthError,
|
||||
/**
|
||||
* Called if the user is logged out
|
||||
* (will only be called if the session status iframe is enabled, or in Cordova mode).
|
||||
*/
|
||||
OnAuthLogout,
|
||||
/**
|
||||
* Called if there was an error while trying to refresh the token.
|
||||
*/
|
||||
OnAuthRefreshError,
|
||||
/**
|
||||
* Called when the token is refreshed.
|
||||
*/
|
||||
OnAuthRefreshSuccess,
|
||||
/**
|
||||
* Called when a user is successfully authenticated.
|
||||
*/
|
||||
OnAuthSuccess,
|
||||
/**
|
||||
* Called when the adapter is initialized.
|
||||
*/
|
||||
OnReady,
|
||||
/**
|
||||
* Called when the access token is expired. If a refresh token is available the token
|
||||
* can be refreshed with updateToken, or in cases where it is not (that is, with implicit flow)
|
||||
* you can redirect to login screen to obtain a new access token.
|
||||
*/
|
||||
OnTokenExpired,
|
||||
/**
|
||||
* Called when a AIA has been requested by the application.
|
||||
*/
|
||||
OnActionUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
* Structure of an event triggered by Keycloak, contains it's type
|
||||
* and arguments (if any).
|
||||
*/
|
||||
export interface KeycloakEvent {
|
||||
/**
|
||||
* Event type as described at {@link KeycloakEventType}.
|
||||
*/
|
||||
type: KeycloakEventType;
|
||||
/**
|
||||
* Arguments from the keycloak-js event function.
|
||||
*/
|
||||
args?: unknown;
|
||||
}
|
||||
142
bizmatch/src/app/models/keycloak-options.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Mauricio Gemelli Vigolo and contributors.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be
|
||||
* found in the LICENSE file at https://github.com/mauriciovigolo/keycloak-angular/blob/main/LICENSE.md
|
||||
*/
|
||||
|
||||
import { HttpRequest } from '@angular/common/http';
|
||||
|
||||
/**
|
||||
* HTTP Methods
|
||||
*/
|
||||
export type HttpMethods =
|
||||
| 'GET'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'DELETE'
|
||||
| 'OPTIONS'
|
||||
| 'HEAD'
|
||||
| 'PATCH';
|
||||
|
||||
/**
|
||||
* ExcludedUrl type may be used to specify the url and the HTTP method that
|
||||
* should not be intercepted by the KeycloakBearerInterceptor.
|
||||
*
|
||||
* Example:
|
||||
* const excludedUrl: ExcludedUrl[] = [
|
||||
* {
|
||||
* url: 'reports/public'
|
||||
* httpMethods: ['GET']
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* In the example above for URL reports/public and HTTP Method GET the
|
||||
* bearer will not be automatically added.
|
||||
*
|
||||
* If the url is informed but httpMethod is undefined, then the bearer
|
||||
* will not be added for all HTTP Methods.
|
||||
*/
|
||||
export interface ExcludedUrl {
|
||||
url: string;
|
||||
httpMethods?: HttpMethods[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to ExcludedUrl, contains the HTTP methods and a regex to
|
||||
* include the url patterns.
|
||||
* This interface is used internally by the KeycloakService.
|
||||
*/
|
||||
export interface ExcludedUrlRegex {
|
||||
urlPattern: RegExp;
|
||||
httpMethods?: HttpMethods[];
|
||||
}
|
||||
|
||||
/**
|
||||
* keycloak-angular initialization options.
|
||||
*/
|
||||
export interface KeycloakOptions {
|
||||
/**
|
||||
* Configs to init the keycloak-js library. If undefined, will look for a keycloak.json file
|
||||
* at root of the project.
|
||||
* If not undefined, can be a string meaning the url to the keycloak.json file or an object
|
||||
* of {@link Keycloak.KeycloakConfig}. Use this configuration if you want to specify the keycloak server,
|
||||
* realm, clientId. This is usefull if you have different configurations for production, stage
|
||||
* and development environments. Hint: Make use of Angular environment configuration.
|
||||
*/
|
||||
config?: string | Keycloak.KeycloakConfig;
|
||||
/**
|
||||
* Options to initialize the Keycloak adapter, matches the options as provided by Keycloak itself.
|
||||
*/
|
||||
initOptions?: Keycloak.KeycloakInitOptions;
|
||||
/**
|
||||
* By default all requests made by Angular HttpClient will be intercepted in order to
|
||||
* add the bearer in the Authorization Http Header. However, if this is a not desired
|
||||
* feature, the enableBearerInterceptor must be false.
|
||||
*
|
||||
* Briefly, if enableBearerInterceptor === false, the bearer will not be added
|
||||
* to the authorization header.
|
||||
*
|
||||
* The default value is true.
|
||||
*/
|
||||
enableBearerInterceptor?: boolean;
|
||||
/**
|
||||
* Forces the execution of loadUserProfile after the keycloak initialization considering that the
|
||||
* user logged in.
|
||||
* This option is recommended if is desirable to have the user details at the beginning,
|
||||
* so after the login, the loadUserProfile function will be called and its value cached.
|
||||
*
|
||||
* The default value is true.
|
||||
*/
|
||||
loadUserProfileAtStartUp?: boolean;
|
||||
/**
|
||||
* @deprecated
|
||||
* String Array to exclude the urls that should not have the Authorization Header automatically
|
||||
* added. This library makes use of Angular Http Interceptor, to automatically add the Bearer
|
||||
* token to the request.
|
||||
*/
|
||||
bearerExcludedUrls?: (string | ExcludedUrl)[];
|
||||
/**
|
||||
* This value will be used as the Authorization Http Header name. The default value is
|
||||
* **Authorization**. If the backend expects requests to have a token in a different header, you
|
||||
* should change this value, i.e: **JWT-Authorization**. This will result in a Http Header
|
||||
* Authorization as "JWT-Authorization: bearer <token>".
|
||||
*/
|
||||
authorizationHeaderName?: string;
|
||||
/**
|
||||
* This value will be included in the Authorization Http Header param. The default value is
|
||||
* **Bearer**, which will result in a Http Header Authorization as "Authorization: Bearer <token>".
|
||||
*
|
||||
* If any other value is needed by the backend in the authorization header, you should change this
|
||||
* value.
|
||||
*
|
||||
* Warning: this value must be in compliance with the keycloak server instance and the adapter.
|
||||
*/
|
||||
bearerPrefix?: string;
|
||||
/**
|
||||
* This value will be used to determine whether or not the token needs to be updated. If the token
|
||||
* will expire is fewer seconds than the updateMinValidity value, then it will be updated.
|
||||
*
|
||||
* The default value is 20.
|
||||
*/
|
||||
updateMinValidity?: number;
|
||||
/**
|
||||
* A function that will tell the KeycloakBearerInterceptor whether to add the token to the request
|
||||
* or to leave the request as it is. If the returned value is `true`, the request will have the token
|
||||
* present on it. If it is `false`, the token will be left off the request.
|
||||
*
|
||||
* The default is a function that always returns `true`.
|
||||
*/
|
||||
shouldAddToken?: (request: HttpRequest<unknown>) => boolean;
|
||||
/**
|
||||
* A function that will tell the KeycloakBearerInterceptor if the token should be considered for
|
||||
* updating as a part of the request being made. If the returned value is `true`, the request will
|
||||
* check the token's expiry time and if it is less than the number of seconds configured by
|
||||
* updateMinValidity then it will be updated before the request is made. If the returned value is
|
||||
* false, the token will not be updated.
|
||||
*
|
||||
* The default is a function that always returns `true`.
|
||||
*/
|
||||
shouldUpdateToken?: (request: HttpRequest<unknown>) => boolean;
|
||||
}
|
||||
1
bizmatch/src/app/models/main.model.ts
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../common-models/src/main.model.ts
|
||||
146
bizmatch/src/app/pages/details/details.component.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<div class="surface-ground h-full">
|
||||
<div class="px-6 py-5">
|
||||
<div class="surface-card p-4 shadow-2 border-round">
|
||||
<div class="flex justify-content-between align-items-center align-content-center">
|
||||
<div class="font-medium text-3xl text-900 mb-3">{{listing?.title}}</div>
|
||||
<!-- <button pButton pRipple type="button" label="Go back to listings" icon="pi pi-user-plus" class="mr-3 p-button-rounded"></button> -->
|
||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="back()"></p-button>
|
||||
</div>
|
||||
<!-- <div class="text-500 mb-5">Egestas sed tempus urna et pharetra pharetra massa massa ultricies.</div> -->
|
||||
<div class="grid">
|
||||
<div class="col-12 md:col-6">
|
||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
||||
@if (listing && (listing.listingsCategory==='business' || listing.listingsCategory==='professionals_brokers')){
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Summary</div>
|
||||
<div class="w-full md:w-10">
|
||||
@for (summary of listing.summary; track summary; let idx = $index; let last = $last) {
|
||||
<div class="text-900">{{summary}}</div>
|
||||
@if (!last) {
|
||||
<br/>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
||||
<div class="text-900 w-full md:w-10 line-height-3">{{listing?.description}}</div>
|
||||
</li>
|
||||
@if (listing && (listing.listingsCategory==='business')){
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Category</div>
|
||||
<div class="text-900 w-full md:w-10">
|
||||
<p-chip [label]="selectOptions.getBusiness(listing.type)"></p-chip>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Asking Price</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.price | currency}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Real Estate Included</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.realEstateIncluded?'Yes':'No'}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Sales revenue</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.salesRevenue | currency}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Cash flow</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.cashFlow | currency}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Employees</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.employees}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Broker licensing</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.brokerLicencing}}</div>
|
||||
</li>
|
||||
}
|
||||
@if (listing && (listing.listingsCategory==='professionals_brokers')){
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Address</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.address}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">EMail</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.email}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Website</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.website}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Category</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.category}}</div>
|
||||
</li>
|
||||
}
|
||||
@if (listing && (listing.listingsCategory==='investment')){
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||
<div class="text-500 w-full md:w-2 font-medium">EMail</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.email}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Website</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.website}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Phone Number</div>
|
||||
<div class="text-900 w-full md:w-10">{{listing.phoneNumber}}</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@if(listing && user && (user.id===listing?.userId || isAdmin())){
|
||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editListing',listing.id]"></button>
|
||||
}
|
||||
</div>
|
||||
<div class="col-12 md:col-6">
|
||||
<div class="surface-card p-4 border-round p-fluid">
|
||||
<div class="font-medium text-xl text-primary text-900 mb-3">Contact The Author of This Listing
|
||||
</div>
|
||||
<div class="font-italic text-sm text-900 mb-5">Please Include your contact info below:</div>
|
||||
<div class="grid formgrid p-fluid">
|
||||
<div class="field mb-4 col-12 md:col-6">
|
||||
<label for="company_name" class="font-medium text-900">Your Name</label>
|
||||
<input id="company_name" type="text" pInputText>
|
||||
</div>
|
||||
<div class="field mb-4 col-12 md:col-6">
|
||||
<label for="invoice_id" class="font-medium text-900">Your Email</label>
|
||||
<input id="invoice_id" type="text" pInputText>
|
||||
</div>
|
||||
<div class="field mb-4 col-12 md:col-6">
|
||||
<label for="customer_name" class="font-medium text-900">Phone Number</label>
|
||||
<input id="customer_name" type="text" pInputText>
|
||||
</div>
|
||||
<div class="field mb-4 col-12 md:col-6">
|
||||
<label for="customer_email" class="font-medium text-900">Country/State</label>
|
||||
<input id="customer_email" type="text" pInputText>
|
||||
</div>
|
||||
<div class="surface-border border-top-1 opacity-50 mb-4 col-12"></div>
|
||||
<div class="field mb-4 col-12">
|
||||
<label for="notes" class="font-medium text-900">Questions/Comments</label>
|
||||
<textarea id="notes" pInputTextarea [autoResize]="true" [rows]="5"></textarea>
|
||||
</div>
|
||||
<div class="surface-border border-top-1 opacity-50 mb-4 col-12"></div>
|
||||
</div>
|
||||
<button pButton pRipple label="Submit" icon="pi pi-file" class="w-auto"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
50
bizmatch/src/app/pages/details/details.component.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, InvestmentsListing, KeyValue, ListingCriteria, ProfessionalsBrokersListing, User } from '../../models/main.model';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import data from '../../../assets/data/listings.json';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ListingsService } from '../../services/listings.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import onChange from 'on-change';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../utils/utils';
|
||||
@Component({
|
||||
selector: 'app-details',
|
||||
standalone: true,
|
||||
imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule,RouterModule],
|
||||
templateUrl: './details.component.html',
|
||||
styleUrl: './details.component.scss'
|
||||
})
|
||||
export class DetailsComponent {
|
||||
// listings: Array<BusinessListing>;
|
||||
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
listing: BusinessListing|ProfessionalsBrokersListing|InvestmentsListing;
|
||||
user:User;
|
||||
criteria:ListingCriteria
|
||||
constructor(private activatedRoute: ActivatedRoute,private listingsService:ListingsService,private router:Router,private userService:UserService,public selectOptions: SelectOptionsService){
|
||||
this.criteria = onChange(getCriteriaStateObject(),getSessionStorageHandler);
|
||||
}
|
||||
|
||||
async ngOnInit(){
|
||||
this.user = this.userService.getUser();
|
||||
this.listing=await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||
}
|
||||
back(){
|
||||
this.router.navigate(['listings',this.criteria.listingsCategory])
|
||||
}
|
||||
isAdmin(){
|
||||
return this.userService.hasAdminRole();
|
||||
}
|
||||
}
|
||||
93
bizmatch/src/app/pages/home/home.component.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<div class="container">
|
||||
<div class="wrapper">
|
||||
<div class="py-3 px-6 flex align-items-center justify-content-between relative">
|
||||
<a routerLink="/home"><img src="../../../assets/images/header-logo.png" alt="Image" height="50" ></a>
|
||||
<div
|
||||
class="align-items-center flex-grow-1 justify-content-between hidden lg:flex absolute lg:static w-full left-0 top-100 px-6 lg:px-0 shadow-2 lg:shadow-none z-2">
|
||||
<section></section>
|
||||
<ul
|
||||
class="list-none p-0 m-0 flex lg:align-items-center text-blue-900 select-none flex-column lg:flex-row cursor-pointer">
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Corporate</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Resources</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Pricing</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="flex justify-content-between lg:block border-top-1 lg:border-top-none border-gray-800 py-3 lg:py-0 mt-3 lg:mt-0">
|
||||
<!-- <p-button label="Account" class="ml-3 font-bold" [outlined]="true" severity="secondary" [routerLink]="['/account']"></p-button> -->
|
||||
<p-button label="Account" class="ml-3 font-bold" [outlined]="true" severity="secondary" (click)="account()"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-8 md:px-6 lg:px-8">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-12 lg:w-6 p-4">
|
||||
<h1 class="text-6xl font-bold text-blue-900 mt-0 mb-3">Find businesses for sale</h1>
|
||||
<p class="text-3xl text-blue-600 mt-0 mb-5">Arcu cursus euismod quis viverra nibh cras. Amet justo
|
||||
donec
|
||||
enim diam vulputate ut.</p>
|
||||
<ul class="list-none p-0 m-0">
|
||||
<li class="mb-3 flex align-items-center"><i
|
||||
class="pi pi-compass text-yellow-500 text-xl mr-2"></i><span
|
||||
class="text-blue-600 line-height-3">Senectus et netus et malesuada fames.</span></li>
|
||||
<li class="mb-3 flex align-items-center"><i
|
||||
class="pi pi-map text-yellow-500 text-xl mr-2"></i><span
|
||||
class="text-blue-600 line-height-3">Orci a scelerisque purus semper eget.</span></li>
|
||||
<li class="mb-3 flex align-items-center"><i
|
||||
class="pi pi-calendar text-yellow-500 text-xl mr-2"></i><span
|
||||
class="text-blue-600 line-height-3">Aenean sed adipiscing diam donec adipiscing
|
||||
tristique.</span></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div class="w-12 lg:w-6 text-center lg:text-right flex">
|
||||
<div class="mt-5">
|
||||
<ul class="flex flex-column align-items-left gap-3 px-2 py-3 list-none surface-border">
|
||||
<li><button pButton pRipple icon="pi pi-user" (click)="activeTabAction = 'business'"
|
||||
label="Businesses"
|
||||
[ngClass]="{'p-button-text text-700': activeTabAction !== 'business'}"></button></li>
|
||||
<li><button pButton pRipple icon="pi pi-globe" (click)="activeTabAction = 'professionals_brokers'"
|
||||
label="Professionals/Brokers Directory"
|
||||
[ngClass]="{'p-button-text text-700': activeTabAction != 'professionals_brokers'}"></button></li>
|
||||
<li><button pButton pRipple icon="pi pi-shield" (click)="activeTabAction = 'investment'"
|
||||
label="Investment Property"
|
||||
[ngClass]="{'p-button-text text-700': activeTabAction != 'investment'}"></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="flex flex-column align-items-right gap-3 px-2 py-3 my-3 surface-border">
|
||||
<p-dropdown [options]="selectOptions.typesOfBusiness" [(ngModel)]="criteria.type" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Category"
|
||||
[style]="{ width: '200px'}"></p-dropdown>
|
||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value"
|
||||
[showClear]="true" placeholder="Min Price" [style]="{ width: '200px'}"></p-dropdown>
|
||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value"
|
||||
[showClear]="true" placeholder="Max Price" [style]="{ width: '200px'}"></p-dropdown>
|
||||
<button pButton pRipple label="Find" class="ml-3 font-bold"
|
||||
[style]="{ width: '170px'}" (click)="search()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-12 flex justify-content-center">
|
||||
<button type="button" pButton pRipple label="Create Your Listing"
|
||||
class="block mt-7 mb-7 lg:mb-0 p-button-rounded p-button-success p-button-lg font-medium" [routerLink]="userService.isLoggedIn()?'/createListing':'/pricing'"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
17
bizmatch/src/app/pages/home/home.component.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
:host {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.container {
|
||||
background-image: url(../../../assets/images/index-bg.webp);
|
||||
//background-image: url(../../../assets/images/corpusChristiSkyline.jpg);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 100vh;
|
||||
}
|
||||
.combo_lp{
|
||||
width: 200px;
|
||||
}
|
||||
.p-button-white{
|
||||
color:aliceblue
|
||||
}
|
||||
45
bizmatch/src/app/pages/home/home.component.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { KeyValue, ListingCriteria } from '../../models/main.model';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import onChange from 'on-change';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../utils/utils';
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
standalone: true,
|
||||
imports: [CommonModule, StyleClassModule,ButtonModule, CheckboxModule,InputTextModule,DropdownModule,FormsModule, RouterModule],
|
||||
templateUrl: './home.component.html',
|
||||
styleUrl: './home.component.scss'
|
||||
})
|
||||
export class HomeComponent {
|
||||
activeTabAction = 'business';
|
||||
type:string;
|
||||
maxPrice:string;
|
||||
minPrice:string;
|
||||
criteria:ListingCriteria
|
||||
public constructor(private router: Router,private activatedRoute: ActivatedRoute, public selectOptions:SelectOptionsService, public userService:UserService) {
|
||||
this.criteria = onChange(getCriteriaStateObject(),getSessionStorageHandler);
|
||||
}
|
||||
ngOnInit(){
|
||||
|
||||
}
|
||||
|
||||
search(){
|
||||
this.router.navigate([`listings/${this.activeTabAction}`])
|
||||
}
|
||||
|
||||
account(){
|
||||
setTimeout(()=>{
|
||||
this.router.navigate([`account`])
|
||||
},10);
|
||||
}
|
||||
}
|
||||
130
bizmatch/src/app/pages/listings/listings.component.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<div id="sky-line" class="hidden-lg-down">
|
||||
</div>
|
||||
<div class="search">
|
||||
<div class="wrapper">
|
||||
<div class="grid p-4 align-items-center">
|
||||
@if (listingCategory==='business'){
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="selectOptions.typesOfBusiness" [(ngModel)]="criteria.type" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Categorie of Business"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="locations" [(ngModel)]="location" optionLabel="criteria.location" optionLabel="name" optionValue="value"
|
||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Min Price"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Max Price"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<!-- <p-toggleButton [(ngModel)]="checked1" onLabel="Sustainable" offLabel="Unsustainable" onIcon="pi pi-check" offIcon="pi pi-times" styleClass="mb-3 lg:mt-0 mr-4 flex-shrink-0 w-12rem"></p-toggleButton> -->
|
||||
<p-toggleButton [(ngModel)]="criteria.realEstateChecked" onLabel="Real Estate not included"
|
||||
offLabel="Real Estate included"></p-toggleButton>
|
||||
</div>
|
||||
}
|
||||
@if (listingCategory==='investment'){
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="locations" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
}
|
||||
@if (listingCategory==='professionals_brokers'){
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="selectOptions.categories" [(ngModel)]="criteria.category" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Category"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<p-dropdown [options]="locations" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
}
|
||||
<div [ngClass]="{'col-offset-9':type==='investment','col-offset-7':type==='professionals_brokers'}" class="col-1">
|
||||
<p-button label="Refine" (click)="search()"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="surface-200 h-full">
|
||||
<div class="wrapper">
|
||||
<div class="grid">
|
||||
@for (listing of filteredListings; track listing.id) {
|
||||
<div class="col-12 lg:col-3 p-3">
|
||||
<div class="shadow-2 border-round surface-card mb-3 h-full flex-column justify-content-between flex">
|
||||
<div class="p-4 h-full flex flex-column">
|
||||
@if (listing.listingsCategory==='business'){
|
||||
<div class="flex align-items-center">
|
||||
<span [class]="selectOptions.getBgColorType(listing.type)"
|
||||
class="inline-flex border-circle align-items-center justify-content-center mr-3"
|
||||
style="width:38px;height:38px">
|
||||
<i [class]="selectOptions.getIconAndTextColorType(listing.type)" class="pi text-xl"></i>
|
||||
</span>
|
||||
<span class="text-900 font-medium text-2xl">{{selectOptions.getBusiness(listing.type)}}</span>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='professionals_brokers'){
|
||||
<div class="flex align-items-center">
|
||||
<span [class]="selectOptions.getBgColor(listing.category)" class="inline-flex border-circle align-items-center justify-content-center mr-3"
|
||||
style="width:38px;height:38px">
|
||||
<i [class]="selectOptions.getIconAndTextColor(listing.category)" class="text-xl"></i>
|
||||
</span>
|
||||
<span class="text-900 font-medium text-2xl">{{selectOptions.getCategory(listing.category)}}</span>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='investment'){
|
||||
<div class="flex align-items-center">
|
||||
<span
|
||||
class="inline-flex border-circle align-items-center justify-content-center bg-green-100 mr-3"
|
||||
style="width:38px;height:38px">
|
||||
<i class="pi pi-globe text-xl text-green-600"></i>
|
||||
</span>
|
||||
<span class="text-900 font-medium text-2xl">Investment</span>
|
||||
</div>
|
||||
}
|
||||
<div class="text-900 my-3 text-xl font-medium">{{listing.title}}</div>
|
||||
@if (listing.listingsCategory==='business'){
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Asking price: {{listing.price | currency}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Sales revenue: {{listing.salesRevenue | currency}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Net profit: {{listing.cashFlow | currency}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{listing.established}}</p>
|
||||
}
|
||||
@if (listing.listingsCategory==='professionals_brokers'){
|
||||
<!-- <p class="mt-0 mb-1 text-700 line-height-3">Category: {{listing.category}}</p> -->
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
||||
}
|
||||
@if (listing.listingsCategory==='investment'){
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
||||
<p class="mt-0 mb-1 text-700 line-height-3">Phone Number: {{listing.phoneNumber}}</p>
|
||||
}
|
||||
<div class="mt-auto ml-auto">
|
||||
<img *ngIf="!listing.hideImage" src="{{environment.apiBaseUrl}}/profile_{{listing.userId}}" (error)="imageErrorHandler(listing)" class="rounded-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 surface-100 text-right">
|
||||
<button pButton pRipple icon="pi pi-arrow-right" iconPos="right" label="View Full Listing"
|
||||
class="p-button-rounded p-button-success" [routerLink]="['/details',listing.id]"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="mb-2 surface-200 flex align-items-center justify-content-center">
|
||||
<div class="mx-1 text-color">Total number of Listings: {{totalRecords}}</div>
|
||||
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]" ></p-paginator>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
22
bizmatch/src/app/pages/listings/listings.component.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
#sky-line {
|
||||
background-image: url(../../../assets/images/bw-sky.jpg);
|
||||
height: 204px;
|
||||
background-position: bottom;
|
||||
background-size: cover;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.search{
|
||||
background-color: #343F69;
|
||||
}
|
||||
::ng-deep p-paginator div {
|
||||
background-color: var(--surface-200) !important;
|
||||
// background-color: var(--surface-400) !important;
|
||||
}
|
||||
.rounded-image {
|
||||
border-radius: 6px;
|
||||
width: 100px;
|
||||
height: 25px;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
}
|
||||
96
bizmatch/src/app/pages/listings/listings.component.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, InvestmentsListing, KeyValue, ListingCriteria, ListingType, PageEvent, ProfessionalsBrokersListing, } from '../../models/main.model';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { ListingsService } from '../../services/listings.service';
|
||||
import { Observable, lastValueFrom } from 'rxjs';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import onChange from 'on-change';
|
||||
import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler } from '../../utils/utils';
|
||||
import { InitEditableRow } from 'primeng/table';
|
||||
import { environment } from '../../../environments/environment';
|
||||
@Component({
|
||||
selector: 'app-listings',
|
||||
standalone: true,
|
||||
imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, StyleClassModule, ToggleButtonModule, RouterModule, PaginatorModule],
|
||||
templateUrl: './listings.component.html',
|
||||
styleUrls: ['./listings.component.scss', '../pages.scss']
|
||||
})
|
||||
export class ListingsComponent {
|
||||
environment=environment;
|
||||
listings: Array<ListingType>;
|
||||
filteredListings: Array<ListingType>;
|
||||
criteria:ListingCriteria;
|
||||
realEstateChecked: boolean;
|
||||
category: string;
|
||||
maxPrice: string;
|
||||
minPrice: string;
|
||||
type:string;
|
||||
locations = [];
|
||||
locationsSet = new Set();
|
||||
location:string;
|
||||
first: number = 0;
|
||||
rows: number = 12;
|
||||
totalRecords:number = 0;
|
||||
public listingCategory: 'business' | 'professionals_brokers' | 'investment' | undefined;
|
||||
|
||||
constructor(public selectOptions: SelectOptionsService, private listingsService:ListingsService,private activatedRoute: ActivatedRoute, private router:Router, private cdRef:ChangeDetectorRef) {
|
||||
this.criteria = onChange(getCriteriaStateObject(),getSessionStorageHandler);
|
||||
this.router.getCurrentNavigation()
|
||||
this.activatedRoute.snapshot
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
if (this.activatedRoute.snapshot.fragment===''){
|
||||
this.criteria = onChange(createGenericObject<ListingCriteria>(),getSessionStorageHandler)
|
||||
this.first=0;
|
||||
}
|
||||
this.listingCategory = (<any>params).type;
|
||||
this.criteria.listingsCategory=this.listingCategory;
|
||||
this.init()
|
||||
})
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
}
|
||||
async init(){
|
||||
this.listings=await this.listingsService.getListings(this.criteria);
|
||||
this.setLocations();
|
||||
this.filteredListings=[...this.listings];
|
||||
this.totalRecords=this.listings.length
|
||||
this.filteredListings=[...this.listings].splice(this.first,this.rows);
|
||||
this.cdRef.markForCheck();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
setLocations(){
|
||||
this.locationsSet=new Set();
|
||||
this.listings.forEach(l=>{
|
||||
if (l.location){
|
||||
this.locationsSet.add(l.location)
|
||||
}
|
||||
})
|
||||
this.locations = [...this.locationsSet].map((ls) =>({name:this.selectOptions.getLocation(ls as string),value:ls}))
|
||||
}
|
||||
async search() {
|
||||
this.listings= await this.listingsService.getListings(this.criteria);
|
||||
this.setLocations();
|
||||
this.totalRecords=this.listings.length
|
||||
this.filteredListings =[...this.listings].splice(this.first,this.rows);
|
||||
this.cdRef.markForCheck();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
onPageChange(event: any) {
|
||||
this.first = event.first;
|
||||
this.rows = event.rows;
|
||||
this.filteredListings=[...this.listings].splice(this.first,this.rows);
|
||||
}
|
||||
imageErrorHandler(listing: ListingType) {
|
||||
listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<div class="surface-ground ">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<ul class="list-none m-0 p-0 flex flex-row lg:flex-column justify-content-evenly md:justify-content-between lg:justify-content-start mb-5 lg:pr-8 lg:mb-0">
|
||||
<li>
|
||||
<a routerLink="/account" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline" >
|
||||
<i class="pi pi-user md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">Account</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a routerLink="/createListing" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||
<i class="pi pi-plus-circle md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">Create Listing</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a routerLink="/myListings" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||
<i class="pi pi-list md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Listings</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a routerLink="/myFavorites" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||
<i class="pi pi-star md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Favorites</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a routerLink="/emailUs" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||
<fa-icon [icon]="faEnvelope" class="mr-2 flex"></fa-icon>
|
||||
<span class="font-medium hidden md:block">Email Us</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a (click)="userService.logout()" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||
<fa-icon [icon]="faRightFromBracket" class="mr-2 flex"></fa-icon>
|
||||
<span class="font-medium hidden md:block">Logout</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
ul {
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, KeyValue } from '../../models/main.model';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import data from '../../../assets/data/listings.json';
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterModule } from '@angular/router';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DividerModule } from 'primeng/divider';
|
||||
import { RippleModule } from 'primeng/ripple';
|
||||
import { faEnvelope } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faRightFromBracket } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { UserService } from '../../services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'menu-account',
|
||||
standalone: true,
|
||||
imports: [CommonModule, StyleClassModule, ButtonModule, DividerModule, RouterModule, RippleModule, FontAwesomeModule ],
|
||||
templateUrl: './menu-account.component.html',
|
||||
styleUrl: './menu-account.component.scss'
|
||||
})
|
||||
export class MenuAccountComponent {
|
||||
activeLink: string;
|
||||
faEnvelope=faEnvelope;
|
||||
faRightFromBracket=faRightFromBracket;
|
||||
constructor(private router: Router,public userService:UserService) {
|
||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.activeLink = event.url;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
6
bizmatch/src/app/pages/pages.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
.wrapper {
|
||||
width: 1491px;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
85
bizmatch/src/app/pages/pricing/pricing.component.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<div class="container">
|
||||
<div class="wrapper">
|
||||
<div class="py-3 px-6 flex flex-column align-items-center justify-content-between relative">
|
||||
<a routerLink="/home"><img src="../../../assets/images/header-logo.png" alt="Image" height="50" ></a>
|
||||
<div class="px-4 py-8 md:px-6 lg:px-8 bg-no-repeat bg-cover" >
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full lg:w-6 lg:pr-8">
|
||||
<div class="text-900 font-bold text-6xl text-blue-900 mb-4">Pricing</div>
|
||||
<div class="text-700 text-xl text-blue-600 line-height-3 mb-4 lg:mb-0">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Velitnumquam eligendi quos.</div>
|
||||
</div>
|
||||
<div class="w-full md:w-6 lg:w-3">
|
||||
<ul class="list-none p-0 m-0">
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Arcu vitae elementum</span>
|
||||
</li>
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Dui faucibus in ornare</span>
|
||||
</li>
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Morbi tincidunt augue</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="w-full md:w-6 lg:w-3 md:pl-5">
|
||||
<ul class="list-none p-0 m-0">
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Duis ultricies lacus sed</span>
|
||||
</li>
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Imperdiet proin</span>
|
||||
</li>
|
||||
<li class="flex align-items-center my-4 bg-white-alpha-40 shadow-2 py-1 px-2 border-round-lg w-max">
|
||||
<i class="pi pi-check text-green-500 mr-3"></i>
|
||||
<span>Nisi scelerisque</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap mt-5 -mx-3">
|
||||
<div class="w-full lg:w-4 p-3">
|
||||
<div class="shadow-2 p-3 h-full bg-primary" style="border-radius: 6px">
|
||||
<div class="font-medium text-xl mb-5">Free Forever</div>
|
||||
<div class="font-bold text-5xl mb-5">Free</div>
|
||||
<button (click)="register()" type="button" pRipple class="font-medium appearance-none border-none p-2 surface-0 text-primary hover:surface-100 p-component lg:w-full border-rounded cursor-pointer transition-colors transition-duration-150" style="border-radius: 6px">
|
||||
<span>Create Account</span>
|
||||
</button>
|
||||
<p class="text-sm line-height-3 mb-0 mt-5">Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-4 p-3">
|
||||
<div class="shadow-2 p-3 h-full surface-card" style="border-radius: 6px">
|
||||
<div class="font-medium text-xl mb-5 text-900 ">Monthly</div>
|
||||
<div class="flex align-items-center mb-5">
|
||||
<span class="text-900 font-bold text-5xl">$29</span>
|
||||
<span class="font-medium text-500 ml-2">per month</span>
|
||||
</div>
|
||||
<button (click)="register()" pButton pRipple label="Proceed Monthly" icon="pi pi-arrow-right" iconPos="right" class="lg:w-full font-medium p-2" style="border-radius: 6px"></button>
|
||||
<p class="text-sm line-height-3 mb-0 mt-5">Nec ultrices dui sapien eget. Amet nulla facilisi morbi tempus.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-4 p-3">
|
||||
<div class="shadow-2 p-3 h-full flex flex-column surface-card" style="border-radius: 6px">
|
||||
<div class="flex flex-row justify-content-between mb-5 align-items-center">
|
||||
<div class="text-900 text-xl font-medium">Yearly</div>
|
||||
<span class="bg-orange-100 500 text-orange-500 font-semibold px-2 py-1 border-round">🎉 Save 20%</span>
|
||||
</div>
|
||||
<div class="flex align-items-center mb-5">
|
||||
<span class="text-900 font-bold text-5xl">$275</span>
|
||||
<span class="font-medium text-500 ml-2">per year</span>
|
||||
</div>
|
||||
<button (click)="register()" pButton pRipple label="Proceed Yearly" icon="pi pi-arrow-right" iconPos="right" class="lg:w-full font-medium p-2" style="border-radius: 6px"></button>
|
||||
<p class="text-sm line-height-3 mb-0 mt-5">Placerat in egestas erat imperdiet sed euismod nisi porta.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
11
bizmatch/src/app/pages/pricing/pricing.component.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.container {
|
||||
background-image: url(../../../assets/images/index-bg.jpg), url(../../../assets/images/pricing-4.svg);
|
||||
//background-image: url(../../../assets/images/corpusChristiSkyline.jpg);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 100vh;
|
||||
}
|
||||
17
bizmatch/src/app/pages/pricing/pricing.component.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SharedModule } from '../../shared/shared/shared.module';
|
||||
import { UserService } from '../../services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pricing',
|
||||
standalone: true,
|
||||
imports: [SharedModule],
|
||||
templateUrl: './pricing.component.html',
|
||||
styleUrl: './pricing.component.scss'
|
||||
})
|
||||
export class PricingComponent {
|
||||
constructor(private userService:UserService){}
|
||||
register(){
|
||||
this.userService.register(`${window.location.origin}/account`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<p-toast></p-toast>
|
||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||
<div class="text-900 font-semibold text-lg mt-3">Account Details</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
<div class="mb-4">
|
||||
<label for="email" class="block font-medium text-900 mb-2">Username</label>
|
||||
<input id="email" type="text" [disabled]="true" pInputText [(ngModel)]="user.username">
|
||||
<p class="font-italic text-sm line-height-1">Usernames cannot be changed.</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="state" class="block font-medium text-900 mb-2">First Name</label>
|
||||
<input id="state" type="text" pInputText [(ngModel)]="user.firstname">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="state" class="block font-medium text-900 mb-2">Last Name</label>
|
||||
<input id="state" type="text" pInputText [(ngModel)]="user.lastname">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="state" class="block font-medium text-900 mb-2">E-mail (required)</label>
|
||||
<input id="state" type="text" pInputText [(ngModel)]="user.email">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="state" class="block font-medium text-900 mb-2">New Password</label>
|
||||
<p class="font-italic text-sm line-height-1">If you would like to change the password type a new one. Otherwise leave this blank.</p>
|
||||
<input id="state" type="text" pInputText>
|
||||
<p class="font-italic text-sm line-height-1">Password repetition</p>
|
||||
<input id="state" type="text" pInputText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button pButton pRipple label="Update Profile" class="w-auto" (click)="updateProfile(user)"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-column align-items-center flex-or">
|
||||
<span class="font-medium text-900 mb-2">Profile Picture</span>
|
||||
<img [src]="imageUrl" (error)="setImageToFallback($event)" class="rounded-image"/>
|
||||
<!-- <p-image src="http://localhost:3000/public/user.png" alt="Image" width="10rem" [preview]="true"></p-image> -->
|
||||
<!-- <button pButton type="button" icon="pi pi-pencil" class="p-button-rounded -mt-4"></button> -->
|
||||
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUpload($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-900 font-semibold text-lg mt-3">Membership Level</div>
|
||||
<p-divider></p-divider>
|
||||
<p-table [value]="userSubscriptions" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 5rem"></th>
|
||||
<th>ID</th>
|
||||
<th>Level</th>
|
||||
<th>Start Date</th>
|
||||
<th>Date Modified</th>
|
||||
<th>End Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-subscription let-expanded="expanded">
|
||||
<tr>
|
||||
<td>
|
||||
<button type="button" pButton pRipple [pRowToggler]="subscription" class="p-button-text p-button-rounded p-button-plain" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
|
||||
</td>
|
||||
<td>{{ subscription.id }}</td>
|
||||
<td>{{ subscription.level }}</td>
|
||||
<td>{{ subscription.start | date }}</td>
|
||||
<td>{{ subscription.modified | date }}</td>
|
||||
<td>{{ subscription.end | date }}</td>
|
||||
<td>{{ subscription.status }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="rowexpansion" let-subscription>
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="p-3">
|
||||
<p-table [value]="subscription.invoices" dataKey="id">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 5rem"></th>
|
||||
<th>ID</th>
|
||||
<th>Date</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-invoice>
|
||||
<tr>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-print" class="p-button-rounded p-button-success mr-2" (click)="printInvoice(invoice)"></button>
|
||||
</td>
|
||||
<td>{{ invoice.id }}</td>
|
||||
<td>{{ invoice.date | date}}</td>
|
||||
<td>{{ invoice.price | currency}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
.rounded-image {
|
||||
border-radius: 6px;
|
||||
width: 120px;
|
||||
height: 30px;
|
||||
border: 1px solid #6b7280;
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, Invoice, KeyValue, Subscription, User } from '../../../models/main.model';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
import { DividerModule } from 'primeng/divider';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { SubscriptionsService } from '../../../services/subscriptions.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { FileUploadModule } from 'primeng/fileupload';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
standalone: true,
|
||||
// imports: [CommonModule, StyleClassModule, MenuAccountComponent, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule ],
|
||||
imports: [SharedModule,FileUploadModule],
|
||||
providers:[MessageService],
|
||||
templateUrl: './account.component.html',
|
||||
styleUrl: './account.component.scss'
|
||||
})
|
||||
export class AccountComponent {
|
||||
user:User;
|
||||
subscriptions:Array<Subscription>;
|
||||
userSubscriptions:Array<Subscription>=[];
|
||||
uploadUrl:string;
|
||||
maxFileSize=1000000;
|
||||
imageUrl:string;
|
||||
constructor(public userService: UserService, private subscriptionService: SubscriptionsService,private messageService: MessageService) {
|
||||
this.user=this.userService.getUser()
|
||||
}
|
||||
async ngOnInit(){
|
||||
this.imageUrl = `${environment.apiBaseUrl}/profile_${this.user.id}`
|
||||
this.userSubscriptions=await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/account/uploadPhoto/${this.user.id}`;
|
||||
}
|
||||
printInvoice(invoice:Invoice){}
|
||||
updateProfile(user:User){
|
||||
this.messageService.add({ severity: 'warn', summary: 'Information', detail: 'This function is not yet available, please send an email to info@bizmatch.net for changes to your customer data', life: 15000 });
|
||||
}
|
||||
onUpload(event:any){
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.imageUrl = `${environment.apiBaseUrl}/profile_${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
||||
}
|
||||
setImageToFallback(event: Event) {
|
||||
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<p-toast></p-toast>
|
||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||
<div class="text-900 font-semibold text-lg mt-3">{{mode==='create'?'New':'Edit'}} Listing</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
<div class="mb-4">
|
||||
<label for="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
||||
<p-dropdown id="listingCategory" [options]="selectOptions?.listingCategories" [(ngModel)]="listing.listingsCategory" optionLabel="name"
|
||||
optionValue="value" placeholder="Listing category" [disabled]="mode==='edit'"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
||||
<input id="email" type="text" pInputText [(ngModel)]="listing.title">
|
||||
</div>
|
||||
@if (listing.listingsCategory==='business' || listing.listingsCategory==='professionals_brokers'){
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="summary" class="block font-medium text-900 mb-2">Summary (Brief description)</label>
|
||||
<textarea id="summary" type="text" pInputTextarea rows="5" [autoResize]="true" [ngModel]="listing.summary | arrayToString:'\n\n'" (ngModelChange)="updateSummary($event)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
||||
<textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@if (listing.listingsCategory==='business'){
|
||||
<div class="mb-4">
|
||||
<label for="listingCategory" class="block font-medium text-900 mb-2">Type of business</label>
|
||||
<p-dropdown id="listingCategory" [options]="selectOptions?.typesOfBusiness" [(ngModel)]="listing.type" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Type of business"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-4">
|
||||
<label for="listingCategory" class="block font-medium text-900 mb-2">Location</label>
|
||||
<p-dropdown id="listingCategory" [options]="selectOptions?.locations" [(ngModel)]="listing.location" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Location"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
@if (listing.listingsCategory==='professionals_brokers'){
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="address" class="block font-medium text-900 mb-2">Address</label>
|
||||
<input id="address" type="text" pInputText [(ngModel)]="listing.address">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='professionals_brokers' || listing.listingsCategory==='investment'){
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="email" class="block font-medium text-900 mb-2">Email</label>
|
||||
<input id="address" type="text" pInputText [(ngModel)]="listing.email">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="website" class="block font-medium text-900 mb-2">Website</label>
|
||||
<input id="address" type="text" pInputText [(ngModel)]="listing.website">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='professionals_brokers'){
|
||||
<div class="mb-4">
|
||||
<label for="category" class="block font-medium text-900 mb-2">Category</label>
|
||||
<p-dropdown id="category" [options]="selectOptions?.categories" [(ngModel)]="listing.category" optionLabel="name"
|
||||
optionValue="value" [showClear]="true" placeholder="Category"
|
||||
[style]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='investment'){
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<label for="phoneNumber" class="block font-medium text-900 mb-2">Phone Number</label>
|
||||
<input id="phoneNumber" type="text" pInputText [(ngModel)]="listing.phoneNumber">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
@if (listing.listingsCategory==='business'){
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||
<!-- <input id="price" type="text" pInputText [(ngModel)]="listing.price"> -->
|
||||
<p-inputNumber mode="currency" currency="USD" inputId="price" type="text" [(ngModel)]="listing.price"></p-inputNumber>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6 flex align-items-end justify-content-center">
|
||||
<p-checkbox [binary]="true" [(ngModel)]="listing.realEstateIncluded"></p-checkbox>
|
||||
<span class="ml-2 text-900">Real Estate Included</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
||||
<!-- <input id="salesRevenue" type="text" pInputText [(ngModel)]="listing.salesRevenue"> -->
|
||||
<p-inputNumber mode="currency" currency="USD" inputId="salesRevenue" type="text" [(ngModel)]="listing.salesRevenue"></p-inputNumber>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
||||
<!-- <input id="cashFlow" type="text" pInputText [(ngModel)]="listing.cashFlow"> -->
|
||||
<p-inputNumber mode="currency" currency="USD" inputId="cashFlow" type="text" [(ngModel)]="listing.cashFlow"></p-inputNumber>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="netProfit" class="block font-medium text-900 mb-2">Net Profit</label>
|
||||
<!-- <input id="netProfit" type="text" pInputText [(ngModel)]="listing.netProfit"> -->
|
||||
<p-inputNumber mode="currency" currency="USD" inputId="netProfit" type="text" [(ngModel)]="listing.netProfit"></p-inputNumber>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
||||
<!-- <input id="employees" type="text" pInputText [(ngModel)]="listing.employees"> -->
|
||||
<p-inputNumber mode="employees" mode="decimal" inputId="employees" type="text" [(ngModel)]="listing.employees"></p-inputNumber>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="inventory" class="block font-medium text-900 mb-2">Inventory</label>
|
||||
<textarea id="inventory" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.inventory"></textarea>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="reasonForSale" class="block font-medium text-900 mb-2">Reason for Sale</label>
|
||||
<textarea id="reasonForSale" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.reasonForSale"></textarea>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="brokerLicensing" class="block font-medium text-900 mb-2">Broker Licensing</label>
|
||||
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="internalListing" class="block font-medium text-900 mb-2">Internal Listing (Will not be shown on the listing, for your records only.)</label>
|
||||
<input id="internalListing" type="text" pInputText [(ngModel)]="listing.internals">
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
@if (mode==='create'){
|
||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="create()"></button>
|
||||
} @else {
|
||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="update(listing.id)"></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, InvestmentsListing, Invoice, KeyValue, ProfessionalsBrokersListing, User } from '../../../models/main.model';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import data from '../../../../assets/data/user.json';
|
||||
import dataListings from '../../../../assets/data/listings.json';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
import { DividerModule } from 'primeng/divider';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { createGenericObject } from '../../../utils/utils';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { InputNumberModule } from 'primeng/inputnumber';
|
||||
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { MessageService } from 'primeng/api';
|
||||
@Component({
|
||||
selector: 'create-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule,ArrayToStringPipe],
|
||||
providers:[MessageService],
|
||||
templateUrl: './edit-listing.component.html',
|
||||
styleUrl: './edit-listing.component.scss'
|
||||
})
|
||||
export class EditListingComponent {
|
||||
listingCategory:'Business'|'Professionals/Brokers Directory'|'Investment Property';
|
||||
category:string;
|
||||
location:string;
|
||||
mode:'edit'|'create';
|
||||
separator:'\n\n'
|
||||
listing:BusinessListing|ProfessionalsBrokersListing|InvestmentsListing = createGenericObject<BusinessListing>();
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user:User;
|
||||
constructor(public selectOptions:SelectOptionsService,private router: Router,private activatedRoute: ActivatedRoute,private listingsService:ListingsService,public userService: UserService,private messageService: MessageService){
|
||||
this.user=this.userService.getUser();
|
||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.mode = event.url==='/createListing'?'create':'edit';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
if (this.mode==='edit'){
|
||||
this.listing=await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||
} else {
|
||||
this.listing=createGenericObject<BusinessListing>();
|
||||
this.listing.userId=this.user.id
|
||||
this.listing.listingsCategory='business';
|
||||
}
|
||||
}
|
||||
updateSummary(value: string): void {
|
||||
const lines = value.split('\n');
|
||||
(<BusinessListing>this.listing).summary = lines.filter(l=>l.trim().length>0);
|
||||
}
|
||||
async update(id:string){
|
||||
await this.listingsService.update(this.listing,this.listing.id);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been updated', life: 3000 });
|
||||
}
|
||||
async create(){
|
||||
await this.listingsService.create(this.listing);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been created', life: 3000 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||
<div class="text-900 font-semibold text-lg mt-3">Contact Us</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="name" class="block font-medium text-900 mb-2">Your name</label>
|
||||
<input id="name" type="text" pInputText>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="email" class="block font-medium text-900 mb-2">Your Email</label>
|
||||
<input id="email" type="text" pInputText>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="phone" class="block font-medium text-900 mb-2">Your Phone</label>
|
||||
<input id="phone" type="text" pInputText>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="help" class="block font-medium text-900 mb-2">How can we help you ?</label>
|
||||
<textarea id="help" type="text" pInputTextarea rows="5" [autoResize]="true"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<button pButton pRipple label="Submit" class="w-auto"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { BusinessListing, Invoice, KeyValue, User } from '../../../models/main.model';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import data from '../../../../assets/data/user.json';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
import { DividerModule } from 'primeng/divider';
|
||||
import { TableModule } from 'primeng/table';
|
||||
@Component({
|
||||
selector: 'app-email-us',
|
||||
standalone: true,
|
||||
imports: [CommonModule, StyleClassModule, MenuAccountComponent, DividerModule,ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule],
|
||||
templateUrl: './email-us.component.html',
|
||||
styleUrl: './email-us.component.scss'
|
||||
})
|
||||
export class EmailUsComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||
<div class="text-900 font-semibold text-lg mt-3">My Favorites</div>
|
||||
<p-divider></p-divider>
|
||||
<p-table [value]="favorites" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id" [paginator]="true" [rows]="10" [rowsPerPageOptions]="[10, 20, 50]" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="wide-column">Title</th>
|
||||
<th>Category</th>
|
||||
<th>Located in</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-listing>
|
||||
<tr>
|
||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||
<td>{{ selectOptions.getLocation(listing.location) }}</td>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-eye" class="p-button-rounded p-button-success mr-2" [routerLink]="['/details',listing.id]"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.wide-column{
|
||||
width: 40%;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
import dataListings from '../../../../assets/data/listings.json';
|
||||
import { BusinessListing, User } from '../../../models/main.model';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-favorites',
|
||||
standalone: true,
|
||||
imports: [MenuAccountComponent, SharedModule],
|
||||
templateUrl: './favorites.component.html',
|
||||
styleUrl: './favorites.component.scss'
|
||||
})
|
||||
export class FavoritesComponent {
|
||||
user: User;
|
||||
listings: Array<BusinessListing> //= dataListings as unknown as Array<BusinessListing>;
|
||||
favorites: Array<BusinessListing>
|
||||
constructor(public userService: UserService, private listingsService:ListingsService, public selectOptions:SelectOptionsService){
|
||||
this.user=this.userService.getUser();
|
||||
}
|
||||
async ngOnInit(){
|
||||
this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
||||
this.favorites=this.listings.filter(l=>l.favoritesForUser?.includes(this.user.id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmPopup></p-confirmPopup>
|
||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||
<div class="text-900 font-semibold text-lg mt-3">My Listings</div>
|
||||
<p-divider></p-divider>
|
||||
<p-table [value]="myListings" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id" [paginator]="true" [rows]="10" [rowsPerPageOptions]="[10, 20, 50]" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="wide-column">Title</th>
|
||||
<th>Category</th>
|
||||
<th>Located in</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-listing>
|
||||
<tr>
|
||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||
<td>{{ selectOptions.getLocation(listing.location) }}</td>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editListing',listing.id]"></button>
|
||||
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event,listing)"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.wide-column{
|
||||
width: 40%;
|
||||
}
|
||||