From 6b61c19bd7169e95cfefce6175d01d40d30b9420 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Mon, 6 May 2024 20:13:09 +0200 Subject: [PATCH] Bug Fixing overall --- bizmatch-server/.editorconfig | 16 + bizmatch-server/.eslintrc.json | 45 ++ bizmatch-server/.prettierrc | 16 +- bizmatch-server/.vscode/settings.json | 28 ++ bizmatch-server/data/broker.json | 135 +++-- bizmatch-server/nest-cli.json | 9 +- bizmatch-server/package.json | 1 + bizmatch-server/src/drizzle/import.ts | 164 ++++-- .../0001_rapid_daimon_hellstrom.sql | 1 + .../migrations/0002_black_zaladane.sql | 7 + .../migrations/meta/0001_snapshot.json | 460 +++++++++++++++++ .../migrations/meta/0002_snapshot.json | 474 ++++++++++++++++++ .../src/drizzle/migrations/meta/_journal.json | 14 + bizmatch-server/src/drizzle/schema.ts | 7 +- bizmatch-server/src/image/image.controller.ts | 126 +++-- .../listings/business-listings.controller.ts | 37 +- .../src/listings/listings.service.ts | 90 ++-- bizmatch-server/src/mail/mail.controller.ts | 18 +- bizmatch-server/src/mail/mail.module.ts | 23 +- bizmatch-server/src/mail/mail.service.ts | 55 +- bizmatch-server/src/models/db.model.ts | 2 +- bizmatch-server/src/models/main.model.ts | 3 + .../select-options/select-options.service.ts | 210 ++++---- bizmatch-server/src/user/user.service.ts | 122 +++-- bizmatch/proxy.conf.json | 10 + .../details-business-listing.component.html | 12 +- .../details-business-listing.component.ts | 9 +- ...commercial-property-listing.component.html | 2 +- ...s-commercial-property-listing.component.ts | 7 +- .../details-user/details-user.component.html | 2 + .../details-user/details-user.component.ts | 11 +- .../src/app/pages/home/home.component.html | 22 +- .../broker-listings.component.html | 18 +- .../broker-listings.component.ts | 21 +- .../business-listings.component.html | 20 +- .../business-listings.component.scss | 37 +- .../business-listings.component.ts | 31 +- ...ommercial-property-listings.component.html | 32 +- .../commercial-property-listings.component.ts | 22 +- .../account/account.component.html | 3 +- .../subscription/account/account.component.ts | 22 +- .../edit-business-listing.component.html | 11 +- .../edit-business-listing.component.ts | 6 +- ...commercial-property-listing.component.html | 4 +- ...t-commercial-property-listing.component.ts | 7 +- .../edit-listing/edit-listing.component.html | 216 -------- .../edit-listing/edit-listing.component.scss | 57 --- .../edit-listing/edit-listing.component.ts | 203 -------- .../favorites/favorites.component.ts | 28 +- .../my-listing/my-listing.component.ts | 11 +- bizmatch/src/app/services/user.service.ts | 2 +- .../src/app/shared/shared/shared.module.ts | 85 +++- 52 files changed, 1926 insertions(+), 1048 deletions(-) create mode 100644 bizmatch-server/.editorconfig create mode 100644 bizmatch-server/.eslintrc.json create mode 100644 bizmatch-server/.vscode/settings.json create mode 100644 bizmatch-server/src/drizzle/migrations/0001_rapid_daimon_hellstrom.sql create mode 100644 bizmatch-server/src/drizzle/migrations/0002_black_zaladane.sql create mode 100644 bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json create mode 100644 bizmatch-server/src/drizzle/migrations/meta/0002_snapshot.json delete mode 100644 bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html delete mode 100644 bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.scss delete mode 100644 bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts diff --git a/bizmatch-server/.editorconfig b/bizmatch-server/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/bizmatch-server/.editorconfig @@ -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 diff --git a/bizmatch-server/.eslintrc.json b/bizmatch-server/.eslintrc.json new file mode 100644 index 0000000..eb7cda9 --- /dev/null +++ b/bizmatch-server/.eslintrc.json @@ -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 }] + } + } \ No newline at end of file diff --git a/bizmatch-server/.prettierrc b/bizmatch-server/.prettierrc index dcb7279..9c49078 100644 --- a/bizmatch-server/.prettierrc +++ b/bizmatch-server/.prettierrc @@ -1,4 +1,18 @@ { + "arrowParens": "avoid", + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 220, + "proseWrap": "always", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": true, "singleQuote": true, - "trailingComma": "all" + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "vueIndentScriptAndStyle": false } \ No newline at end of file diff --git a/bizmatch-server/.vscode/settings.json b/bizmatch-server/.vscode/settings.json new file mode 100644 index 0000000..0fd6e04 --- /dev/null +++ b/bizmatch-server/.vscode/settings.json @@ -0,0 +1,28 @@ +{ + "editor.suggestSelection": "first", + "vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue", + "explorer.confirmDelete": false, + "typescript.updateImportsOnFileMove.enabled": "always", + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "prettier.printWidth": 240, + "git.autofetch": false, + "git.autorefresh": true +} diff --git a/bizmatch-server/data/broker.json b/bizmatch-server/data/broker.json index 71c2bd9..ec3cace 100644 --- a/bizmatch-server/data/broker.json +++ b/bizmatch-server/data/broker.json @@ -27,7 +27,8 @@ "Bexar County, TX", "Travis County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender": "male" }, { "id": "1s2t3u4v-5w6x-7y8z-9a0b-1c2d3e4f5g6h", @@ -55,7 +56,8 @@ "Bastrop County, TX", "Caldwell County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "7i8j9k0l-1m2n-3o4p-5q6r-7s8t9u0v1w2x", @@ -83,7 +85,8 @@ "Kendall County, TX", "Medina County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "3y4z5a6b-7c8d-9e0f-1g2h-3i4j5k6l7m8n", @@ -111,7 +114,8 @@ "Brazoria County, TX", "Galveston County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "9o0p1q2r-3s4t-5u6v-7w8x-9y0z1a2b3c4d", @@ -139,7 +143,8 @@ "Denton County, TX", "Johnson County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "5e6f7g8h-9i0j-1k2l-3m4n-5o6p7q8r9s0t", @@ -167,7 +172,8 @@ "Cherokee County, TX", "Rusk County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "1u2v3w4x-5y6z-7a8b-9c0d-1e2f3g4h5i6j", @@ -195,7 +201,8 @@ "Terry County, TX", "Lynn County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "7k8l9m0n-1o2p-3q4r-5s6t-7u8v9w0x1y2z", @@ -223,7 +230,8 @@ "Willacy County, TX", "Zapata County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "3a4b5c6d-7e8f-9g0h-1i2j-3k4l5m6n7o8p", @@ -251,7 +259,8 @@ "Jeff Davis County, TX", "Presidio County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "9q0r1s2t-3u4v-5w6x-7y8z-9a0b1c2d3e4f", @@ -279,7 +288,8 @@ "Bryan County, OK", "Marshall County, OK" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "8a2b1c5d-7e6f-4g3h-9i1j-2k3l4m5n6o7p", @@ -311,7 +321,8 @@ "Clark County, NV", "Washoe County, NV" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "4q5r6s7t-8u9v-0w1x-2y3z-4a5b6c7d8e9f", @@ -339,7 +350,8 @@ "Brevard County, FL", "Volusia County, FL" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "1g2h3i4j-5k6l-7m8n-9o0p-1q2r3s4t5u6v", @@ -373,7 +385,8 @@ "Bergen County, NJ", "Essex County, NJ" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "7w8x9y0z-1a2b-3c4d-5e6f-7g8h9i0j1k2l", @@ -401,7 +414,8 @@ "Bexar County, TX", "Travis County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "3m4n5o6p-7q8r-9s0t-1u2v-3w4x5y6z7a8b", @@ -433,7 +447,8 @@ "Milwaukee County, WI", "Dane County, WI" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "5g6h7i8j-9k0l-1m2n-3o4p-5q6r7s8t9u0v", @@ -465,7 +480,8 @@ "Harris County, TX", "Dallas County, TX" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "1w2x3y4z-5a6b-7c8d-9e0f-1g2h3i4j5k6l", @@ -493,7 +509,8 @@ "Doña Ana County, NM", "San Juan County, NM" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "7m8n9o0p-1q2r-3s4t-5u6v-7w8x9y0z1a2b", @@ -521,7 +538,8 @@ "Logan County, OK", "Pottawatomie County, OK" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "3c4d5e6f-7g8h-9i0j-1k2l-3m4n5o6p7q8r", @@ -549,7 +567,8 @@ "Garland County, AR", "Washington County, AR" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "9s0t1u2v-3w4x-5y6z-7a8b-9c0d1e2f3g4h", @@ -577,7 +596,8 @@ "DeSoto County, MS", "Harrison County, MS" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "5i6j7k8l-9m0n-1o2p-3q4r-5s6t7u8v9w0x", @@ -605,7 +625,8 @@ "Yavapai County, AZ", "Mohave County, AZ" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "1y2z3a4b-5c6d-7e8f-9g0h-1i2j3k4l5m6n", @@ -633,7 +654,8 @@ "Madison County, AL", "Mobile County, AL" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "7o8p9q0r-1s2t-3u4v-5w6x-7y8z9a0b1c2d", @@ -661,7 +683,8 @@ "DeKalb County, GA", "Chatham County, GA" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "3e4f5g6h-7i8j-9k0l-1m2n-3o4p5q6r7s8t", @@ -693,7 +716,8 @@ "St. Tammany Parish, LA", "Jefferson Parish, LA" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"male" }, { "id": "9u0v1w2x-3y4z-5a6b-7c8d-9e0f1g2h3i4j", @@ -721,7 +745,8 @@ "Creek County, OK", "Osage County, OK" ], - "offeredServices": "

Services Offered

" + "offeredServices": "

Services Offered

", + "gender":"female" }, { "id": "5k6l7m8n-9o0p-1q2r-3s4t-5u6v7w8x9y0z", @@ -749,7 +774,8 @@ "Brazoria County, TX", "Galveston County, TX" ], - "offeredServices": "" + "offeredServices": "", + "gender":"male" }, { "id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p", @@ -777,7 +803,8 @@ "Sierra County, NM", "Grant County, NM" ], - "offeredServices": "

What We Offer

" + "offeredServices": "

What We Offer

", + "gender":"female" }, { "id": "7q8r9s0t-1u2v-3w4x-5y6z-7a8b9c0d1e2f", @@ -805,7 +832,8 @@ "Carroll County, AR", "Boone County, AR" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "3g4h5i6j-7k8l-9m0n-1o2p-3q4r5s6t7u8v", @@ -833,7 +861,8 @@ "Union County, MS", "Tate County, MS" ], - "offeredServices": "

Our Services Include

" + "offeredServices": "

Our Services Include

", + "gender":"male" }, { "id": "9w0x1y2z-3a4b-5c6d-7e8f-9g0h1i2j3k4l", @@ -861,7 +890,8 @@ "Vermilion Parish, LA", "St. Landry Parish, LA" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "5m6n7o8p-9q0r-1s2t-3u4v-5w6x7y8z9a0b", @@ -889,7 +919,8 @@ "Apache County, AZ", "Mohave County, AZ" ], - "offeredServices": "

Services We Provide

" + "offeredServices": "

Services We Provide

", + "gender":"male" }, { "id": "1c2d3e4f-5g6h-7i8j-9k0l-1m2n3o4p5q6r", @@ -917,7 +948,8 @@ "Monroe County, AL", "Clarke County, AL" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "7s8t9u0v-1w2x-3y4z-5a6b-7c8d9e0f1g2h", @@ -945,7 +977,8 @@ "McIntosh County, GA", "Bryan County, GA" ], - "offeredServices": "

What We Offer

" + "offeredServices": "

What We Offer

", + "gender":"male" }, { "id": "3i4j5k6l-7m8n-9o0p-1q2r-3s4t5u6v7w8x", @@ -973,7 +1006,8 @@ "McClain County, OK", "Pottawatomie County, OK" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "9y0z1a2b-3c4d-5e6f-7g8h-9i0j1k2l3m4n", @@ -1001,7 +1035,8 @@ "Los Alamos County, NM", "Sandoval County, NM" ], - "offeredServices": "

Services We Provide

" + "offeredServices": "

Services We Provide

", + "gender":"male" }, { "id": "5s6t7u8v-9w0x-1y2z-3a4b-5c6d7e8f9g0h", @@ -1033,7 +1068,8 @@ "Clark County, NV", "Washoe County, NV" ], - "offeredServices": "

Our Services

" + "offeredServices": "

Our Services

", + "gender":"male" }, { "id": "1i2j3k4l-5m6n-7o8p-9q0r-1s2t3u4v5w6x", @@ -1065,7 +1101,8 @@ "Orleans Parish, LA", "Jefferson Parish, LA" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "7y8z9a0b-1c2d-3e4f-5g6h-7i8j9k0l1m2n", @@ -1097,7 +1134,8 @@ "Fulton County, GA", "Gwinnett County, GA" ], - "offeredServices": "

What We Offer

" + "offeredServices": "

What We Offer

", + "gender":"male" }, { "id": "3o4p5q6r-7s8t-9u0v-1w2x-3y4z5a6b7c8d", @@ -1129,7 +1167,8 @@ "Hudson County, NJ", "Bergen County, NJ" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "9e0f1g2h-3i4j-5k6l-7m8n-9o0p1q2r3s4t", @@ -1161,7 +1200,8 @@ "Milwaukee County, WI", "Dane County, WI" ], - "offeredServices": "

Services We Provide

" + "offeredServices": "

Services We Provide

", + "gender":"female" }, { "id": "5u6v7w8x-9y0z-1a2b-3c4d-5e6f7g8h9i0j", @@ -1193,7 +1233,8 @@ "Multnomah County, OR", "Washington County, OR" ], - "offeredServices": "

Our Services

" + "offeredServices": "

Our Services

", + "gender":"male" }, { "id": "1k2l3m4n-5o6p-7q8r-9s0t-1u2v3w4x5y6z", @@ -1225,7 +1266,8 @@ "Doña Ana County, NM", "Santa Fe County, NM" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "7a8b9c0d-1e2f-3g4h-5i6j-7k8l9m0n1o2p", @@ -1257,7 +1299,8 @@ "Salt Lake County, UT", "Utah County, UT" ], - "offeredServices": "

What We Offer

" + "offeredServices": "

What We Offer

", + "gender":"male" }, { "id": "3q4r5s6t-7u8v-9w0x-1y2z-3a4b5c6d7e8f", @@ -1289,7 +1332,8 @@ "Cuyahoga County, OH", "Franklin County, OH" ], - "offeredServices": "" + "offeredServices": "", + "gender":"female" }, { "id": "9g0h1i2j-3k4l-5m6n-7o8p-9q0r1s2t3u4v", @@ -1321,6 +1365,7 @@ "Greenville County, SC", "Charleston County, SC" ], - "offeredServices": "

Services We Provide

" + "offeredServices": "

Services We Provide

", + "gender":"female" } ] \ No newline at end of file diff --git a/bizmatch-server/nest-cli.json b/bizmatch-server/nest-cli.json index 1fe4736..ea67053 100644 --- a/bizmatch-server/nest-cli.json +++ b/bizmatch-server/nest-cli.json @@ -4,7 +4,10 @@ "sourceRoot": "src", "compilerOptions": { "deleteOutDir": true, - "assets": ["assets/**/*","**/*.hbs"], - "watchAssets": true + "assets": [ + "assets/**/*", + "**/*.hbs" + ], + "watchAssets": true } -} +} \ No newline at end of file diff --git a/bizmatch-server/package.json b/bizmatch-server/package.json index 7dcf006..3a96b6a 100644 --- a/bizmatch-server/package.json +++ b/bizmatch-server/package.json @@ -83,6 +83,7 @@ "kysely-codegen": "^0.15.0", "pg-to-ts": "^4.1.1", "prettier": "^3.0.0", + "rimraf": "^5.0.5", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", diff --git a/bizmatch-server/src/drizzle/import.ts b/bizmatch-server/src/drizzle/import.ts index f2ae6fa..1f3a744 100644 --- a/bizmatch-server/src/drizzle/import.ts +++ b/bizmatch-server/src/drizzle/import.ts @@ -1,15 +1,17 @@ import 'dotenv/config'; import { drizzle } from 'drizzle-orm/node-postgres'; +import { existsSync, readFileSync, readdirSync, statSync, unlinkSync } from 'fs'; +import { join } from 'path'; import pkg from 'pg'; -const { Pool } = pkg; -import * as schema from './schema.js'; -import { readFileSync } from 'fs'; +import { rimraf } from 'rimraf'; +import sharp from 'sharp'; import { BusinessListing, CommercialPropertyListing, User } from 'src/models/db.model.js'; +import * as schema from './schema.js'; +const { Pool } = pkg; - -const connectionString = process.env.DATABASE_URL +const connectionString = process.env.DATABASE_URL; // const pool = new Pool({connectionString}) -const client = new Pool({ connectionString }) +const client = new Pool({ connectionString }); const db = drizzle(client, { schema, logger: true }); //Delete Content @@ -18,60 +20,134 @@ await db.delete(schema.businesses); await db.delete(schema.users); //Broker -let filePath = `./data/broker.json` +let filePath = `./data/broker.json`; let data: string = readFileSync(filePath, 'utf8'); -const userData: User[] = JSON.parse(data); // Erwartet ein Array von Objekten -const generatedUserData = [] -console.log(userData.length) +const userData: User[] = JSON.parse(data); // Erwartet ein Array von Objekten +const generatedUserData = []; +console.log(userData.length); +let i = 0, + male = 0, + female = 0; +const targetPathProfile = `./pictures/profile`; +deleteFilesOfDir(targetPathProfile); +const targetPathLogo = `./pictures/logo`; +deleteFilesOfDir(targetPathLogo); for (const user of userData) { - delete user.id - user.licensedIn=user.licensedIn.map(l=>`${l['name']}|${l['value']}`) - const u = await db.insert(schema.users).values(user).returning({ insertedId: schema.users.id }); - generatedUserData.push(u[0].insertedId); + delete user.id; + user.licensedIn = user.licensedIn.map(l => `${l['name']}|${l['value']}`); + user.hasCompanyLogo = true; + user.hasProfile = true; + const u = await db.insert(schema.users).values(user).returning({ insertedId: schema.users.id, gender: schema.users.gender }); + generatedUserData.push(u[0].insertedId); + i++; + if (u[0].gender === 'male') { + male++; + const data = readFileSync(`./pictures/profile_base/Mann_${male}.jpg`); + await storeProfilePicture(data, u[0].insertedId); + } else { + female++; + const data = readFileSync(`./pictures/profile_base/Frau_${male}.jpg`); + await storeProfilePicture(data, u[0].insertedId); + } + const data = readFileSync(`./pictures/logos_base/${i}.jpg`); + await storeCompanyLogo(data, u[0].insertedId); } //Business Listings -filePath = `./data/businesses.json` +filePath = `./data/businesses.json`; data = readFileSync(filePath, 'utf8'); -const businessJsonData = JSON.parse(data) as BusinessListing[]; // Erwartet ein Array von Objekten +const businessJsonData = JSON.parse(data) as BusinessListing[]; // Erwartet ein Array von Objekten for (const business of businessJsonData) { - delete business.id - business.created = new Date(business.created) - business.userId = getRandomItem(generatedUserData); - await db.insert(schema.businesses).values(business); + delete business.id; + business.created = new Date(business.created); + business.userId = getRandomItem(generatedUserData); + await db.insert(schema.businesses).values(business); } //Corporate Listings -filePath = `./data/commercials.json` +filePath = `./data/commercials.json`; data = readFileSync(filePath, 'utf8'); -const commercialJsonData = JSON.parse(data) as CommercialPropertyListing[]; // Erwartet ein Array von Objekten +const commercialJsonData = JSON.parse(data) as CommercialPropertyListing[]; // Erwartet ein Array von Objekten for (const commercial of commercialJsonData) { - const id = commercial.id; - delete commercial.id - commercial.imageOrder=['1.jpg']; - commercial.imagePath=id - commercial.created = getRandomDateWithinLastYear(); - commercial.userId = getRandomItem(generatedUserData); - await db.insert(schema.commercials).values(commercial); + const id = commercial.id; + delete commercial.id; + + commercial.imageOrder = getFilenames(id); + commercial.imagePath = id; + commercial.created = getRandomDateWithinLastYear(); + commercial.userId = getRandomItem(generatedUserData); + await db.insert(schema.commercials).values(commercial); } //End -await client.end() +await client.end(); function getRandomItem(arr: T[]): T { - if (arr.length === 0) { - throw new Error('The array is empty.'); - } + if (arr.length === 0) { + throw new Error('The array is empty.'); + } - const randomIndex = Math.floor(Math.random() * arr.length); - return arr[randomIndex]; + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +} +function getFilenames(id: string): string[] { + try { + let filePath = `./pictures/property/${id}`; + return readdirSync(filePath); + } catch (e) { + return null; + } } function getRandomDateWithinLastYear(): Date { - const currentDate = new Date(); - const lastYear = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), currentDate.getDate()); - - const timeDiff = currentDate.getTime() - lastYear.getTime(); - const randomTimeDiff = Math.random() * timeDiff; - const randomDate = new Date(lastYear.getTime() + randomTimeDiff); - - return randomDate; - } \ No newline at end of file + const currentDate = new Date(); + const lastYear = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), currentDate.getDate()); + + const timeDiff = currentDate.getTime() - lastYear.getTime(); + const randomTimeDiff = Math.random() * timeDiff; + const randomDate = new Date(lastYear.getTime() + randomTimeDiff); + + return randomDate; +} +async function storeProfilePicture(buffer: Buffer, userId: string) { + let quality = 50; + const output = await sharp(buffer) + .resize({ width: 300 }) + .avif({ quality }) // Verwende AVIF + //.webp({ quality }) // Verwende Webp + .toBuffer(); + await sharp(output).toFile(`./pictures/profile/${userId}.avif`); +} + +async function storeCompanyLogo(buffer: Buffer, userId: string) { + let quality = 50; + const output = await sharp(buffer) + .resize({ width: 300 }) + .avif({ quality }) // Verwende AVIF + //.webp({ quality }) // Verwende Webp + .toBuffer(); + await sharp(output).toFile(`./pictures/logo/${userId}.avif`); // Ersetze Dateierweiterung + // await fs.outputFile(`./pictures/logo/${userId}`, file.buffer); +} + +function deleteFilesOfDir(directoryPath) { + // Überprüfen, ob das Verzeichnis existiert + if (existsSync(directoryPath)) { + // Den Inhalt des Verzeichnisses synchron löschen + try { + readdirSync(directoryPath).forEach(file => { + const filePath = join(directoryPath, file); + // Wenn es sich um ein Verzeichnis handelt, rekursiv löschen + if (statSync(filePath).isDirectory()) { + rimraf.sync(filePath); + } else { + // Wenn es sich um eine Datei handelt, direkt löschen + unlinkSync(filePath); + } + }); + console.log('Der Inhalt des Verzeichnisses wurde erfolgreich gelöscht.'); + } catch (err) { + console.error('Fehler beim Löschen des Verzeichnisses:', err); + } + } else { + console.log('Das Verzeichnis existiert nicht.'); + } +} diff --git a/bizmatch-server/src/drizzle/migrations/0001_rapid_daimon_hellstrom.sql b/bizmatch-server/src/drizzle/migrations/0001_rapid_daimon_hellstrom.sql new file mode 100644 index 0000000..6ccab12 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/0001_rapid_daimon_hellstrom.sql @@ -0,0 +1 @@ +ALTER TABLE "commercials" ALTER COLUMN "imageOrder" SET DATA TYPE varchar(200)[]; \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/0002_black_zaladane.sql b/bizmatch-server/src/drizzle/migrations/0002_black_zaladane.sql new file mode 100644 index 0000000..eda9b80 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/0002_black_zaladane.sql @@ -0,0 +1,7 @@ +DO $$ BEGIN + CREATE TYPE "gender" AS ENUM('male', 'female'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "gender" "gender"; \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..61a17bb --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json @@ -0,0 +1,460 @@ +{ + "id": "3e4b8c5f-4474-4877-abec-38283408ee34", + "prevId": "f6d421f9-2394-4a1c-9268-9e46285f0a41", + "version": "5", + "dialect": "pg", + "tables": { + "businesses": { + "name": "businesses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "listingsCategory": { + "name": "listingsCategory", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "realEstateIncluded": { + "name": "realEstateIncluded", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "leasedLocation": { + "name": "leasedLocation", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "franchiseResale": { + "name": "franchiseResale", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "salesRevenue": { + "name": "salesRevenue", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "cashFlow": { + "name": "cashFlow", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "supportAndTraining": { + "name": "supportAndTraining", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "employees": { + "name": "employees", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "established": { + "name": "established", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "internalListingNumber": { + "name": "internalListingNumber", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reasonForSale": { + "name": "reasonForSale", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "brokerLicencing": { + "name": "brokerLicencing", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "internals": { + "name": "internals", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "businesses_userId_users_id_fk": { + "name": "businesses_userId_users_id_fk", + "tableFrom": "businesses", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "commercials": { + "name": "commercials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "hideImage": { + "name": "hideImage", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "zipCode": { + "name": "zipCode", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "county": { + "name": "county", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "phoneNumber": { + "name": "phoneNumber", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "imageOrder": { + "name": "imageOrder", + "type": "varchar(200)[]", + "primaryKey": false, + "notNull": false + }, + "imagePath": { + "name": "imagePath", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "commercials_userId_users_id_fk": { + "name": "commercials_userId_users_id_fk", + "tableFrom": "commercials", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "firstname": { + "name": "firstname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "lastname": { + "name": "lastname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "phoneNumber": { + "name": "phoneNumber", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyName": { + "name": "companyName", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyOverview": { + "name": "companyOverview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyWebsite": { + "name": "companyWebsite", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyLocation": { + "name": "companyLocation", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "offeredServices": { + "name": "offeredServices", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "areasServed": { + "name": "areasServed", + "type": "varchar(100)[]", + "primaryKey": false, + "notNull": false + }, + "hasProfile": { + "name": "hasProfile", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "hasCompanyLogo": { + "name": "hasCompanyLogo", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "licensedIn": { + "name": "licensedIn", + "type": "varchar(50)[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/0002_snapshot.json b/bizmatch-server/src/drizzle/migrations/meta/0002_snapshot.json new file mode 100644 index 0000000..8f4dff0 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/meta/0002_snapshot.json @@ -0,0 +1,474 @@ +{ + "id": "ad48c6eb-2d04-442f-9242-b6765553c7c4", + "prevId": "3e4b8c5f-4474-4877-abec-38283408ee34", + "version": "5", + "dialect": "pg", + "tables": { + "businesses": { + "name": "businesses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "listingsCategory": { + "name": "listingsCategory", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "realEstateIncluded": { + "name": "realEstateIncluded", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "leasedLocation": { + "name": "leasedLocation", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "franchiseResale": { + "name": "franchiseResale", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "salesRevenue": { + "name": "salesRevenue", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "cashFlow": { + "name": "cashFlow", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "supportAndTraining": { + "name": "supportAndTraining", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "employees": { + "name": "employees", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "established": { + "name": "established", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "internalListingNumber": { + "name": "internalListingNumber", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reasonForSale": { + "name": "reasonForSale", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "brokerLicencing": { + "name": "brokerLicencing", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "internals": { + "name": "internals", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "businesses_userId_users_id_fk": { + "name": "businesses_userId_users_id_fk", + "tableFrom": "businesses", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "commercials": { + "name": "commercials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "hideImage": { + "name": "hideImage", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "zipCode": { + "name": "zipCode", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "county": { + "name": "county", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "phoneNumber": { + "name": "phoneNumber", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "imageOrder": { + "name": "imageOrder", + "type": "varchar(200)[]", + "primaryKey": false, + "notNull": false + }, + "imagePath": { + "name": "imagePath", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "commercials_userId_users_id_fk": { + "name": "commercials_userId_users_id_fk", + "tableFrom": "commercials", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "firstname": { + "name": "firstname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "lastname": { + "name": "lastname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "phoneNumber": { + "name": "phoneNumber", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyName": { + "name": "companyName", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyOverview": { + "name": "companyOverview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyWebsite": { + "name": "companyWebsite", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyLocation": { + "name": "companyLocation", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "offeredServices": { + "name": "offeredServices", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "areasServed": { + "name": "areasServed", + "type": "varchar(100)[]", + "primaryKey": false, + "notNull": false + }, + "hasProfile": { + "name": "hasProfile", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "hasCompanyLogo": { + "name": "hasCompanyLogo", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "licensedIn": { + "name": "licensedIn", + "type": "varchar(50)[]", + "primaryKey": false, + "notNull": false + }, + "gender": { + "name": "gender", + "type": "gender", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "gender": { + "name": "gender", + "values": { + "male": "male", + "female": "female" + } + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/_journal.json b/bizmatch-server/src/drizzle/migrations/meta/_journal.json index bddfc24..633985c 100644 --- a/bizmatch-server/src/drizzle/migrations/meta/_journal.json +++ b/bizmatch-server/src/drizzle/migrations/meta/_journal.json @@ -8,6 +8,20 @@ "when": 1714913766996, "tag": "0000_third_spacker_dave", "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1714981666488, + "tag": "0001_rapid_daimon_hellstrom", + "breakpoints": true + }, + { + "idx": 2, + "version": "5", + "when": 1714982539265, + "tag": "0002_black_zaladane", + "breakpoints": true } ] } \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/schema.ts b/bizmatch-server/src/drizzle/schema.ts index 1b496cf..9397274 100644 --- a/bizmatch-server/src/drizzle/schema.ts +++ b/bizmatch-server/src/drizzle/schema.ts @@ -1,8 +1,8 @@ -import { integer, serial, text, pgTable, timestamp, jsonb, varchar, char, numeric, boolean, uuid, real, doublePrecision } from 'drizzle-orm/pg-core'; +import { integer, serial, text, pgTable, timestamp, jsonb, varchar, char, numeric, boolean, uuid, real, doublePrecision, pgEnum } from 'drizzle-orm/pg-core'; import { InferInsertModel, InferModel, InferModelFromColumns, InferSelectModel, relations, sql } from 'drizzle-orm'; export const PG_CONNECTION = 'PG_CONNECTION'; - +export const genderEnum = pgEnum('gender', ['male','female']); export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), firstname: varchar('firstname', { length: 255 }).notNull(), @@ -19,6 +19,7 @@ export const users = pgTable('users', { hasProfile: boolean('hasProfile'), hasCompanyLogo: boolean('hasCompanyLogo'), licensedIn:varchar('licensedIn', { length: 50 }).array(), + gender: genderEnum('gender'), }); export const businesses = pgTable('businesses', { @@ -68,7 +69,7 @@ export const commercials = pgTable('commercials', { email: varchar('email', { length: 255 }), website: varchar('website', { length: 255 }), phoneNumber: varchar('phoneNumber', { length: 255 }), - imageOrder:varchar('imageOrder',{length:30}).array(), + imageOrder:varchar('imageOrder',{length:200}).array(), imagePath:varchar('imagePath',{length:50}), created: timestamp('created'), updated: timestamp('updated'), diff --git a/bizmatch-server/src/image/image.controller.ts b/bizmatch-server/src/image/image.controller.ts index a27c544..b31c547 100644 --- a/bizmatch-server/src/image/image.controller.ts +++ b/bizmatch-server/src/image/image.controller.ts @@ -1,76 +1,74 @@ -import { Body, Controller, Delete, Get, Inject, Param, Post, UploadedFile, UseInterceptors } from '@nestjs/common'; +import { Controller, Delete, Get, Inject, Param, Post, UploadedFile, UseInterceptors } from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; -import { FileInterceptor } from '@nestjs/platform-express'; import { FileService } from '../file/file.service.js'; -import { SelectOptionsService } from '../select-options/select-options.service.js'; import { ListingsService } from '../listings/listings.service.js'; +import { SelectOptionsService } from '../select-options/select-options.service.js'; -import { Entity, EntityData } from 'redis-om'; -import { businesses, commercials } from 'src/drizzle/schema.js'; +import { commercials } from 'src/drizzle/schema.js'; import { CommercialPropertyListing } from 'src/models/db.model.js'; @Controller('image') export class ImageController { - - constructor(private fileService:FileService, - private listingService:ListingsService, - @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, - private selectOptions:SelectOptionsService) { - } - - @Post('uploadPropertyPicture/:id') - @UseInterceptors(FileInterceptor('file'),) - async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) { - const imagename = await this.fileService.storePropertyPicture(file,id); - // await this.listingService.addImage(id,imagename); - } + constructor( + private fileService: FileService, + private listingService: ListingsService, + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + private selectOptions: SelectOptionsService, + ) {} - @Post('uploadProfile/:id') - @UseInterceptors(FileInterceptor('file'),) - async uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) { - await this.fileService.storeProfilePicture(file,id); - } - - @Post('uploadCompanyLogo/:id') - @UseInterceptors(FileInterceptor('file'),) - async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) { - await this.fileService.storeCompanyLogo(file,id); - } - - @Get(':id') - async getPropertyImagesById(@Param('id') id:string): Promise { - const result = await this.listingService.findById(id,commercials); - const listing = result as CommercialPropertyListing; - if (listing.imageOrder){ - return listing.imageOrder - } else { - const imageOrder = await this.fileService.getPropertyImages(id); - listing.imageOrder=imageOrder; - this.listingService.updateListing(listing.id,listing,commercials); - return imageOrder; - } - } - @Get('profileImages/:userids') - async getProfileImagesForUsers(@Param('userids') userids:string): Promise { - return await this.fileService.getProfileImagesForUsers(userids); - } - @Get('companyLogos/:userids') - async getCompanyLogosForUsers(@Param('userids') userids:string): Promise { - return await this.fileService.getCompanyLogosForUsers(userids); - } - - @Delete('propertyPicture/:listingid/:imagename') - async deletePropertyImagesById(@Param('listingid') listingid:string,@Param('imagename') imagename:string): Promise { - this.fileService.deleteImage(`pictures/property/${listingid}/${imagename}`); - // await this.listingService.deleteImage(listingid,imagename); - } - @Delete('logo/:userid/') - async deleteLogoImagesById(@Param('id') id:string): Promise { - this.fileService.deleteImage(`pictures/property//${id}`) - } - @Delete('profile/:userid/') - async deleteProfileImagesById(@Param('id') id:string): Promise { - this.fileService.deleteImage(`pictures/property//${id}`) + @Post('uploadPropertyPicture/:id') + @UseInterceptors(FileInterceptor('file')) + async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) { + const imagename = await this.fileService.storePropertyPicture(file, id); + await this.listingService.addImage(id, imagename); + } + + @Post('uploadProfile/:id') + @UseInterceptors(FileInterceptor('file')) + async uploadProfile(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) { + await this.fileService.storeProfilePicture(file, id); + } + + @Post('uploadCompanyLogo/:id') + @UseInterceptors(FileInterceptor('file')) + async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) { + await this.fileService.storeCompanyLogo(file, id); + } + + @Get(':id') + async getPropertyImagesById(@Param('id') id: string): Promise { + const result = await this.listingService.findById(id, commercials); + const listing = result as CommercialPropertyListing; + if (listing.imageOrder) { + return listing.imageOrder; + } else { + const imageOrder = await this.fileService.getPropertyImages(id); + listing.imageOrder = imageOrder; + this.listingService.updateListing(listing.id, listing, commercials); + return imageOrder; } + } + @Get('profileImages/:userids') + async getProfileImagesForUsers(@Param('userids') userids: string): Promise { + return await this.fileService.getProfileImagesForUsers(userids); + } + @Get('companyLogos/:userids') + async getCompanyLogosForUsers(@Param('userids') userids: string): Promise { + return await this.fileService.getCompanyLogosForUsers(userids); + } + + @Delete('propertyPicture/:listingid/:imagename') + async deletePropertyImagesById(@Param('listingid') listingid: string, @Param('imagename') imagename: string): Promise { + this.fileService.deleteImage(`pictures/property/${listingid}/${imagename}`); + } + @Delete('logo/:userid/') + async deleteLogoImagesById(@Param('id') id: string): Promise { + this.fileService.deleteImage(`pictures/property//${id}`); + } + @Delete('profile/:userid/') + async deleteProfileImagesById(@Param('id') id: string): Promise { + this.fileService.deleteImage(`pictures/property//${id}`); + } } diff --git a/bizmatch-server/src/listings/business-listings.controller.ts b/bizmatch-server/src/listings/business-listings.controller.ts index 0858dac..0b087a0 100644 --- a/bizmatch-server/src/listings/business-listings.controller.ts +++ b/bizmatch-server/src/listings/business-listings.controller.ts @@ -1,50 +1,47 @@ import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nestjs/common'; -import { FileService } from '../file/file.service.js'; -import { convertStringToNullUndefined } from '../utils.js'; -import { ListingsService } from './listings.service.js'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; -import { ListingCriteria } from '../models/main.model.js'; import { businesses } from '../drizzle/schema.js'; +import { ListingCriteria } from '../models/main.model.js'; +import { ListingsService } from './listings.service.js'; @Controller('listings/business') export class BusinessListingsController { - - constructor(private readonly listingsService:ListingsService, - @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) { - } + constructor( + private readonly listingsService: ListingsService, + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + ) {} @Get(':id') - findById(@Param('id') id:string): any { - return this.listingsService.findById(id,businesses); + findById(@Param('id') id: string): any { + return this.listingsService.findById(id, businesses); } @Get('user/:userid') - findByUserId(@Param('userid') userid:string): any { - return this.listingsService.findByUserId(userid,businesses); + findByUserId(@Param('userid') userid: string): any { + return this.listingsService.findByUserId(userid, businesses); } @Post('search') find(@Body() criteria: ListingCriteria): any { - return this.listingsService.findListingsByCriteria(criteria,businesses); + return this.listingsService.findListingsByCriteria(criteria, businesses); } @Post() - create(@Body() listing: any){ + create(@Body() listing: any) { this.logger.info(`Save Listing`); - this.listingsService.createListing(listing,businesses) + return this.listingsService.createListing(listing, businesses); } @Put() - update(@Body() listing: any){ + update(@Body() listing: any) { this.logger.info(`Save Listing`); - this.listingsService.updateListing(listing.id,listing,businesses) + return this.listingsService.updateListing(listing.id, listing, businesses); } @Delete(':id') - deleteById(@Param('id') id:string){ - this.listingsService.deleteListing(id,businesses) + deleteById(@Param('id') id: string) { + this.listingsService.deleteListing(id, businesses); } @Get('states/all') getStates(): any { return this.listingsService.getStates(businesses); } } - diff --git a/bizmatch-server/src/listings/listings.service.ts b/bizmatch-server/src/listings/listings.service.ts index 19c2a06..a3a8034 100644 --- a/bizmatch-server/src/listings/listings.service.ts +++ b/bizmatch-server/src/listings/listings.service.ts @@ -1,29 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; -import { - ListingCriteria, - ListingType, - ImageProperty, - ListingCategory, - ResponseBusinessListing -} from '../models/main.model.js'; -import { convertStringToNullUndefined } from '../utils.js'; -import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; -import { Logger } from 'winston'; -import { EntityData, EntityId, Schema, SchemaDefinition } from 'redis-om'; -import { SQL, eq, gte, ilike, lte, sql, and} from 'drizzle-orm'; -import { PG_CONNECTION, businesses, commercials, } from '../drizzle/schema.js'; +import { and, eq, gte, ilike, lte, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; -import * as schema from '../drizzle/schema.js'; -import { PgTableFn, PgTableWithColumns, QueryBuilder } from 'drizzle-orm/pg-core'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { BusinessListing, CommercialPropertyListing } from 'src/models/db.model.js'; +import { Logger } from 'winston'; +import * as schema from '../drizzle/schema.js'; +import { PG_CONNECTION, businesses, commercials } from '../drizzle/schema.js'; +import { ListingCriteria } from '../models/main.model.js'; @Injectable() export class ListingsService { - - constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, - @Inject(PG_CONNECTION) private conn: NodePgDatabase,) { - } - private getConditions(criteria: ListingCriteria,table: typeof businesses | typeof commercials): any[] { + constructor( + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + @Inject(PG_CONNECTION) private conn: NodePgDatabase, + ) {} + private getConditions(criteria: ListingCriteria, table: typeof businesses | typeof commercials): any[] { const conditions = []; if (criteria.type) { conditions.push(eq(table.type, criteria.type)); @@ -51,36 +42,49 @@ export class ListingsService { async findListingsByCriteria(criteria: ListingCriteria, table: typeof businesses | typeof commercials): Promise<{ data: Record[]; total: number }> { const start = criteria.start ? criteria.start : 0; const length = criteria.length ? criteria.length : 12; - return await this.findListings(table, criteria, start, length) + return await this.findListings(table, criteria, start, length); } private async findListings(table: typeof businesses | typeof commercials, criteria: ListingCriteria, start = 0, length = 12): Promise { - const conditions = this.getConditions(criteria,table) + const conditions = this.getConditions(criteria, table); const [data, total] = await Promise.all([ - this.conn.select().from(table).where(and(...conditions)).offset(start).limit(length), - this.conn.select({ count: sql`count(*)` }).from(table).where(and(...conditions)).then((result) => Number(result[0].count)), + this.conn + .select() + .from(table) + .where(and(...conditions)) + .offset(start) + .limit(length), + this.conn + .select({ count: sql`count(*)` }) + .from(table) + .where(and(...conditions)) + .then(result => Number(result[0].count)), ]); return { total, data }; } async findById(id: string, table: typeof businesses | typeof commercials): Promise { - const result = await this.conn.select().from(table).where(sql`${table.id} = ${id}`) - return result[0] as BusinessListing | CommercialPropertyListing + const result = await this.conn + .select() + .from(table) + .where(sql`${table.id} = ${id}`); + return result[0] as BusinessListing | CommercialPropertyListing; } async findByUserId(userId: string, table: typeof businesses | typeof commercials): Promise { - return await this.conn.select().from(table).where(eq(table.userId, userId)) as BusinessListing[] | CommercialPropertyListing[] + return (await this.conn.select().from(table).where(eq(table.userId, userId))) as BusinessListing[] | CommercialPropertyListing[]; } async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise { - data.created=new Date() - data.updated=new Date() - data.visits=0; - data.lastVisit=null + data.created = new Date(); + data.updated = new Date(); + data.visits = 0; + data.lastVisit = null; const [createdListing] = await this.conn.insert(table).values(data).returning(); return createdListing as BusinessListing | CommercialPropertyListing; } async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise { - data.updated=new Date(); + data.updated = new Date(); + data.created = new Date(data.created); const [updateListing] = await this.conn.update(table).set(data).where(eq(table.id, id)).returning(); return updateListing as BusinessListing | CommercialPropertyListing; } @@ -89,29 +93,33 @@ export class ListingsService { await this.conn.delete(table).where(eq(table.id, id)); } async getStates(table: typeof businesses | typeof commercials): Promise { - return await this.conn.select({state: table.state,count: sql`count(${table.id})`.mapWith(Number)}).from(table).groupBy(sql`${table.state}`).orderBy(sql`count desc`); + return await this.conn + .select({ state: table.state, count: sql`count(${table.id})`.mapWith(Number) }) + .from(table) + .groupBy(sql`${table.state}`) + .orderBy(sql`count desc`); } // ############################################################## // Images for commercial Properties // ############################################################## async updateImageOrder(id: string, imageOrder: string[]) { - const listing = await this.findById(id, commercials) as unknown as CommercialPropertyListing + const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing; listing.imageOrder = imageOrder; - await this.updateListing(listing.id, listing, commercials) + await this.updateListing(listing.id, listing, commercials); } - async deleteImage(id: string, name: string,) { - const listing = await this.findById(id, commercials) as unknown as CommercialPropertyListing + async deleteImage(id: string, name: string) { + const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing; const index = listing.imageOrder.findIndex(im => im === name); if (index > -1) { listing.imageOrder.splice(index, 1); - await this.updateListing(listing.id, listing, commercials) + await this.updateListing(listing.id, listing, commercials); } } async addImage(id: string, imagename: string) { - const listing = await this.findById(id, commercials) as unknown as CommercialPropertyListing + const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing; listing.imageOrder.push(imagename); - await this.updateListing(listing.id, listing, commercials) + listing.imagePath = listing.id; + await this.updateListing(listing.id, listing, commercials); } - } diff --git a/bizmatch-server/src/mail/mail.controller.ts b/bizmatch-server/src/mail/mail.controller.ts index f8ff3a4..1377b13 100644 --- a/bizmatch-server/src/mail/mail.controller.ts +++ b/bizmatch-server/src/mail/mail.controller.ts @@ -1,15 +1,13 @@ -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { Body, Controller, Post } from '@nestjs/common'; +import { User } from 'src/models/db.model.js'; +import { MailInfo } from 'src/models/main.model.js'; import { MailService } from './mail.service.js'; -import { KeycloakUser, MailInfo } from 'src/models/main.model.js'; - @Controller('mail') export class MailController { - constructor(private mailService:MailService){ - - } - @Post() - sendEMail(@Body() mailInfo: MailInfo): Promise< KeycloakUser> { - return this.mailService.sendInquiry(mailInfo); - } + constructor(private mailService: MailService) {} + @Post() + sendEMail(@Body() mailInfo: MailInfo): Promise { + return this.mailService.sendInquiry(mailInfo); + } } diff --git a/bizmatch-server/src/mail/mail.module.ts b/bizmatch-server/src/mail/mail.module.ts index bda9800..f424531 100644 --- a/bizmatch-server/src/mail/mail.module.ts +++ b/bizmatch-server/src/mail/mail.module.ts @@ -1,22 +1,27 @@ -import { Module } from '@nestjs/common'; -import { MailService } from './mail.service.js'; -import { MailController } from './mail.controller.js'; import { MailerModule } from '@nestjs-modules/mailer'; -import path, { join } from 'path'; import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter.js'; +import { Module } from '@nestjs/common'; +import path, { join } from 'path'; import { fileURLToPath } from 'url'; -import { AuthModule } from '../auth/auth.module.js'; +import { DrizzleModule } from '../drizzle/drizzle.module.js'; +import { FileService } from '../file/file.service.js'; +import { UserModule } from '../user/user.module.js'; +import { UserService } from '../user/user.service.js'; +import { MailController } from './mail.controller.js'; +import { MailService } from './mail.service.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @Module({ - imports: [AuthModule, + imports: [ + DrizzleModule, + UserModule, MailerModule.forRoot({ // transport: 'smtps://user@example.com:topsecret@smtp.example.com', // or transport: { host: 'email-smtp.us-east-2.amazonaws.com', secure: false, - port:587, + port: 587, // auth: { // user: 'andreas.knuth@gmail.com', // pass: 'ksnh xjae dqbv xana', @@ -38,7 +43,7 @@ const __dirname = path.dirname(__filename); }, }), ], - providers: [MailService], - controllers: [MailController] + providers: [MailService, UserService, FileService], + controllers: [MailController], }) export class MailModule {} diff --git a/bizmatch-server/src/mail/mail.service.ts b/bizmatch-server/src/mail/mail.service.ts index 756c32f..712aaca 100644 --- a/bizmatch-server/src/mail/mail.service.ts +++ b/bizmatch-server/src/mail/mail.service.ts @@ -1,26 +1,41 @@ import { MailerService } from '@nestjs-modules/mailer'; import { Injectable } from '@nestjs/common'; -import { AuthService } from '../auth/auth.service.js'; -import { KeycloakUser, MailInfo } from '../models/main.model.js'; +import path, { join } from 'path'; +import { fileURLToPath } from 'url'; +import { User } from '../models/db.model.js'; +import { MailInfo } from '../models/main.model.js'; +import { UserService } from '../user/user.service.js'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); @Injectable() export class MailService { - constructor(private mailerService: MailerService, private authService:AuthService) {} - - async sendInquiry(mailInfo: MailInfo):Promise { - const user = await this.authService.getUser(mailInfo.userId) as KeycloakUser; - console.log(JSON.stringify(user)); - await this.mailerService.sendMail({ - to: user.email, - from: '"Bizmatch Team" ', // override default from - subject: `Inquiry from ${mailInfo.sender.name}`, - template: './inquiry', // `.hbs` extension is appended automatically - context: { // ✏️ filling curly brackets with content - name: user.firstName, - inquiry:mailInfo.sender.comments - }, - }); - return user - } - + constructor( + private mailerService: MailerService, + private userService: UserService, + ) {} + + async sendInquiry(mailInfo: MailInfo): Promise { + //const user = await this.authService.getUser(mailInfo.userId) as KeycloakUser; + const user = await this.userService.getUserByMail(mailInfo.email); + console.log(JSON.stringify(user)); + await this.mailerService.sendMail({ + to: user.email, + from: '"Bizmatch Team" ', // override default from + subject: `Inquiry from ${mailInfo.sender.name}`, + //template: './inquiry', // `.hbs` extension is appended automatically + template: join(__dirname, '../..', 'mail/templates/inquiry.hbs'), + context: { + // ✏️ filling curly brackets with content + name: user.firstname, + inquiry: mailInfo.sender.comments, + internalListingNumber: mailInfo.listing.internalListingNumber, + title: mailInfo.listing.title, + iname: mailInfo.sender.name, + phone: mailInfo.sender.phoneNumber, + email: mailInfo.sender.email, + }, + }); + return user; } +} diff --git a/bizmatch-server/src/models/db.model.ts b/bizmatch-server/src/models/db.model.ts index fdfc006..13b94b9 100644 --- a/bizmatch-server/src/models/db.model.ts +++ b/bizmatch-server/src/models/db.model.ts @@ -1,5 +1,5 @@ export interface User { - id: string; + id?: string; firstname: string; lastname: string; email: string; diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index 53c7802..45ce934 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -65,6 +65,7 @@ export interface ListingCriteria { realEstateChecked: boolean; title: string; category: 'professional|broker'; + name: string; } export interface KeycloakUser { @@ -148,6 +149,8 @@ export interface AutoCompleteCompleteEvent { export interface MailInfo { sender: Sender; userId: string; + email: string; + listing?: BusinessListing; } export interface Sender { name?: string; diff --git a/bizmatch-server/src/select-options/select-options.service.ts b/bizmatch-server/src/select-options/select-options.service.ts index c11f924..c74d2e7 100644 --- a/bizmatch-server/src/select-options/select-options.service.ts +++ b/bizmatch-server/src/select-options/select-options.service.ts @@ -3,113 +3,105 @@ import { ImageType, KeyValue, KeyValueStyle } from '../models/main.model.js'; @Injectable() export class SelectOptionsService { - - constructor() { } - public typesOfBusiness: Array = [ - { name: 'Automotive', value: '1', icon:'fa-solid fa-car',bgColorClass:'bg-green-100',textColorClass:'text-green-600' }, - { name: 'Industrial Services', value: '2', icon:'fa-solid fa-industry',bgColorClass:'bg-yellow-100',textColorClass:'text-yellow-600'}, - { name: 'Real Estate', value: '3' , icon:'pi pi-building',bgColorClass:'bg-blue-100',textColorClass:'text-blue-600'}, - { name: 'Uncategorized', value: '4' , icon:'pi pi-question',bgColorClass:'bg-cyan-100',textColorClass:'text-cyan-600'}, - { name: 'Retail', value: '5' , icon:'fa-solid fa-money-bill-wave',bgColorClass:'bg-pink-100',textColorClass:'text-pink-600'}, - { name: 'Oilfield SVE and MFG.', value: '6' , icon:'fa-solid fa-oil-well',bgColorClass:'bg-indigo-100',textColorClass:'text-indigo-600'}, - { name: 'Service', value: '7' , icon:'fa-solid fa-umbrella',bgColorClass:'bg-teal-100',textColorClass:'text-teal-600'}, - { name: 'Advertising', value: '8' , icon:'fa-solid fa-rectangle-ad',bgColorClass:'bg-orange-100',textColorClass:'text-orange-600'}, - { name: 'Agriculture', value: '9' , icon:'fa-solid fa-wheat-awn',bgColorClass:'bg-bluegray-100',textColorClass:'text-bluegray-600'}, - { name: 'Franchise', value: '10' , icon:'pi pi-star',bgColorClass:'bg-purple-100',textColorClass:'text-purple-600'}, - { name: 'Professional', value: '11' , icon:'fa-solid fa-user-gear',bgColorClass:'bg-gray-100',textColorClass:'text-gray-600'}, - { name: 'Manufacturing', value: '12' , icon:'fa-solid fa-industry',bgColorClass:'bg-red-100',textColorClass:'text-red-600'}, - { name: 'Food and Restaurant', value: '13' , icon:'fa-solid fa-utensils',bgColorClass:'bg-primary-100',textColorClass:'text-primary-600'}, - ]; - public typesOfCommercialProperty: Array = [ - { name: 'Retail', value: '100' , icon:'fa-solid fa-money-bill-wave',bgColorClass:'bg-pink-100',textColorClass:'text-pink-600'}, - { name: 'Land', value: '101' , icon:'pi pi-building',bgColorClass:'bg-blue-100',textColorClass:'text-blue-600'}, - { name: 'Industrial', value: '102', icon:'fa-solid fa-industry',bgColorClass:'bg-yellow-100',textColorClass:'text-yellow-600'}, - { name: 'Office', value: '103' , icon:'fa-solid fa-umbrella',bgColorClass:'bg-teal-100',textColorClass:'text-teal-600'}, - { name: 'Mixed Use', value: '104' , icon:'fa-solid fa-rectangle-ad',bgColorClass:'bg-orange-100',textColorClass:'text-orange-600'}, - { name: 'Multifamily', value: '105' , icon:'pi pi-star',bgColorClass:'bg-purple-100',textColorClass:'text-purple-600'}, - { name: 'Uncategorized', value: '106' , icon:'pi pi-question',bgColorClass:'bg-cyan-100',textColorClass:'text-cyan-600'}, - ]; - public prices: Array = [ - { name: '$100K', value: '100000' }, - { name: '$250K', value: '250000' }, - { name: '$500K', value: '500000' }, - { name: '$1M', value: '1000000' }, - { name: '$5M', value: '5000000' }, - ]; - public listingCategories: Array = [ - { name: 'Business', value: 'business' }, - { name: 'Commercial Property', value: 'commercialProperty' }, - ] - public categories: Array = [ - { name: 'Broker', value: 'broker', icon:'pi-image',bgColorClass:'bg-green-100',textColorClass:'text-green-600' }, - { name: 'Professional', value: 'professional', icon:'pi-globe',bgColorClass:'bg-yellow-100',textColorClass:'text-yellow-600' }, - ] - public imageTypes:ImageType[] = [ - {name:'propertyPicture',upload:'uploadPropertyPicture',delete:'propertyPicture'}, - {name:'companyLogo',upload:'uploadCompanyLogo',delete:'logo'}, - {name:'profile',upload:'uploadProfile',delete:'profile'}, - ] - private usStates = [ - { name: 'ALABAMA', abbreviation: 'AL'}, - { name: 'ALASKA', abbreviation: 'AK'}, - { name: 'AMERICAN SAMOA', abbreviation: 'AS'}, - { name: 'ARIZONA', abbreviation: 'AZ'}, - { name: 'ARKANSAS', abbreviation: 'AR'}, - { name: 'CALIFORNIA', abbreviation: 'CA'}, - { name: 'COLORADO', abbreviation: 'CO'}, - { name: 'CONNECTICUT', abbreviation: 'CT'}, - { name: 'DELAWARE', abbreviation: 'DE'}, - { name: 'DISTRICT OF COLUMBIA', abbreviation: 'DC'}, - { name: 'FEDERATED STATES OF MICRONESIA', abbreviation: 'FM'}, - { name: 'FLORIDA', abbreviation: 'FL'}, - { name: 'GEORGIA', abbreviation: 'GA'}, - { name: 'GUAM', abbreviation: 'GU'}, - { name: 'HAWAII', abbreviation: 'HI'}, - { name: 'IDAHO', abbreviation: 'ID'}, - { name: 'ILLINOIS', abbreviation: 'IL'}, - { name: 'INDIANA', abbreviation: 'IN'}, - { name: 'IOWA', abbreviation: 'IA'}, - { name: 'KANSAS', abbreviation: 'KS'}, - { name: 'KENTUCKY', abbreviation: 'KY'}, - { name: 'LOUISIANA', abbreviation: 'LA'}, - { name: 'MAINE', abbreviation: 'ME'}, - { name: 'MARSHALL ISLANDS', abbreviation: 'MH'}, - { name: 'MARYLAND', abbreviation: 'MD'}, - { name: 'MASSACHUSETTS', abbreviation: 'MA'}, - { name: 'MICHIGAN', abbreviation: 'MI'}, - { name: 'MINNESOTA', abbreviation: 'MN'}, - { name: 'MISSISSIPPI', abbreviation: 'MS'}, - { name: 'MISSOURI', abbreviation: 'MO'}, - { name: 'MONTANA', abbreviation: 'MT'}, - { name: 'NEBRASKA', abbreviation: 'NE'}, - { name: 'NEVADA', abbreviation: 'NV'}, - { name: 'NEW HAMPSHIRE', abbreviation: 'NH'}, - { name: 'NEW JERSEY', abbreviation: 'NJ'}, - { name: 'NEW MEXICO', abbreviation: 'NM'}, - { name: 'NEW YORK', abbreviation: 'NY'}, - { name: 'NORTH CAROLINA', abbreviation: 'NC'}, - { name: 'NORTH DAKOTA', abbreviation: 'ND'}, - { name: 'NORTHERN MARIANA ISLANDS', abbreviation: 'MP'}, - { name: 'OHIO', abbreviation: 'OH'}, - { name: 'OKLAHOMA', abbreviation: 'OK'}, - { name: 'OREGON', abbreviation: 'OR'}, - { name: 'PALAU', abbreviation: 'PW'}, - { name: 'PENNSYLVANIA', abbreviation: 'PA'}, - { name: 'PUERTO RICO', abbreviation: 'PR'}, - { name: 'RHODE ISLAND', abbreviation: 'RI'}, - { name: 'SOUTH CAROLINA', abbreviation: 'SC'}, - { name: 'SOUTH DAKOTA', abbreviation: 'SD'}, - { name: 'TENNESSEE', abbreviation: 'TN'}, - { name: 'TEXAS', abbreviation: 'TX'}, - { name: 'UTAH', abbreviation: 'UT'}, - { name: 'VERMONT', abbreviation: 'VT'}, - { name: 'VIRGIN ISLANDS', abbreviation: 'VI'}, - { name: 'VIRGINIA', abbreviation: 'VA'}, - { name: 'WASHINGTON', abbreviation: 'WA'}, - { name: 'WEST VIRGINIA', abbreviation: 'WV'}, - { name: 'WISCONSIN', abbreviation: 'WI'}, - { name: 'WYOMING', abbreviation: 'WY' } - ] - public locations:Array = [...this.usStates.map(state=>({name:state.name, value:state.abbreviation}))].concat({name:'CANADA',value:"CA"}); - + constructor() {} + public typesOfBusiness: Array = [ + { name: 'Automotive', value: '1', icon: 'fa-solid fa-car', bgColorClass: 'bg-green-100', textColorClass: 'text-green-600' }, + { name: 'Industrial Services', value: '2', icon: 'fa-solid fa-industry', bgColorClass: 'bg-yellow-100', textColorClass: 'text-yellow-600' }, + { name: 'Real Estate', value: '3', icon: 'pi pi-building', bgColorClass: 'bg-blue-100', textColorClass: 'text-blue-600' }, + { name: 'Uncategorized', value: '4', icon: 'pi pi-question', bgColorClass: 'bg-cyan-100', textColorClass: 'text-cyan-600' }, + { name: 'Retail', value: '5', icon: 'fa-solid fa-money-bill-wave', bgColorClass: 'bg-pink-100', textColorClass: 'text-pink-600' }, + { name: 'Oilfield SVE and MFG.', value: '6', icon: 'fa-solid fa-oil-well', bgColorClass: 'bg-indigo-100', textColorClass: 'text-indigo-600' }, + { name: 'Service', value: '7', icon: 'fa-solid fa-umbrella', bgColorClass: 'bg-teal-100', textColorClass: 'text-teal-600' }, + { name: 'Advertising', value: '8', icon: 'fa-solid fa-rectangle-ad', bgColorClass: 'bg-orange-100', textColorClass: 'text-orange-600' }, + { name: 'Agriculture', value: '9', icon: 'fa-solid fa-wheat-awn', bgColorClass: 'bg-bluegray-100', textColorClass: 'text-bluegray-600' }, + { name: 'Franchise', value: '10', icon: 'pi pi-star', bgColorClass: 'bg-purple-100', textColorClass: 'text-purple-600' }, + { name: 'Professional', value: '11', icon: 'fa-solid fa-user-gear', bgColorClass: 'bg-gray-100', textColorClass: 'text-gray-600' }, + { name: 'Manufacturing', value: '12', icon: 'fa-solid fa-industry', bgColorClass: 'bg-red-100', textColorClass: 'text-red-600' }, + { name: 'Food and Restaurant', value: '13', icon: 'fa-solid fa-utensils', bgColorClass: 'bg-primary-100', textColorClass: 'text-primary-600' }, + ]; + public typesOfCommercialProperty: Array = [ + { name: 'Retail', value: '100', icon: 'fa-solid fa-money-bill-wave', bgColorClass: 'bg-pink-100', textColorClass: 'text-pink-600' }, + { name: 'Land', value: '101', icon: 'pi pi-building', bgColorClass: 'bg-blue-100', textColorClass: 'text-blue-600' }, + { name: 'Industrial', value: '102', icon: 'fa-solid fa-industry', bgColorClass: 'bg-yellow-100', textColorClass: 'text-yellow-600' }, + { name: 'Office', value: '103', icon: 'fa-solid fa-umbrella', bgColorClass: 'bg-teal-100', textColorClass: 'text-teal-600' }, + { name: 'Mixed Use', value: '104', icon: 'fa-solid fa-rectangle-ad', bgColorClass: 'bg-orange-100', textColorClass: 'text-orange-600' }, + { name: 'Multifamily', value: '105', icon: 'pi pi-star', bgColorClass: 'bg-purple-100', textColorClass: 'text-purple-600' }, + { name: 'Uncategorized', value: '106', icon: 'pi pi-question', bgColorClass: 'bg-cyan-100', textColorClass: 'text-cyan-600' }, + ]; + public prices: Array = [ + { name: '$100K', value: '100000' }, + { name: '$250K', value: '250000' }, + { name: '$500K', value: '500000' }, + { name: '$1M', value: '1000000' }, + { name: '$5M', value: '5000000' }, + ]; + public listingCategories: Array = [ + { name: 'Business', value: 'business' }, + { name: 'Commercial Property', value: 'commercialProperty' }, + ]; + public categories: Array = [ + { name: 'Broker', value: 'broker', icon: 'pi-image', bgColorClass: 'bg-green-100', textColorClass: 'text-green-600' }, + { name: 'Professional', value: 'professional', icon: 'pi-globe', bgColorClass: 'bg-yellow-100', textColorClass: 'text-yellow-600' }, + ]; + public imageTypes: ImageType[] = [ + { name: 'propertyPicture', upload: 'uploadPropertyPicture', delete: 'propertyPicture' }, + { name: 'companyLogo', upload: 'uploadCompanyLogo', delete: 'logo' }, + { name: 'profile', upload: 'uploadProfile', delete: 'profile' }, + ]; + private usStates = [ + { name: 'ALABAMA', abbreviation: 'AL' }, + { name: 'ALASKA', abbreviation: 'AK' }, + { name: 'ARIZONA', abbreviation: 'AZ' }, + { name: 'ARKANSAS', abbreviation: 'AR' }, + { name: 'CALIFORNIA', abbreviation: 'CA' }, + { name: 'COLORADO', abbreviation: 'CO' }, + { name: 'CONNECTICUT', abbreviation: 'CT' }, + { name: 'DELAWARE', abbreviation: 'DE' }, + { name: 'DISTRICT OF COLUMBIA', abbreviation: 'DC' }, + { name: 'FLORIDA', abbreviation: 'FL' }, + { name: 'GEORGIA', abbreviation: 'GA' }, + { name: 'GUAM', abbreviation: 'GU' }, + { name: 'HAWAII', abbreviation: 'HI' }, + { name: 'IDAHO', abbreviation: 'ID' }, + { name: 'ILLINOIS', abbreviation: 'IL' }, + { name: 'INDIANA', abbreviation: 'IN' }, + { name: 'IOWA', abbreviation: 'IA' }, + { name: 'KANSAS', abbreviation: 'KS' }, + { name: 'KENTUCKY', abbreviation: 'KY' }, + { name: 'LOUISIANA', abbreviation: 'LA' }, + { name: 'MAINE', abbreviation: 'ME' }, + { name: 'MARYLAND', abbreviation: 'MD' }, + { name: 'MASSACHUSETTS', abbreviation: 'MA' }, + { name: 'MICHIGAN', abbreviation: 'MI' }, + { name: 'MINNESOTA', abbreviation: 'MN' }, + { name: 'MISSISSIPPI', abbreviation: 'MS' }, + { name: 'MISSOURI', abbreviation: 'MO' }, + { name: 'MONTANA', abbreviation: 'MT' }, + { name: 'NEBRASKA', abbreviation: 'NE' }, + { name: 'NEVADA', abbreviation: 'NV' }, + { name: 'NEW HAMPSHIRE', abbreviation: 'NH' }, + { name: 'NEW JERSEY', abbreviation: 'NJ' }, + { name: 'NEW MEXICO', abbreviation: 'NM' }, + { name: 'NEW YORK', abbreviation: 'NY' }, + { name: 'NORTH CAROLINA', abbreviation: 'NC' }, + { name: 'NORTH DAKOTA', abbreviation: 'ND' }, + { name: 'OHIO', abbreviation: 'OH' }, + { name: 'OKLAHOMA', abbreviation: 'OK' }, + { name: 'OREGON', abbreviation: 'OR' }, + { name: 'PALAU', abbreviation: 'PW' }, + { name: 'PENNSYLVANIA', abbreviation: 'PA' }, + { name: 'RHODE ISLAND', abbreviation: 'RI' }, + { name: 'SOUTH CAROLINA', abbreviation: 'SC' }, + { name: 'SOUTH DAKOTA', abbreviation: 'SD' }, + { name: 'TENNESSEE', abbreviation: 'TN' }, + { name: 'TEXAS', abbreviation: 'TX' }, + { name: 'UTAH', abbreviation: 'UT' }, + { name: 'VERMONT', abbreviation: 'VT' }, + { name: 'VIRGINIA', abbreviation: 'VA' }, + { name: 'WASHINGTON', abbreviation: 'WA' }, + { name: 'WEST VIRGINIA', abbreviation: 'WV' }, + { name: 'WISCONSIN', abbreviation: 'WI' }, + { name: 'WYOMING', abbreviation: 'WY' }, + ]; + public locations: Array = [...this.usStates.map(state => ({ name: state.name, value: state.abbreviation }))].concat({ name: 'CANADA', value: 'CA' }); } diff --git a/bizmatch-server/src/user/user.service.ts b/bizmatch-server/src/user/user.service.ts index 275f53c..4142932 100644 --- a/bizmatch-server/src/user/user.service.ts +++ b/bizmatch-server/src/user/user.service.ts @@ -1,61 +1,77 @@ -import { Get, Inject, Injectable, Param } from '@nestjs/common'; -import { createClient } from 'redis'; -import { Entity, Repository, Schema } from 'redis-om'; -import { ListingCriteria } from '../models/main.model.js'; -import { REDIS_CLIENT } from '../redis/redis.module.js'; -import { FileService } from '../file/file.service.js'; -import { User } from 'src/models/db.model.js'; -import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; -import { Logger } from 'winston'; -import { PG_CONNECTION } from 'src/drizzle/schema.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { and, eq, ilike, or, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres/driver.js'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { PG_CONNECTION } from 'src/drizzle/schema.js'; +import { User } from 'src/models/db.model.js'; +import { Logger } from 'winston'; import * as schema from '../drizzle/schema.js'; -import { eq, sql,and } from 'drizzle-orm'; +import { FileService } from '../file/file.service.js'; +import { ListingCriteria } from '../models/main.model.js'; @Injectable() export class UserService { - constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, - @Inject(PG_CONNECTION) private conn: NodePgDatabase,private fileService:FileService) { + constructor( + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + @Inject(PG_CONNECTION) private conn: NodePgDatabase, + private fileService: FileService, + ) {} + private getConditions(criteria: ListingCriteria): any[] { + const conditions = []; + if (criteria.state) { + conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`); } - private getConditions(criteria: ListingCriteria): any[] { - const conditions = []; - if (criteria.state) { - conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`); - } - return conditions; - } - async getUserByMail( id:string){ - const users = await this.conn.select().from(schema.users).where(sql`email = ${id}`) as User[] - const user = users[0] - user.hasCompanyLogo=this.fileService.hasCompanyLogo(id); - user.hasProfile=this.fileService.hasProfile(id); - return user; + if (criteria.name) { + conditions.push(or(ilike(schema.users.firstname, `%${criteria.name}%`), ilike(schema.users.lastname, `%${criteria.name}%`))); } - async getUserById( id:string){ - const users = await this.conn.select().from(schema.users).where(sql`id = ${id}`) as User[] - const user = users[0] - user.hasCompanyLogo=this.fileService.hasCompanyLogo(id); - user.hasProfile=this.fileService.hasProfile(id); - return user; + return conditions; + } + async getUserByMail(email: string) { + const users = (await this.conn + .select() + .from(schema.users) + .where(sql`email = ${email}`)) as User[]; + const user = users[0]; + user.hasCompanyLogo = this.fileService.hasCompanyLogo(user.id); + user.hasProfile = this.fileService.hasProfile(user.id); + return user; + } + async getUserById(id: string) { + const users = (await this.conn + .select() + .from(schema.users) + .where(sql`id = ${id}`)) as User[]; + const user = users[0]; + user.hasCompanyLogo = this.fileService.hasCompanyLogo(id); + user.hasProfile = this.fileService.hasProfile(id); + return user; + } + async saveUser(user: any): Promise { + if (user.id) { + const [updateUser] = await this.conn.update(schema.users).set(user).where(eq(schema.users.id, user.id)).returning(); + return updateUser as User; + } else { + const [newUser] = await this.conn.insert(schema.users).values(user).returning(); + return newUser as User; } - async saveUser(user:any):Promise{ - if (user.id){ - const [updateUser] = await this.conn.update(schema.users).set(user).where(eq(schema.users.id, user.id)).returning(); - return updateUser as User; - } else { - const [newUser] = await this.conn.insert(schema.users).values(user).returning(); - return newUser as User; - } - } - async findUser(criteria:ListingCriteria){ - const start = criteria.start ? criteria.start : 0; - const length = criteria.length ? criteria.length : 12; - const conditions = this.getConditions(criteria) - const [data, total] = await Promise.all([ - this.conn.select().from(schema.users).where(and(...conditions)).offset(start).limit(length), - this.conn.select({ count: sql`count(*)` }).from(schema.users).where(and(...conditions)).then((result) => Number(result[0].count)), - ]); - return { total, data }; - } - -} \ No newline at end of file + } + async findUser(criteria: ListingCriteria) { + const start = criteria.start ? criteria.start : 0; + const length = criteria.length ? criteria.length : 12; + const conditions = this.getConditions(criteria); + const [data, total] = await Promise.all([ + this.conn + .select() + .from(schema.users) + .where(and(...conditions)) + .offset(start) + .limit(length), + this.conn + .select({ count: sql`count(*)` }) + .from(schema.users) + .where(and(...conditions)) + .then(result => Number(result[0].count)), + ]); + return { total, data }; + } +} diff --git a/bizmatch/proxy.conf.json b/bizmatch/proxy.conf.json index 85795bc..5280472 100644 --- a/bizmatch/proxy.conf.json +++ b/bizmatch/proxy.conf.json @@ -7,5 +7,15 @@ "target": "http://localhost:3000", "secure": false, "changeOrigin": true + }, + "/logo": { + "target": "http://localhost:3000", + "secure": false, + "changeOrigin": true + }, + "/profile": { + "target": "http://localhost:3000", + "secure": false, + "changeOrigin": true } } \ No newline at end of file diff --git a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.html b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.html index adfc142..707ccaa 100644 --- a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.html +++ b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.html @@ -12,7 +12,7 @@
  • -
    Description
    +
    Description
  • @@ -50,16 +50,6 @@
    {{ listing.brokerLicencing }}
- - - - - - @if(listing && user && (user.id===listing?.userId || isAdmin())){ } diff --git a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts index 0d20b84..9ee3b87 100644 --- a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts +++ b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts @@ -1,3 +1,4 @@ +import { Location } from '@angular/common'; import { Component } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; @@ -46,7 +47,6 @@ export class DetailsBusinessListingComponent { listing: BusinessListing; criteria: ListingCriteria; mailinfo: MailInfo; - propertyImages: string[] = []; environment = environment; user: User; description: SafeHtml; @@ -59,27 +59,28 @@ export class DetailsBusinessListingComponent { private mailService: MailService, private messageService: MessageService, private sanitizer: DomSanitizer, + private location: Location, ) { this.userService.getUserObservable().subscribe(user => { this.user = user; + this.mailinfo = { sender: {}, userId: '', email: user.email }; }); this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); - this.mailinfo = { sender: {}, userId: '' }; } async ngOnInit() { this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business')); - this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description); } back() { - this.router.navigate(['businessListings']); + this.location.back(); } isAdmin() { return this.userService.hasAdminRole(); } async mail() { this.mailinfo.userId = this.listing.userId; + this.mailinfo.listing = this.listing; await this.mailService.mail(this.mailinfo); this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Your message has been sent to the creator of the listing', life: 3000 }); } diff --git a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.html b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.html index f2aa1f3..0265946 100644 --- a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.html +++ b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.html @@ -12,7 +12,7 @@
  • -
    Description
    +
    Description
  • diff --git a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts index bb0a7ed..6ecffdf 100644 --- a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts @@ -1,3 +1,4 @@ +import { Location } from '@angular/common'; import { Component } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; @@ -59,12 +60,13 @@ export class DetailsCommercialPropertyListingComponent { private mailService: MailService, private messageService: MessageService, private sanitizer: DomSanitizer, + private location: Location, ) { this.userService.getUserObservable().subscribe(user => { this.user = user; + this.mailinfo = { sender: {}, userId: '', email: user.email }; }); this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); - this.mailinfo = { sender: {}, userId: '' }; } async ngOnInit() { @@ -73,13 +75,14 @@ export class DetailsCommercialPropertyListingComponent { this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description); } back() { - this.router.navigate(['commercialPropertyListings']); + this.location.back(); } isAdmin() { return this.userService.hasAdminRole(); } async mail() { this.mailinfo.userId = this.listing.userId; + this.mailinfo.listing = this.listing; await this.mailService.mail(this.mailinfo); this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Your message has been sent to the creator of the listing', life: 3000 }); } diff --git a/bizmatch/src/app/pages/details/details-user/details-user.component.html b/bizmatch/src/app/pages/details/details-user/details-user.component.html index 3718552..6c3164e 100644 --- a/bizmatch/src/app/pages/details/details-user/details-user.component.html +++ b/bizmatch/src/app/pages/details/details-user/details-user.component.html @@ -1,5 +1,6 @@
    + @if (user){
    - +
    - +
    @@ -52,7 +58,7 @@

    Location: {{ selectOptions.getState(listing.state) }}

    Established: {{ listing.established }}

    - +
diff --git a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.scss b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.scss index 57253e1..3ea0761 100644 --- a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.scss +++ b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.scss @@ -1,23 +1,26 @@ #sky-line { - background-image: url(../../../../assets/images/bw-sky.jpg); - height: 204px; - background-position: bottom; - background-size: cover; - margin-bottom: -1px; + background-image: url(../../../../assets/images/bw-sky.jpg); + height: 204px; + background-position: bottom; + background-size: cover; + margin-bottom: -1px; } -.search{ - background-color: #343F69; +.search { + background-color: #343f69; } ::ng-deep p-paginator div { - background-color: var(--surface-200) !important; - // background-color: var(--surface-400) !important; + background-color: var(--surface-200) !important; + // background-color: var(--surface-400) !important; } .rounded-image { - border-radius: 6px; - // width: 100px; - max-width: 100px; - height: 45px; - border: 1px solid rgba(0,0,0,0.2); - padding: 1px 1px; - object-fit: contain; -} \ No newline at end of file + border-radius: 6px; + // width: 100px; + max-width: 100px; + height: 45px; + border: 1px solid rgba(0, 0, 0, 0.2); + padding: 1px 1px; + object-fit: contain; +} +::ng-deep span.p-button-label { + font-weight: 500; +} diff --git a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts index f8601c1..c5a003e 100644 --- a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts +++ b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts @@ -6,10 +6,12 @@ import onChange from 'on-change'; import { ButtonModule } from 'primeng/button'; import { CheckboxModule } from 'primeng/checkbox'; import { DropdownModule } from 'primeng/dropdown'; +import { InputGroupModule } from 'primeng/inputgroup'; import { InputTextModule } from 'primeng/inputtext'; import { PaginatorModule } from 'primeng/paginator'; import { StyleClassModule } from 'primeng/styleclass'; import { ToggleButtonModule } from 'primeng/togglebutton'; +import { TooltipModule } from 'primeng/tooltip'; import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model'; import { ListingCriteria, ListingType } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; @@ -21,7 +23,21 @@ import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler } @Component({ selector: 'app-business-listings', standalone: true, - imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, StyleClassModule, ToggleButtonModule, RouterModule, PaginatorModule], + imports: [ + CommonModule, + StyleClassModule, + ButtonModule, + CheckboxModule, + InputTextModule, + DropdownModule, + FormsModule, + StyleClassModule, + ToggleButtonModule, + RouterModule, + PaginatorModule, + InputGroupModule, + TooltipModule, + ], templateUrl: './business-listings.component.html', styleUrl: './business-listings.component.scss', }) @@ -36,10 +52,10 @@ export class BusinessListingsComponent { type: string; states = []; state: string; - first: number = 0; - rows: number = 12; totalRecords: number = 0; ts = new Date().getTime(); + first: number = 0; + rows: number = 12; public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined; constructor( @@ -56,7 +72,6 @@ export class BusinessListingsComponent { this.activatedRoute.params.subscribe(params => { if (this.activatedRoute.snapshot.fragment === '') { this.criteria = onChange(createGenericObject(), getSessionStorageHandler); - this.first = 0; } this.category = (params).type; this.init(); @@ -68,6 +83,11 @@ export class BusinessListingsComponent { this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); this.search(); } + refine() { + this.criteria.start = 0; + this.criteria.page = 0; + this.search(); + } async search() { const listingReponse = await this.listingsService.getListings(this.criteria, 'business'); this.listings = listingReponse.data; @@ -85,4 +105,7 @@ export class BusinessListingsComponent { imageErrorHandler(listing: ListingType) { // listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann } + reset() { + this.criteria.title = null; + } } diff --git a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.html b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.html index 6ada800..872eabb 100644 --- a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.html +++ b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.html @@ -5,8 +5,31 @@
-
- +
+ +
+
+ +
+
+ +
+
+ + + + +
+
+
@@ -19,7 +42,7 @@
- @if (listing.imageOrder.length>0){ + @if (listing.imageOrder?.length>0){ Image } @else { @@ -60,9 +83,12 @@ }
+
+
Total number of Listings: {{ totalRecords }}
+
diff --git a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts index e9860d6..d3dec2c 100644 --- a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts +++ b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts @@ -6,12 +6,13 @@ import onChange from 'on-change'; import { ButtonModule } from 'primeng/button'; import { CheckboxModule } from 'primeng/checkbox'; import { DropdownModule } from 'primeng/dropdown'; +import { InputGroupModule } from 'primeng/inputgroup'; import { InputTextModule } from 'primeng/inputtext'; import { PaginatorModule } from 'primeng/paginator'; import { StyleClassModule } from 'primeng/styleclass'; import { ToggleButtonModule } from 'primeng/togglebutton'; import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria, ListingType } from '../../../../../../bizmatch-server/src/models/main.model'; +import { ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { ImageService } from '../../../services/image.service'; import { ListingsService } from '../../../services/listings.service'; @@ -21,7 +22,7 @@ import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler } @Component({ selector: 'app-commercial-property-listings', standalone: true, - imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, StyleClassModule, ToggleButtonModule, RouterModule, PaginatorModule], + imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, StyleClassModule, ToggleButtonModule, RouterModule, PaginatorModule, InputGroupModule], templateUrl: './commercial-property-listings.component.html', styleUrl: './commercial-property-listings.component.scss', }) @@ -31,15 +32,14 @@ export class CommercialPropertyListingsComponent { filteredListings: Array; criteria: ListingCriteria; realEstateChecked: boolean; - + first: number = 0; + rows: number = 12; maxPrice: string; minPrice: string; type: string; states = []; statesSet = new Set(); state: string; - first: number = 0; - rows: number = 12; totalRecords: number = 0; ts = new Date().getTime(); @@ -52,12 +52,12 @@ export class CommercialPropertyListingsComponent { private imageService: ImageService, ) { this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria.type = undefined; this.router.getCurrentNavigation(); this.activatedRoute.snapshot; this.activatedRoute.params.subscribe(params => { if (this.activatedRoute.snapshot.fragment === '') { this.criteria = onChange(createGenericObject(), getSessionStorageHandler); - this.first = 0; } this.init(); }); @@ -68,7 +68,11 @@ export class CommercialPropertyListingsComponent { this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); this.search(); } - + refine() { + this.criteria.start = 0; + this.criteria.page = 0; + this.search(); + } async search() { const listingReponse = await this.listingsService.getListings(this.criteria, 'commercialProperty'); this.listings = listingReponse.data; @@ -83,7 +87,7 @@ export class CommercialPropertyListingsComponent { this.criteria.pageCount = event.pageCount; this.search(); } - imageErrorHandler(listing: ListingType) { - // listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann + reset() { + this.criteria.title = null; } } diff --git a/bizmatch/src/app/pages/subscription/account/account.component.html b/bizmatch/src/app/pages/subscription/account/account.component.html index 0ab3818..1886205 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.html +++ b/bizmatch/src/app/pages/subscription/account/account.component.html @@ -103,6 +103,7 @@ (is shown in every offer) @if(user?.hasCompanyLogo){ + } @else { } @@ -121,7 +122,7 @@
Your Profile Picture - @if(user.hasProfile){ + @if(user?.hasProfile){ } @else { diff --git a/bizmatch/src/app/pages/subscription/account/account.component.ts b/bizmatch/src/app/pages/subscription/account/account.component.ts index f7fa798..f74b8f7 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.ts +++ b/bizmatch/src/app/pages/subscription/account/account.component.ts @@ -57,17 +57,25 @@ export class AccountComponent { public dialogService: DialogService, ) {} async ngOnInit() { - const email = this.userService.getUser().email; - this.user = await this.userService.getByMail(email); - this.userLicensedIn = this.user.licensedIn.map(l => { - return { name: l.split('|')[0], value: l.split('|')[1] }; - }); + const keycloakUser = this.userService.getKeycloakUser(); + const email = keycloakUser.email; + try { + this.user = await this.userService.getByMail(email); + } catch (e) { + this.user = { email, firstname: keycloakUser.firstname, lastname: keycloakUser.lastname }; + this.user = await this.userService.save(this.user); + } + this.userLicensedIn = this.user.licensedIn + ? this.user.licensedIn.map(l => { + return { name: l.split('|')[0], value: l.split('|')[1] }; + }) + : []; this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions()); if (!this.user.licensedIn || this.user.licensedIn?.length === 0) { this.user.licensedIn = ['']; } - this.profileUrl = this.user.hasProfile ? `${environment.apiBaseUrl}/profile/${this.user.id}.avif` : `/assets/images/placeholder.png`; - this.companyLogoUrl = this.user.hasCompanyLogo ? `${environment.apiBaseUrl}/logo/${this.user.id}.avif` : `/assets/images/placeholder.png`; + this.profileUrl = this.user.hasProfile ? `profile/${this.user.id}.avif` : `/assets/images/placeholder.png`; + this.companyLogoUrl = this.user.hasCompanyLogo ? `logo/${this.user.id}.avif` : `/assets/images/placeholder.png`; } printInvoice(invoice: Invoice) {} diff --git a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html index 44121c1..8e86c8f 100644 --- a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html +++ b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html @@ -36,16 +36,7 @@
- +
diff --git a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts index 7b0a055..7550ff3 100644 --- a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts @@ -85,6 +85,7 @@ export class EditBusinessListingComponent { draggedImage: ImageProperty; faTrash = faTrash; data: CommercialPropertyListing; + typesOfBusiness = []; constructor( public selectOptions: SelectOptionsService, private router: Router, @@ -109,10 +110,13 @@ export class EditBusinessListingComponent { this.data = this.router.getCurrentNavigation().extras.state['data']; } }); + this.typesOfBusiness = selectOptions.typesOfBusiness.map(e => { + return { name: e.name, value: parseInt(e.value) }; + }); } async ngOnInit() { if (this.mode === 'edit') { - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id)); + this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business')); } else { this.listing = createGenericObject(); this.listing.listingsCategory = 'business'; diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html index 77d6a74..73a28e1 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html @@ -38,7 +38,7 @@
diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts index f100ba4..b5c16d3 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts @@ -89,6 +89,7 @@ export class EditCommercialPropertyListingComponent { suggestions: string[] | undefined; data: BusinessListing; userId: string; + typesOfCommercialProperty = []; constructor( public selectOptions: SelectOptionsService, private router: Router, @@ -103,7 +104,6 @@ export class EditCommercialPropertyListingComponent { private confirmationService: ConfirmationService, private route: ActivatedRoute, ) { - // this.user = this.userService.getUser(); // Abonniere Router-Events, um den aktiven Link zu ermitteln this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { @@ -115,10 +115,13 @@ export class EditCommercialPropertyListingComponent { this.data = this.router.getCurrentNavigation().extras.state['data']; } }); + this.typesOfCommercialProperty = selectOptions.typesOfCommercialProperty.map(e => { + return { name: e.name, value: parseInt(e.value) }; + }); } async ngOnInit() { if (this.mode === 'edit') { - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id)); + this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); } else { this.listing = createGenericObject(); this.listing.userId = await this.userService.getId(); diff --git a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html deleted file mode 100644 index a063a02..0000000 --- a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html +++ /dev/null @@ -1,216 +0,0 @@ - diff --git a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.scss b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.scss deleted file mode 100644 index 88018d9..0000000 --- a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.scss +++ /dev/null @@ -1,57 +0,0 @@ -.translate-y-5 { - transform: translateY(5px); -} - -.image-container { - display: flex; /* Erlaubt ein flexibles Box-Layout */ - flex-wrap: wrap; /* Erlaubt das Umfließen der Elemente auf die nächste Zeile */ - justify-content: flex-start; /* Startet die Anordnung der Elemente am Anfang des Containers */ - align-items: flex-start; /* Ausrichtung der Elemente am Anfang der Querachse */ - padding: 10px; /* Abstand zwischen den Inhalten des Containers und dessen Rand */ - } - -.image-container span { - flex-flow: row; - display: flex; - width: fit-content; - height: fit-content; -} - -.image-container span img { - max-height: 150px; /* Maximale Höhe der Bilder */ - width: auto; /* Die Breite der Bilder passt sich automatisch an die Höhe an */ - cursor: pointer; - margin: 10px; -} -// .image-container fa-icon { -// top: 0; /* Positioniert das Icon am oberen Rand des Bildes */ -// right: 0; /* Positioniert das Icon am rechten Rand des Bildes */ -// color: #fff; /* Weiße Farbe für das Icon */ -// background-color: rgba(0,0,0,0.5); /* Halbtransparenter Hintergrund für bessere Sichtbarkeit */ -// padding: 5px; /* Ein wenig Platz um das Icon */ -// cursor: pointer; /* Verwandelt den Cursor in eine Hand, um Interaktivität anzudeuten */ -// } - - .image-wrap { - position: relative; /* Ermöglicht die absolute Positionierung des Icons bezogen auf diesen Container */ - display: inline-block; /* Erlaubt die Inline-Anordnung, falls mehrere Bilder vorhanden sind */ - } - - /* Stil für das Bild */ - .image-wrap img { - max-height: 150px; - width: auto; - display: block; /* Verhindert unerwünschten Abstand unter dem Bild */ - } - - /* Stil für das FontAwesome Icon */ - .image-wrap fa-icon { - position: absolute; - top: 15px; /* Positioniert das Icon am oberen Rand des Bildes */ - right: 15px; /* Positioniert das Icon am rechten Rand des Bildes */ - color: #fff; /* Weiße Farbe für das Icon */ - background-color: rgba(0,0,0,0.5); /* Halbtransparenter Hintergrund für bessere Sichtbarkeit */ - padding: 5px; /* Ein wenig Platz um das Icon */ - cursor: pointer; /* Verwandelt den Cursor in eine Hand, um Interaktivität anzudeuten */ - border-radius: 8px; /* Optional: Abrunden der linken unteren Ecke für ästhetische Zwecke */ - } \ No newline at end of file diff --git a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts deleted file mode 100644 index e9a73e6..0000000 --- a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { Component, ViewChild } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; -import { lastValueFrom } from 'rxjs'; -import { ListingsService } from '../../../services/listings.service'; -import { SelectOptionsService } from '../../../services/select-options.service'; -import { createGenericObject } from '../../../utils/utils'; - -import { DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop'; -import { HttpEventType } from '@angular/common/http'; -import { faTrash } from '@fortawesome/free-solid-svg-icons'; -import { AngularCropperjsModule } from 'angular-cropperjs'; -import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { CarouselModule } from 'primeng/carousel'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; -import { DialogModule } from 'primeng/dialog'; -import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog'; -import { EditorModule } from 'primeng/editor'; -import { FileUpload, FileUploadModule } from 'primeng/fileupload'; -import { v4 as uuidv4 } from 'uuid'; -import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { AutoCompleteCompleteEvent, ImageProperty, ListingType } from '../../../../../../bizmatch-server/src/models/main.model'; -import { environment } from '../../../../environments/environment'; -import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component'; -import { InputNumberModule } from '../../../components/inputnumber/inputnumber.component'; -import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe'; -import { GeoService } from '../../../services/geo.service'; -import { ImageService } from '../../../services/image.service'; -import { LoadingService } from '../../../services/loading.service'; -import { UserService } from '../../../services/user.service'; -import { SharedModule } from '../../../shared/shared/shared.module'; -import { TOOLBAR_OPTIONS } from '../../utils/defaults'; -@Component({ - selector: 'create-listing', - standalone: true, - imports: [ - SharedModule, - ArrayToStringPipe, - InputNumberModule, - CarouselModule, - DialogModule, - AngularCropperjsModule, - FileUploadModule, - EditorModule, - DynamicDialogModule, - DragDropModule, - ConfirmDialogModule, - MixedCdkDragDropModule, - ], - providers: [MessageService, DialogService, ConfirmationService], - templateUrl: './edit-listing.component.html', - styleUrl: './edit-listing.component.scss', -}) -export class EditListingComponent { - @ViewChild(FileUpload) public fileUpload: FileUpload; - listingCategory: 'Business' | 'Commercial Property'; - category: string; - location: string; - mode: 'edit' | 'create'; - separator: '\n\n'; - listing: ListingType; - private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined; - user: User; - maxFileSize = 3000000; - uploadUrl: string; - environment = environment; - propertyImages: ImageProperty[]; - responsiveOptions = [ - { - breakpoint: '1199px', - numVisible: 1, - numScroll: 1, - }, - { - breakpoint: '991px', - numVisible: 2, - numScroll: 1, - }, - { - breakpoint: '767px', - numVisible: 1, - numScroll: 1, - }, - ]; - config = { aspectRatio: 16 / 9 }; - editorModules = TOOLBAR_OPTIONS; - dialogRef: DynamicDialogRef | undefined; - draggedImage: ImageProperty; - faTrash = faTrash; - constructor( - public selectOptions: SelectOptionsService, - private router: Router, - private activatedRoute: ActivatedRoute, - private listingsService: ListingsService, - public userService: UserService, - private messageService: MessageService, - private geoService: GeoService, - private imageService: ImageService, - private loadingService: LoadingService, - public dialogService: DialogService, - private confirmationService: ConfirmationService, - ) { - 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 { - const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4(); - sessionStorage.setItem('uuid', uuid); - this.listing = createGenericObject(); - this.listing.id = uuid; - this.listing.userId = this.user.id; - } - this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`; - this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); - } - - async save() { - sessionStorage.removeItem('uuid'); - // await this.listingsService.save(this.listing, this.listing.listingsCategory); - this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 }); - } - - suggestions: string[] | undefined; - - async search(event: AutoCompleteCompleteEvent) { - const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state)); - this.suggestions = result.map(r => r.city).slice(0, 5); - } - - select(event: any) { - const imageUrl = URL.createObjectURL(event.files[0]); - this.dialogRef = this.dialogService.open(ImageCropperComponent, { - data: { - imageUrl: imageUrl, - fileUpload: this.fileUpload, - ratioVariable: false, - }, - header: 'Edit Image', - width: '50vw', - modal: true, - closeOnEscape: true, - keepInViewport: true, - closable: false, - breakpoints: { - '960px': '75vw', - '640px': '90vw', - }, - }); - this.dialogRef.onClose.subscribe(cropper => { - if (cropper) { - this.loadingService.startLoading('uploadImage'); - cropper.getCroppedCanvas().toBlob(async blob => { - this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe( - async event => { - if (event.type === HttpEventType.Response) { - console.log('Upload abgeschlossen', event.body); - this.loadingService.stopLoading('uploadImage'); - this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); - } - }, - error => console.error('Fehler beim Upload:', error), - ); - }, 'image/jpg'); - cropper.destroy(); - } - }); - } - - deleteConfirm(imageName: string) { - this.confirmationService.confirm({ - target: event.target as EventTarget, - message: `Do you want to delete this image ${imageName}?`, - header: 'Delete Confirmation', - icon: 'pi pi-info-circle', - acceptButtonStyleClass: 'p-button-danger p-button-text', - rejectButtonStyleClass: 'p-button-text p-button-text', - acceptIcon: 'none', - rejectIcon: 'none', - - accept: async () => { - await this.imageService.deleteListingImage(this.listing.id, imageName); - this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' }); - this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); - }, - reject: () => { - console.log('deny'); - }, - }); - } - - onDrop(event: { previousIndex: number; currentIndex: number }) { - moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex); - this.listingsService.changeImageOrder(this.listing.id, this.propertyImages); - } -} diff --git a/bizmatch/src/app/pages/subscription/favorites/favorites.component.ts b/bizmatch/src/app/pages/subscription/favorites/favorites.component.ts index e0d3c0e..a7014aa 100644 --- a/bizmatch/src/app/pages/subscription/favorites/favorites.component.ts +++ b/bizmatch/src/app/pages/subscription/favorites/favorites.component.ts @@ -1,30 +1,28 @@ import { Component } from '@angular/core'; -import { MenuAccountComponent } from '../../menu-account/menu-account.component'; -import dataListings from '../../../../assets/data/listings.json'; -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'; import { User } from '../../../../../../bizmatch-server/src/models/db.model'; import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model'; +import { ListingsService } from '../../../services/listings.service'; +import { SelectOptionsService } from '../../../services/select-options.service'; +import { UserService } from '../../../services/user.service'; +import { SharedModule } from '../../../shared/shared/shared.module'; +import { MenuAccountComponent } from '../../menu-account/menu-account.component'; @Component({ selector: 'app-favorites', standalone: true, imports: [MenuAccountComponent, SharedModule], templateUrl: './favorites.component.html', - styleUrl: './favorites.component.scss' + styleUrl: './favorites.component.scss', }) export class FavoritesComponent { user: User; - listings: Array =[]//= dataListings as unknown as Array; - favorites: Array - constructor(public userService: UserService, private listingsService:ListingsService, public selectOptions:SelectOptionsService){ - this.user=this.userService.getUser(); + listings: Array = []; //= dataListings as unknown as Array; + favorites: Array; + constructor(public userService: UserService, private listingsService: ListingsService, public selectOptions: SelectOptionsService) { + this.user = this.userService.getKeycloakUser(); } - async ngOnInit(){ - // this.listings=await lastValueFrom(this.listingsService.getAllListings()); - this.favorites=this.listings.filter(l=>l.favoritesForUser?.includes(this.user.id)); + async ngOnInit() { + // this.listings=await lastValueFrom(this.listingsService.getAllListings()); + this.favorites = this.listings.filter(l => l.favoritesForUser?.includes(this.user.id)); } } diff --git a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts index 2adda0c..2fa42d2 100644 --- a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectorRef, Component } from '@angular/core'; import { ConfirmationService, MessageService } from 'primeng/api'; +import { User } from '../../../../../../bizmatch-server/src/models/db.model'; import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model'; import { ListingsService } from '../../../services/listings.service'; import { SelectOptionsService } from '../../../services/select-options.service'; @@ -18,7 +19,7 @@ import { MenuAccountComponent } from '../../menu-account/menu-account.component' export class MyListingComponent { listings: Array = []; //dataListings as unknown as Array; myListings: Array; - userId: string; + user: User; isBusinessListing = isBusinessListing; isCommercialPropertyListing = isCommercialPropertyListing; constructor( @@ -30,13 +31,15 @@ export class MyListingComponent { private messageService: MessageService, ) {} async ngOnInit() { - this.userId = await this.userService.getId(); - this.myListings = await this.listingsService.getListingByUserId(this.userId); + const keycloakUser = this.userService.getKeycloakUser(); + const email = keycloakUser.email; + this.user = await this.userService.getByMail(email); + this.myListings = await this.listingsService.getListingByUserId(this.user.id); } async deleteListing(listing: ListingType) { await this.listingsService.deleteListing(listing.id, getListingType(listing)); - this.myListings = await this.listingsService.getListingByUserId(this.userId); + this.myListings = await this.listingsService.getListingByUserId(this.user.id); } confirm(event: Event, listing: ListingType) { diff --git a/bizmatch/src/app/services/user.service.ts b/bizmatch/src/app/services/user.service.ts index d683696..17d9e49 100644 --- a/bizmatch/src/app/services/user.service.ts +++ b/bizmatch/src/app/services/user.service.ts @@ -69,7 +69,7 @@ export class UserService { isLoggedIn(): boolean { return this.$isLoggedIn(); } - getUser(): User { + getKeycloakUser(): User { return this.user; } getUserObservable(): Observable { diff --git a/bizmatch/src/app/shared/shared/shared.module.ts b/bizmatch/src/app/shared/shared/shared.module.ts index cac5dbc..0eb3f9d 100644 --- a/bizmatch/src/app/shared/shared/shared.module.ts +++ b/bizmatch/src/app/shared/shared/shared.module.ts @@ -1,35 +1,74 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { AutoCompleteModule } from 'primeng/autocomplete'; import { ButtonModule } from 'primeng/button'; import { CheckboxModule } from 'primeng/checkbox'; -import { InputTextModule } from 'primeng/inputtext'; -import { StyleClassModule } from 'primeng/styleclass'; -import { DropdownModule } from 'primeng/dropdown'; -import { FormsModule } from '@angular/forms'; -import { ToggleButtonModule } from 'primeng/togglebutton'; -import { TagModule } from 'primeng/tag'; -import { ActivatedRoute, RouterModule } from '@angular/router'; -import { InputTextareaModule } from 'primeng/inputtextarea'; import { ChipModule } from 'primeng/chip'; -import { DividerModule } from 'primeng/divider'; -import { TableModule } from 'primeng/table'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { MenuAccountComponent } from '../../pages/menu-account/menu-account.component'; -import { InputNumberModule } from 'primeng/inputnumber'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { ConfirmPopupModule } from 'primeng/confirmpopup'; -import { ToastModule } from 'primeng/toast'; -import { AutoCompleteModule } from 'primeng/autocomplete'; +import { DividerModule } from 'primeng/divider'; +import { DropdownModule } from 'primeng/dropdown'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputNumberModule } from 'primeng/inputnumber'; import { InputSwitchModule } from 'primeng/inputswitch'; - +import { InputTextModule } from 'primeng/inputtext'; +import { InputTextareaModule } from 'primeng/inputtextarea'; +import { StyleClassModule } from 'primeng/styleclass'; +import { TableModule } from 'primeng/table'; +import { TagModule } from 'primeng/tag'; +import { ToastModule } from 'primeng/toast'; +import { MenuAccountComponent } from '../../pages/menu-account/menu-account.component'; @NgModule({ declarations: [], imports: [ - CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule, RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule, AutoCompleteModule,InputSwitchModule + CommonModule, + StyleClassModule, + DividerModule, + ButtonModule, + TableModule, + InputTextModule, + DropdownModule, + FormsModule, + ChipModule, + InputTextareaModule, + RouterModule, + FontAwesomeModule, + MenuAccountComponent, + InputNumberModule, + ConfirmDialogModule, + ConfirmPopupModule, + ToastModule, + CheckboxModule, + AutoCompleteModule, + InputSwitchModule, + InputGroupModule, + ], + exports: [ + CommonModule, + StyleClassModule, + DividerModule, + ButtonModule, + TableModule, + InputTextModule, + DropdownModule, + FormsModule, + ChipModule, + InputTextareaModule, + RouterModule, + FontAwesomeModule, + MenuAccountComponent, + InputNumberModule, + ConfirmDialogModule, + ConfirmPopupModule, + ToastModule, + CheckboxModule, + AutoCompleteModule, + TagModule, + InputSwitchModule, + InputGroupModule, ], - exports:[ - CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule,RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule, AutoCompleteModule, TagModule,InputSwitchModule - ] }) -export class SharedModule { } +export class SharedModule {}