Bug Fixing overall
This commit is contained in:
parent
bb5a408cdc
commit
6b61c19bd7
|
|
@ -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
|
||||||
|
|
@ -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 }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
"singleQuote": true,
|
||||||
"trailingComma": "all"
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"useTabs": false,
|
||||||
|
"vueIndentScriptAndStyle": false
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,8 @@
|
||||||
"Bexar County, TX",
|
"Bexar County, TX",
|
||||||
"Travis County, TX"
|
"Travis County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender": "male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1s2t3u4v-5w6x-7y8z-9a0b-1c2d3e4f5g6h",
|
"id": "1s2t3u4v-5w6x-7y8z-9a0b-1c2d3e4f5g6h",
|
||||||
|
|
@ -55,7 +56,8 @@
|
||||||
"Bastrop County, TX",
|
"Bastrop County, TX",
|
||||||
"Caldwell County, TX"
|
"Caldwell County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7i8j9k0l-1m2n-3o4p-5q6r-7s8t9u0v1w2x",
|
"id": "7i8j9k0l-1m2n-3o4p-5q6r-7s8t9u0v1w2x",
|
||||||
|
|
@ -83,7 +85,8 @@
|
||||||
"Kendall County, TX",
|
"Kendall County, TX",
|
||||||
"Medina County, TX"
|
"Medina County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3y4z5a6b-7c8d-9e0f-1g2h-3i4j5k6l7m8n",
|
"id": "3y4z5a6b-7c8d-9e0f-1g2h-3i4j5k6l7m8n",
|
||||||
|
|
@ -111,7 +114,8 @@
|
||||||
"Brazoria County, TX",
|
"Brazoria County, TX",
|
||||||
"Galveston County, TX"
|
"Galveston County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9o0p1q2r-3s4t-5u6v-7w8x-9y0z1a2b3c4d",
|
"id": "9o0p1q2r-3s4t-5u6v-7w8x-9y0z1a2b3c4d",
|
||||||
|
|
@ -139,7 +143,8 @@
|
||||||
"Denton County, TX",
|
"Denton County, TX",
|
||||||
"Johnson County, TX"
|
"Johnson County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5e6f7g8h-9i0j-1k2l-3m4n-5o6p7q8r9s0t",
|
"id": "5e6f7g8h-9i0j-1k2l-3m4n-5o6p7q8r9s0t",
|
||||||
|
|
@ -167,7 +172,8 @@
|
||||||
"Cherokee County, TX",
|
"Cherokee County, TX",
|
||||||
"Rusk County, TX"
|
"Rusk County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1u2v3w4x-5y6z-7a8b-9c0d-1e2f3g4h5i6j",
|
"id": "1u2v3w4x-5y6z-7a8b-9c0d-1e2f3g4h5i6j",
|
||||||
|
|
@ -195,7 +201,8 @@
|
||||||
"Terry County, TX",
|
"Terry County, TX",
|
||||||
"Lynn County, TX"
|
"Lynn County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7k8l9m0n-1o2p-3q4r-5s6t-7u8v9w0x1y2z",
|
"id": "7k8l9m0n-1o2p-3q4r-5s6t-7u8v9w0x1y2z",
|
||||||
|
|
@ -223,7 +230,8 @@
|
||||||
"Willacy County, TX",
|
"Willacy County, TX",
|
||||||
"Zapata County, TX"
|
"Zapata County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3a4b5c6d-7e8f-9g0h-1i2j-3k4l5m6n7o8p",
|
"id": "3a4b5c6d-7e8f-9g0h-1i2j-3k4l5m6n7o8p",
|
||||||
|
|
@ -251,7 +259,8 @@
|
||||||
"Jeff Davis County, TX",
|
"Jeff Davis County, TX",
|
||||||
"Presidio County, TX"
|
"Presidio County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9q0r1s2t-3u4v-5w6x-7y8z-9a0b1c2d3e4f",
|
"id": "9q0r1s2t-3u4v-5w6x-7y8z-9a0b1c2d3e4f",
|
||||||
|
|
@ -279,7 +288,8 @@
|
||||||
"Bryan County, OK",
|
"Bryan County, OK",
|
||||||
"Marshall County, OK"
|
"Marshall County, OK"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8a2b1c5d-7e6f-4g3h-9i1j-2k3l4m5n6o7p",
|
"id": "8a2b1c5d-7e6f-4g3h-9i1j-2k3l4m5n6o7p",
|
||||||
|
|
@ -311,7 +321,8 @@
|
||||||
"Clark County, NV",
|
"Clark County, NV",
|
||||||
"Washoe County, NV"
|
"Washoe County, NV"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4q5r6s7t-8u9v-0w1x-2y3z-4a5b6c7d8e9f",
|
"id": "4q5r6s7t-8u9v-0w1x-2y3z-4a5b6c7d8e9f",
|
||||||
|
|
@ -339,7 +350,8 @@
|
||||||
"Brevard County, FL",
|
"Brevard County, FL",
|
||||||
"Volusia County, FL"
|
"Volusia County, FL"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1g2h3i4j-5k6l-7m8n-9o0p-1q2r3s4t5u6v",
|
"id": "1g2h3i4j-5k6l-7m8n-9o0p-1q2r3s4t5u6v",
|
||||||
|
|
@ -373,7 +385,8 @@
|
||||||
"Bergen County, NJ",
|
"Bergen County, NJ",
|
||||||
"Essex County, NJ"
|
"Essex County, NJ"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7w8x9y0z-1a2b-3c4d-5e6f-7g8h9i0j1k2l",
|
"id": "7w8x9y0z-1a2b-3c4d-5e6f-7g8h9i0j1k2l",
|
||||||
|
|
@ -401,7 +414,8 @@
|
||||||
"Bexar County, TX",
|
"Bexar County, TX",
|
||||||
"Travis County, TX"
|
"Travis County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3m4n5o6p-7q8r-9s0t-1u2v-3w4x5y6z7a8b",
|
"id": "3m4n5o6p-7q8r-9s0t-1u2v-3w4x5y6z7a8b",
|
||||||
|
|
@ -433,7 +447,8 @@
|
||||||
"Milwaukee County, WI",
|
"Milwaukee County, WI",
|
||||||
"Dane County, WI"
|
"Dane County, WI"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5g6h7i8j-9k0l-1m2n-3o4p-5q6r7s8t9u0v",
|
"id": "5g6h7i8j-9k0l-1m2n-3o4p-5q6r7s8t9u0v",
|
||||||
|
|
@ -465,7 +480,8 @@
|
||||||
"Harris County, TX",
|
"Harris County, TX",
|
||||||
"Dallas County, TX"
|
"Dallas County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1w2x3y4z-5a6b-7c8d-9e0f-1g2h3i4j5k6l",
|
"id": "1w2x3y4z-5a6b-7c8d-9e0f-1g2h3i4j5k6l",
|
||||||
|
|
@ -493,7 +509,8 @@
|
||||||
"Doña Ana County, NM",
|
"Doña Ana County, NM",
|
||||||
"San Juan County, NM"
|
"San Juan County, NM"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7m8n9o0p-1q2r-3s4t-5u6v-7w8x9y0z1a2b",
|
"id": "7m8n9o0p-1q2r-3s4t-5u6v-7w8x9y0z1a2b",
|
||||||
|
|
@ -521,7 +538,8 @@
|
||||||
"Logan County, OK",
|
"Logan County, OK",
|
||||||
"Pottawatomie County, OK"
|
"Pottawatomie County, OK"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3c4d5e6f-7g8h-9i0j-1k2l-3m4n5o6p7q8r",
|
"id": "3c4d5e6f-7g8h-9i0j-1k2l-3m4n5o6p7q8r",
|
||||||
|
|
@ -549,7 +567,8 @@
|
||||||
"Garland County, AR",
|
"Garland County, AR",
|
||||||
"Washington County, AR"
|
"Washington County, AR"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9s0t1u2v-3w4x-5y6z-7a8b-9c0d1e2f3g4h",
|
"id": "9s0t1u2v-3w4x-5y6z-7a8b-9c0d1e2f3g4h",
|
||||||
|
|
@ -577,7 +596,8 @@
|
||||||
"DeSoto County, MS",
|
"DeSoto County, MS",
|
||||||
"Harrison County, MS"
|
"Harrison County, MS"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5i6j7k8l-9m0n-1o2p-3q4r-5s6t7u8v9w0x",
|
"id": "5i6j7k8l-9m0n-1o2p-3q4r-5s6t7u8v9w0x",
|
||||||
|
|
@ -605,7 +625,8 @@
|
||||||
"Yavapai County, AZ",
|
"Yavapai County, AZ",
|
||||||
"Mohave County, AZ"
|
"Mohave County, AZ"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1y2z3a4b-5c6d-7e8f-9g0h-1i2j3k4l5m6n",
|
"id": "1y2z3a4b-5c6d-7e8f-9g0h-1i2j3k4l5m6n",
|
||||||
|
|
@ -633,7 +654,8 @@
|
||||||
"Madison County, AL",
|
"Madison County, AL",
|
||||||
"Mobile County, AL"
|
"Mobile County, AL"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7o8p9q0r-1s2t-3u4v-5w6x-7y8z9a0b1c2d",
|
"id": "7o8p9q0r-1s2t-3u4v-5w6x-7y8z9a0b1c2d",
|
||||||
|
|
@ -661,7 +683,8 @@
|
||||||
"DeKalb County, GA",
|
"DeKalb County, GA",
|
||||||
"Chatham County, GA"
|
"Chatham County, GA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3e4f5g6h-7i8j-9k0l-1m2n-3o4p5q6r7s8t",
|
"id": "3e4f5g6h-7i8j-9k0l-1m2n-3o4p5q6r7s8t",
|
||||||
|
|
@ -693,7 +716,8 @@
|
||||||
"St. Tammany Parish, LA",
|
"St. Tammany Parish, LA",
|
||||||
"Jefferson Parish, LA"
|
"Jefferson Parish, LA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9u0v1w2x-3y4z-5a6b-7c8d-9e0f1g2h3i4j",
|
"id": "9u0v1w2x-3y4z-5a6b-7c8d-9e0f1g2h3i4j",
|
||||||
|
|
@ -721,7 +745,8 @@
|
||||||
"Creek County, OK",
|
"Creek County, OK",
|
||||||
"Osage County, OK"
|
"Osage County, OK"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5k6l7m8n-9o0p-1q2r-3s4t-5u6v7w8x9y0z",
|
"id": "5k6l7m8n-9o0p-1q2r-3s4t-5u6v7w8x9y0z",
|
||||||
|
|
@ -749,7 +774,8 @@
|
||||||
"Brazoria County, TX",
|
"Brazoria County, TX",
|
||||||
"Galveston County, TX"
|
"Galveston County, TX"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p",
|
"id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p",
|
||||||
|
|
@ -777,7 +803,8 @@
|
||||||
"Sierra County, NM",
|
"Sierra County, NM",
|
||||||
"Grant County, NM"
|
"Grant County, NM"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>What We Offer</h3><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h3>What We Offer</h3><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7q8r9s0t-1u2v-3w4x-5y6z-7a8b9c0d1e2f",
|
"id": "7q8r9s0t-1u2v-3w4x-5y6z-7a8b9c0d1e2f",
|
||||||
|
|
@ -805,7 +832,8 @@
|
||||||
"Carroll County, AR",
|
"Carroll County, AR",
|
||||||
"Boone County, AR"
|
"Boone County, AR"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Detailed market analysis</li><li>Dedicated buyer and seller representation</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Detailed market analysis</li><li>Dedicated buyer and seller representation</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3g4h5i6j-7k8l-9m0n-1o2p-3q4r5s6t7u8v",
|
"id": "3g4h5i6j-7k8l-9m0n-1o2p-3q4r5s6t7u8v",
|
||||||
|
|
@ -833,7 +861,8 @@
|
||||||
"Union County, MS",
|
"Union County, MS",
|
||||||
"Tate County, MS"
|
"Tate County, MS"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h4>Our Services Include</h4><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h4>Our Services Include</h4><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9w0x1y2z-3a4b-5c6d-7e8f-9g0h1i2j3k4l",
|
"id": "9w0x1y2z-3a4b-5c6d-7e8f-9g0h1i2j3k4l",
|
||||||
|
|
@ -861,7 +890,8 @@
|
||||||
"Vermilion Parish, LA",
|
"Vermilion Parish, LA",
|
||||||
"St. Landry Parish, LA"
|
"St. Landry Parish, LA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5m6n7o8p-9q0r-1s2t-3u4v-5w6x7y8z9a0b",
|
"id": "5m6n7o8p-9q0r-1s2t-3u4v-5w6x7y8z9a0b",
|
||||||
|
|
@ -889,7 +919,8 @@
|
||||||
"Apache County, AZ",
|
"Apache County, AZ",
|
||||||
"Mohave County, AZ"
|
"Mohave County, AZ"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>Services We Provide</h3><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h3>Services We Provide</h3><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1c2d3e4f-5g6h-7i8j-9k0l-1m2n3o4p5q6r",
|
"id": "1c2d3e4f-5g6h-7i8j-9k0l-1m2n3o4p5q6r",
|
||||||
|
|
@ -917,7 +948,8 @@
|
||||||
"Monroe County, AL",
|
"Monroe County, AL",
|
||||||
"Clarke County, AL"
|
"Clarke County, AL"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Detailed market analysis</li><li>Dedicated buyer and seller representation</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Detailed market analysis</li><li>Dedicated buyer and seller representation</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7s8t9u0v-1w2x-3y4z-5a6b-7c8d9e0f1g2h",
|
"id": "7s8t9u0v-1w2x-3y4z-5a6b-7c8d9e0f1g2h",
|
||||||
|
|
@ -945,7 +977,8 @@
|
||||||
"McIntosh County, GA",
|
"McIntosh County, GA",
|
||||||
"Bryan County, GA"
|
"Bryan County, GA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h2>What We Offer</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
"offeredServices": "<h2>What We Offer</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3i4j5k6l-7m8n-9o0p-1q2r-3s4t5u6v7w8x",
|
"id": "3i4j5k6l-7m8n-9o0p-1q2r-3s4t5u6v7w8x",
|
||||||
|
|
@ -973,7 +1006,8 @@
|
||||||
"McClain County, OK",
|
"McClain County, OK",
|
||||||
"Pottawatomie County, OK"
|
"Pottawatomie County, OK"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Thorough buyer screening and qualification</li><li>Skilled negotiations and deal structuring</li><li>Detailed due diligence coordination</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9y0z1a2b-3c4d-5e6f-7g8h-9i0j1k2l3m4n",
|
"id": "9y0z1a2b-3c4d-5e6f-7g8h-9i0j1k2l3m4n",
|
||||||
|
|
@ -1001,7 +1035,8 @@
|
||||||
"Los Alamos County, NM",
|
"Los Alamos County, NM",
|
||||||
"Sandoval County, NM"
|
"Sandoval County, NM"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h4>Services We Provide</h4><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
"offeredServices": "<h4>Services We Provide</h4><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5s6t7u8v-9w0x-1y2z-3a4b-5c6d7e8f9g0h",
|
"id": "5s6t7u8v-9w0x-1y2z-3a4b-5c6d7e8f9g0h",
|
||||||
|
|
@ -1033,7 +1068,8 @@
|
||||||
"Clark County, NV",
|
"Clark County, NV",
|
||||||
"Washoe County, NV"
|
"Washoe County, NV"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>Our Services</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h3>Our Services</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1i2j3k4l-5m6n-7o8p-9q0r-1s2t3u4v5w6x",
|
"id": "1i2j3k4l-5m6n-7o8p-9q0r-1s2t3u4v5w6x",
|
||||||
|
|
@ -1065,7 +1101,8 @@
|
||||||
"Orleans Parish, LA",
|
"Orleans Parish, LA",
|
||||||
"Jefferson Parish, LA"
|
"Jefferson Parish, LA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7y8z9a0b-1c2d-3e4f-5g6h-7i8j9k0l1m2n",
|
"id": "7y8z9a0b-1c2d-3e4f-5g6h-7i8j9k0l1m2n",
|
||||||
|
|
@ -1097,7 +1134,8 @@
|
||||||
"Fulton County, GA",
|
"Fulton County, GA",
|
||||||
"Gwinnett County, GA"
|
"Gwinnett County, GA"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h4>What We Offer</h4><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h4>What We Offer</h4><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3o4p5q6r-7s8t-9u0v-1w2x-3y4z5a6b7c8d",
|
"id": "3o4p5q6r-7s8t-9u0v-1w2x-3y4z5a6b7c8d",
|
||||||
|
|
@ -1129,7 +1167,8 @@
|
||||||
"Hudson County, NJ",
|
"Hudson County, NJ",
|
||||||
"Bergen County, NJ"
|
"Bergen County, NJ"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9e0f1g2h-3i4j-5k6l-7m8n-9o0p1q2r3s4t",
|
"id": "9e0f1g2h-3i4j-5k6l-7m8n-9o0p1q2r3s4t",
|
||||||
|
|
@ -1161,7 +1200,8 @@
|
||||||
"Milwaukee County, WI",
|
"Milwaukee County, WI",
|
||||||
"Dane County, WI"
|
"Dane County, WI"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>Services We Provide</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h3>Services We Provide</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5u6v7w8x-9y0z-1a2b-3c4d-5e6f7g8h9i0j",
|
"id": "5u6v7w8x-9y0z-1a2b-3c4d-5e6f7g8h9i0j",
|
||||||
|
|
@ -1193,7 +1233,8 @@
|
||||||
"Multnomah County, OR",
|
"Multnomah County, OR",
|
||||||
"Washington County, OR"
|
"Washington County, OR"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>Our Services</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h3>Our Services</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1k2l3m4n-5o6p-7q8r-9s0t-1u2v3w4x5y6z",
|
"id": "1k2l3m4n-5o6p-7q8r-9s0t-1u2v3w4x5y6z",
|
||||||
|
|
@ -1225,7 +1266,8 @@
|
||||||
"Doña Ana County, NM",
|
"Doña Ana County, NM",
|
||||||
"Santa Fe County, NM"
|
"Santa Fe County, NM"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7a8b9c0d-1e2f-3g4h-5i6j-7k8l9m0n1o2p",
|
"id": "7a8b9c0d-1e2f-3g4h-5i6j-7k8l9m0n1o2p",
|
||||||
|
|
@ -1257,7 +1299,8 @@
|
||||||
"Salt Lake County, UT",
|
"Salt Lake County, UT",
|
||||||
"Utah County, UT"
|
"Utah County, UT"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h4>What We Offer</h4><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h4>What We Offer</h4><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"male"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3q4r5s6t-7u8v-9w0x-1y2z-3a4b5c6d7e8f",
|
"id": "3q4r5s6t-7u8v-9w0x-1y2z-3a4b5c6d7e8f",
|
||||||
|
|
@ -1289,7 +1332,8 @@
|
||||||
"Cuyahoga County, OH",
|
"Cuyahoga County, OH",
|
||||||
"Franklin County, OH"
|
"Franklin County, OH"
|
||||||
],
|
],
|
||||||
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>"
|
"offeredServices": "<ul><li>Comprehensive business valuation services</li><li>Targeted marketing and advertising campaigns</li><li>Extensive buyer database</li><li>Thorough due diligence assistance</li><li>Skilled negotiations and closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9g0h1i2j-3k4l-5m6n-7o8p-9q0r1s2t3u4v",
|
"id": "9g0h1i2j-3k4l-5m6n-7o8p-9q0r1s2t3u4v",
|
||||||
|
|
@ -1321,6 +1365,7 @@
|
||||||
"Greenville County, SC",
|
"Greenville County, SC",
|
||||||
"Charleston County, SC"
|
"Charleston County, SC"
|
||||||
],
|
],
|
||||||
"offeredServices": "<h3>Services We Provide</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>"
|
"offeredServices": "<h3>Services We Provide</h3><ul><li>Comprehensive business valuation</li><li>Targeted marketing and advertising</li><li>Extensive buyer network</li><li>Thorough due diligence coordination</li><li>Skilled negotiations and deal structuring</li><li>Professional closing support</li></ul>",
|
||||||
|
"gender":"female"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -4,7 +4,10 @@
|
||||||
"sourceRoot": "src",
|
"sourceRoot": "src",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"deleteOutDir": true,
|
"deleteOutDir": true,
|
||||||
"assets": ["assets/**/*","**/*.hbs"],
|
"assets": [
|
||||||
|
"assets/**/*",
|
||||||
|
"**/*.hbs"
|
||||||
|
],
|
||||||
"watchAssets": true
|
"watchAssets": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +83,7 @@
|
||||||
"kysely-codegen": "^0.15.0",
|
"kysely-codegen": "^0.15.0",
|
||||||
"pg-to-ts": "^4.1.1",
|
"pg-to-ts": "^4.1.1",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
|
"rimraf": "^5.0.5",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
|
import { existsSync, readFileSync, readdirSync, statSync, unlinkSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
import pkg from 'pg';
|
import pkg from 'pg';
|
||||||
const { Pool } = pkg;
|
import { rimraf } from 'rimraf';
|
||||||
import * as schema from './schema.js';
|
import sharp from 'sharp';
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from 'src/models/db.model.js';
|
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 pool = new Pool({connectionString})
|
||||||
const client = new Pool({ connectionString })
|
const client = new Pool({ connectionString });
|
||||||
const db = drizzle(client, { schema, logger: true });
|
const db = drizzle(client, { schema, logger: true });
|
||||||
|
|
||||||
//Delete Content
|
//Delete Content
|
||||||
|
|
@ -18,44 +20,66 @@ await db.delete(schema.businesses);
|
||||||
await db.delete(schema.users);
|
await db.delete(schema.users);
|
||||||
|
|
||||||
//Broker
|
//Broker
|
||||||
let filePath = `./data/broker.json`
|
let filePath = `./data/broker.json`;
|
||||||
let data: string = readFileSync(filePath, 'utf8');
|
let data: string = readFileSync(filePath, 'utf8');
|
||||||
const userData: User[] = JSON.parse(data); // Erwartet ein Array von Objekten
|
const userData: User[] = JSON.parse(data); // Erwartet ein Array von Objekten
|
||||||
const generatedUserData = []
|
const generatedUserData = [];
|
||||||
console.log(userData.length)
|
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) {
|
for (const user of userData) {
|
||||||
delete user.id
|
delete user.id;
|
||||||
user.licensedIn=user.licensedIn.map(l=>`${l['name']}|${l['value']}`)
|
user.licensedIn = user.licensedIn.map(l => `${l['name']}|${l['value']}`);
|
||||||
const u = await db.insert(schema.users).values(user).returning({ insertedId: schema.users.id });
|
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);
|
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
|
//Business Listings
|
||||||
filePath = `./data/businesses.json`
|
filePath = `./data/businesses.json`;
|
||||||
data = readFileSync(filePath, 'utf8');
|
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) {
|
for (const business of businessJsonData) {
|
||||||
delete business.id
|
delete business.id;
|
||||||
business.created = new Date(business.created)
|
business.created = new Date(business.created);
|
||||||
business.userId = getRandomItem(generatedUserData);
|
business.userId = getRandomItem(generatedUserData);
|
||||||
await db.insert(schema.businesses).values(business);
|
await db.insert(schema.businesses).values(business);
|
||||||
}
|
}
|
||||||
//Corporate Listings
|
//Corporate Listings
|
||||||
filePath = `./data/commercials.json`
|
filePath = `./data/commercials.json`;
|
||||||
data = readFileSync(filePath, 'utf8');
|
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) {
|
for (const commercial of commercialJsonData) {
|
||||||
const id = commercial.id;
|
const id = commercial.id;
|
||||||
delete commercial.id
|
delete commercial.id;
|
||||||
commercial.imageOrder=['1.jpg'];
|
|
||||||
commercial.imagePath=id
|
commercial.imageOrder = getFilenames(id);
|
||||||
|
commercial.imagePath = id;
|
||||||
commercial.created = getRandomDateWithinLastYear();
|
commercial.created = getRandomDateWithinLastYear();
|
||||||
commercial.userId = getRandomItem(generatedUserData);
|
commercial.userId = getRandomItem(generatedUserData);
|
||||||
await db.insert(schema.commercials).values(commercial);
|
await db.insert(schema.commercials).values(commercial);
|
||||||
}
|
}
|
||||||
|
|
||||||
//End
|
//End
|
||||||
await client.end()
|
await client.end();
|
||||||
|
|
||||||
function getRandomItem<T>(arr: T[]): T {
|
function getRandomItem<T>(arr: T[]): T {
|
||||||
if (arr.length === 0) {
|
if (arr.length === 0) {
|
||||||
|
|
@ -65,6 +89,14 @@ function getRandomItem<T>(arr: T[]): T {
|
||||||
const randomIndex = Math.floor(Math.random() * arr.length);
|
const randomIndex = Math.floor(Math.random() * arr.length);
|
||||||
return arr[randomIndex];
|
return arr[randomIndex];
|
||||||
}
|
}
|
||||||
|
function getFilenames(id: string): string[] {
|
||||||
|
try {
|
||||||
|
let filePath = `./pictures/property/${id}`;
|
||||||
|
return readdirSync(filePath);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
function getRandomDateWithinLastYear(): Date {
|
function getRandomDateWithinLastYear(): Date {
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
const lastYear = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), currentDate.getDate());
|
const lastYear = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), currentDate.getDate());
|
||||||
|
|
@ -75,3 +107,47 @@ function getRandomDateWithinLastYear(): Date {
|
||||||
|
|
||||||
return randomDate;
|
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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE "commercials" ALTER COLUMN "imageOrder" SET DATA TYPE varchar(200)[];
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,20 @@
|
||||||
"when": 1714913766996,
|
"when": 1714913766996,
|
||||||
"tag": "0000_third_spacker_dave",
|
"tag": "0000_third_spacker_dave",
|
||||||
"breakpoints": true
|
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -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';
|
import { InferInsertModel, InferModel, InferModelFromColumns, InferSelectModel, relations, sql } from 'drizzle-orm';
|
||||||
|
|
||||||
export const PG_CONNECTION = 'PG_CONNECTION';
|
export const PG_CONNECTION = 'PG_CONNECTION';
|
||||||
|
export const genderEnum = pgEnum('gender', ['male','female']);
|
||||||
export const users = pgTable('users', {
|
export const users = pgTable('users', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
firstname: varchar('firstname', { length: 255 }).notNull(),
|
firstname: varchar('firstname', { length: 255 }).notNull(),
|
||||||
|
|
@ -19,6 +19,7 @@ export const users = pgTable('users', {
|
||||||
hasProfile: boolean('hasProfile'),
|
hasProfile: boolean('hasProfile'),
|
||||||
hasCompanyLogo: boolean('hasCompanyLogo'),
|
hasCompanyLogo: boolean('hasCompanyLogo'),
|
||||||
licensedIn:varchar('licensedIn', { length: 50 }).array(),
|
licensedIn:varchar('licensedIn', { length: 50 }).array(),
|
||||||
|
gender: genderEnum('gender'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const businesses = pgTable('businesses', {
|
export const businesses = pgTable('businesses', {
|
||||||
|
|
@ -68,7 +69,7 @@ export const commercials = pgTable('commercials', {
|
||||||
email: varchar('email', { length: 255 }),
|
email: varchar('email', { length: 255 }),
|
||||||
website: varchar('website', { length: 255 }),
|
website: varchar('website', { length: 255 }),
|
||||||
phoneNumber: varchar('phoneNumber', { length: 255 }),
|
phoneNumber: varchar('phoneNumber', { length: 255 }),
|
||||||
imageOrder:varchar('imageOrder',{length:30}).array(),
|
imageOrder:varchar('imageOrder',{length:200}).array(),
|
||||||
imagePath:varchar('imagePath',{length:50}),
|
imagePath:varchar('imagePath',{length:50}),
|
||||||
created: timestamp('created'),
|
created: timestamp('created'),
|
||||||
updated: timestamp('updated'),
|
updated: timestamp('updated'),
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,38 @@
|
||||||
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 { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
import { FileInterceptor } from '@nestjs/platform-express';
|
|
||||||
import { FileService } from '../file/file.service.js';
|
import { FileService } from '../file/file.service.js';
|
||||||
import { SelectOptionsService } from '../select-options/select-options.service.js';
|
|
||||||
import { ListingsService } from '../listings/listings.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 { commercials } from 'src/drizzle/schema.js';
|
||||||
import { businesses, commercials } from 'src/drizzle/schema.js';
|
|
||||||
import { CommercialPropertyListing } from 'src/models/db.model.js';
|
import { CommercialPropertyListing } from 'src/models/db.model.js';
|
||||||
|
|
||||||
@Controller('image')
|
@Controller('image')
|
||||||
export class ImageController {
|
export class ImageController {
|
||||||
|
constructor(
|
||||||
constructor(private fileService:FileService,
|
private fileService: FileService,
|
||||||
private listingService: ListingsService,
|
private listingService: ListingsService,
|
||||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||||
private selectOptions:SelectOptionsService) {
|
private selectOptions: SelectOptionsService,
|
||||||
}
|
) {}
|
||||||
|
|
||||||
@Post('uploadPropertyPicture/:id')
|
@Post('uploadPropertyPicture/:id')
|
||||||
@UseInterceptors(FileInterceptor('file'),)
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
||||||
const imagename = await this.fileService.storePropertyPicture(file, id);
|
const imagename = await this.fileService.storePropertyPicture(file, id);
|
||||||
// await this.listingService.addImage(id,imagename);
|
await this.listingService.addImage(id, imagename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('uploadProfile/:id')
|
@Post('uploadProfile/:id')
|
||||||
@UseInterceptors(FileInterceptor('file'),)
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async uploadProfile(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
async uploadProfile(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
||||||
await this.fileService.storeProfilePicture(file, id);
|
await this.fileService.storeProfilePicture(file, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('uploadCompanyLogo/:id')
|
@Post('uploadCompanyLogo/:id')
|
||||||
@UseInterceptors(FileInterceptor('file'),)
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
||||||
await this.fileService.storeCompanyLogo(file, id);
|
await this.fileService.storeCompanyLogo(file, id);
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +42,7 @@ export class ImageController {
|
||||||
const result = await this.listingService.findById(id, commercials);
|
const result = await this.listingService.findById(id, commercials);
|
||||||
const listing = result as CommercialPropertyListing;
|
const listing = result as CommercialPropertyListing;
|
||||||
if (listing.imageOrder) {
|
if (listing.imageOrder) {
|
||||||
return listing.imageOrder
|
return listing.imageOrder;
|
||||||
} else {
|
} else {
|
||||||
const imageOrder = await this.fileService.getPropertyImages(id);
|
const imageOrder = await this.fileService.getPropertyImages(id);
|
||||||
listing.imageOrder = imageOrder;
|
listing.imageOrder = imageOrder;
|
||||||
|
|
@ -63,14 +62,13 @@ export class ImageController {
|
||||||
@Delete('propertyPicture/:listingid/:imagename')
|
@Delete('propertyPicture/:listingid/:imagename')
|
||||||
async deletePropertyImagesById(@Param('listingid') listingid: string, @Param('imagename') imagename: string): Promise<any> {
|
async deletePropertyImagesById(@Param('listingid') listingid: string, @Param('imagename') imagename: string): Promise<any> {
|
||||||
this.fileService.deleteImage(`pictures/property/${listingid}/${imagename}`);
|
this.fileService.deleteImage(`pictures/property/${listingid}/${imagename}`);
|
||||||
// await this.listingService.deleteImage(listingid,imagename);
|
|
||||||
}
|
}
|
||||||
@Delete('logo/:userid/')
|
@Delete('logo/:userid/')
|
||||||
async deleteLogoImagesById(@Param('id') id: string): Promise<any> {
|
async deleteLogoImagesById(@Param('id') id: string): Promise<any> {
|
||||||
this.fileService.deleteImage(`pictures/property//${id}`)
|
this.fileService.deleteImage(`pictures/property//${id}`);
|
||||||
}
|
}
|
||||||
@Delete('profile/:userid/')
|
@Delete('profile/:userid/')
|
||||||
async deleteProfileImagesById(@Param('id') id: string): Promise<any> {
|
async deleteProfileImagesById(@Param('id') id: string): Promise<any> {
|
||||||
this.fileService.deleteImage(`pictures/property//${id}`)
|
this.fileService.deleteImage(`pictures/property//${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nestjs/common';
|
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 { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
import { ListingCriteria } from '../models/main.model.js';
|
|
||||||
import { businesses } from '../drizzle/schema.js';
|
import { businesses } from '../drizzle/schema.js';
|
||||||
|
import { ListingCriteria } from '../models/main.model.js';
|
||||||
|
import { ListingsService } from './listings.service.js';
|
||||||
|
|
||||||
@Controller('listings/business')
|
@Controller('listings/business')
|
||||||
export class BusinessListingsController {
|
export class BusinessListingsController {
|
||||||
|
constructor(
|
||||||
constructor(private readonly listingsService:ListingsService,
|
private readonly listingsService: ListingsService,
|
||||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||||
}
|
) {}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
findById(@Param('id') id: string): any {
|
findById(@Param('id') id: string): any {
|
||||||
|
|
@ -31,20 +29,19 @@ export class BusinessListingsController {
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() listing: any) {
|
create(@Body() listing: any) {
|
||||||
this.logger.info(`Save Listing`);
|
this.logger.info(`Save Listing`);
|
||||||
this.listingsService.createListing(listing,businesses)
|
return this.listingsService.createListing(listing, businesses);
|
||||||
}
|
}
|
||||||
@Put()
|
@Put()
|
||||||
update(@Body() listing: any) {
|
update(@Body() listing: any) {
|
||||||
this.logger.info(`Save Listing`);
|
this.logger.info(`Save Listing`);
|
||||||
this.listingsService.updateListing(listing.id,listing,businesses)
|
return this.listingsService.updateListing(listing.id, listing, businesses);
|
||||||
}
|
}
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
deleteById(@Param('id') id: string) {
|
deleteById(@Param('id') id: string) {
|
||||||
this.listingsService.deleteListing(id,businesses)
|
this.listingsService.deleteListing(id, businesses);
|
||||||
}
|
}
|
||||||
@Get('states/all')
|
@Get('states/all')
|
||||||
getStates(): any {
|
getStates(): any {
|
||||||
return this.listingsService.getStates(businesses);
|
return this.listingsService.getStates(businesses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,19 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import {
|
import { and, eq, gte, ilike, lte, sql } from 'drizzle-orm';
|
||||||
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 { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||||
import * as schema from '../drizzle/schema.js';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { PgTableFn, PgTableWithColumns, QueryBuilder } from 'drizzle-orm/pg-core';
|
|
||||||
import { BusinessListing, CommercialPropertyListing } from 'src/models/db.model.js';
|
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()
|
@Injectable()
|
||||||
export class ListingsService {
|
export class ListingsService {
|
||||||
|
constructor(
|
||||||
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||||
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,) {
|
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
|
||||||
}
|
) {}
|
||||||
private getConditions(criteria: ListingCriteria, table: typeof businesses | typeof commercials): any[] {
|
private getConditions(criteria: ListingCriteria, table: typeof businesses | typeof commercials): any[] {
|
||||||
const conditions = [];
|
const conditions = [];
|
||||||
if (criteria.type) {
|
if (criteria.type) {
|
||||||
|
|
@ -51,36 +42,49 @@ export class ListingsService {
|
||||||
async findListingsByCriteria(criteria: ListingCriteria, table: typeof businesses | typeof commercials): Promise<{ data: Record<string, any>[]; total: number }> {
|
async findListingsByCriteria(criteria: ListingCriteria, table: typeof businesses | typeof commercials): Promise<{ data: Record<string, any>[]; total: number }> {
|
||||||
const start = criteria.start ? criteria.start : 0;
|
const start = criteria.start ? criteria.start : 0;
|
||||||
const length = criteria.length ? criteria.length : 12;
|
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<any> {
|
private async findListings(table: typeof businesses | typeof commercials, criteria: ListingCriteria, start = 0, length = 12): Promise<any> {
|
||||||
const conditions = this.getConditions(criteria,table)
|
const conditions = this.getConditions(criteria, table);
|
||||||
const [data, total] = await Promise.all([
|
const [data, total] = await Promise.all([
|
||||||
this.conn.select().from(table).where(and(...conditions)).offset(start).limit(length),
|
this.conn
|
||||||
this.conn.select({ count: sql`count(*)` }).from(table).where(and(...conditions)).then((result) => Number(result[0].count)),
|
.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 };
|
return { total, data };
|
||||||
}
|
}
|
||||||
async findById(id: string, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
async findById(id: string, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||||
const result = await this.conn.select().from(table).where(sql`${table.id} = ${id}`)
|
const result = await this.conn
|
||||||
return result[0] as BusinessListing | CommercialPropertyListing
|
.select()
|
||||||
|
.from(table)
|
||||||
|
.where(sql`${table.id} = ${id}`);
|
||||||
|
return result[0] as BusinessListing | CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByUserId(userId: string, table: typeof businesses | typeof commercials): Promise<BusinessListing[] | CommercialPropertyListing[]> {
|
async findByUserId(userId: string, table: typeof businesses | typeof commercials): Promise<BusinessListing[] | CommercialPropertyListing[]> {
|
||||||
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<BusinessListing | CommercialPropertyListing> {
|
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||||
data.created=new Date()
|
data.created = new Date();
|
||||||
data.updated=new Date()
|
data.updated = new Date();
|
||||||
data.visits = 0;
|
data.visits = 0;
|
||||||
data.lastVisit=null
|
data.lastVisit = null;
|
||||||
const [createdListing] = await this.conn.insert(table).values(data).returning();
|
const [createdListing] = await this.conn.insert(table).values(data).returning();
|
||||||
return createdListing as BusinessListing | CommercialPropertyListing;
|
return createdListing as BusinessListing | CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||||
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();
|
const [updateListing] = await this.conn.update(table).set(data).where(eq(table.id, id)).returning();
|
||||||
return updateListing as BusinessListing | CommercialPropertyListing;
|
return updateListing as BusinessListing | CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
|
|
@ -89,29 +93,33 @@ export class ListingsService {
|
||||||
await this.conn.delete(table).where(eq(table.id, id));
|
await this.conn.delete(table).where(eq(table.id, id));
|
||||||
}
|
}
|
||||||
async getStates(table: typeof businesses | typeof commercials): Promise<any[]> {
|
async getStates(table: typeof businesses | typeof commercials): Promise<any[]> {
|
||||||
return await this.conn.select({state: table.state,count: sql<number>`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<number>`count(${table.id})`.mapWith(Number) })
|
||||||
|
.from(table)
|
||||||
|
.groupBy(sql`${table.state}`)
|
||||||
|
.orderBy(sql`count desc`);
|
||||||
}
|
}
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
// Images for commercial Properties
|
// Images for commercial Properties
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
|
|
||||||
async updateImageOrder(id: string, imageOrder: string[]) {
|
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;
|
listing.imageOrder = imageOrder;
|
||||||
await this.updateListing(listing.id, listing, commercials)
|
await this.updateListing(listing.id, listing, commercials);
|
||||||
}
|
}
|
||||||
async deleteImage(id: string, name: string,) {
|
async deleteImage(id: string, name: string) {
|
||||||
const listing = await this.findById(id, commercials) as unknown as CommercialPropertyListing
|
const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing;
|
||||||
const index = listing.imageOrder.findIndex(im => im === name);
|
const index = listing.imageOrder.findIndex(im => im === name);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
listing.imageOrder.splice(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) {
|
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);
|
listing.imageOrder.push(imagename);
|
||||||
await this.updateListing(listing.id, listing, commercials)
|
listing.imagePath = listing.id;
|
||||||
|
await this.updateListing(listing.id, listing, commercials);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { MailService } from './mail.service.js';
|
||||||
import { KeycloakUser, MailInfo } from 'src/models/main.model.js';
|
|
||||||
|
|
||||||
|
|
||||||
@Controller('mail')
|
@Controller('mail')
|
||||||
export class MailController {
|
export class MailController {
|
||||||
constructor(private mailService:MailService){
|
constructor(private mailService: MailService) {}
|
||||||
|
|
||||||
}
|
|
||||||
@Post()
|
@Post()
|
||||||
sendEMail(@Body() mailInfo: MailInfo): Promise< KeycloakUser> {
|
sendEMail(@Body() mailInfo: MailInfo): Promise<User> {
|
||||||
return this.mailService.sendInquiry(mailInfo);
|
return this.mailService.sendInquiry(mailInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { MailService } from './mail.service.js';
|
|
||||||
import { MailController } from './mail.controller.js';
|
|
||||||
import { MailerModule } from '@nestjs-modules/mailer';
|
import { MailerModule } from '@nestjs-modules/mailer';
|
||||||
import path, { join } from 'path';
|
|
||||||
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter.js';
|
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 { 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 __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AuthModule,
|
imports: [
|
||||||
|
DrizzleModule,
|
||||||
|
UserModule,
|
||||||
MailerModule.forRoot({
|
MailerModule.forRoot({
|
||||||
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
|
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
|
||||||
// or
|
// or
|
||||||
|
|
@ -38,7 +43,7 @@ const __dirname = path.dirname(__filename);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
providers: [MailService],
|
providers: [MailService, UserService, FileService],
|
||||||
controllers: [MailController]
|
controllers: [MailController],
|
||||||
})
|
})
|
||||||
export class MailModule {}
|
export class MailModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,41 @@
|
||||||
import { MailerService } from '@nestjs-modules/mailer';
|
import { MailerService } from '@nestjs-modules/mailer';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { AuthService } from '../auth/auth.service.js';
|
import path, { join } from 'path';
|
||||||
import { KeycloakUser, MailInfo } from '../models/main.model.js';
|
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()
|
@Injectable()
|
||||||
export class MailService {
|
export class MailService {
|
||||||
constructor(private mailerService: MailerService, private authService:AuthService) {}
|
constructor(
|
||||||
|
private mailerService: MailerService,
|
||||||
|
private userService: UserService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async sendInquiry(mailInfo: MailInfo):Promise<KeycloakUser> {
|
async sendInquiry(mailInfo: MailInfo): Promise<User> {
|
||||||
const user = await this.authService.getUser(mailInfo.userId) as KeycloakUser;
|
//const user = await this.authService.getUser(mailInfo.userId) as KeycloakUser;
|
||||||
|
const user = await this.userService.getUserByMail(mailInfo.email);
|
||||||
console.log(JSON.stringify(user));
|
console.log(JSON.stringify(user));
|
||||||
await this.mailerService.sendMail({
|
await this.mailerService.sendMail({
|
||||||
to: user.email,
|
to: user.email,
|
||||||
from: '"Bizmatch Team" <info@bizmatch.net>', // override default from
|
from: '"Bizmatch Team" <info@bizmatch.net>', // override default from
|
||||||
subject: `Inquiry from ${mailInfo.sender.name}`,
|
subject: `Inquiry from ${mailInfo.sender.name}`,
|
||||||
template: './inquiry', // `.hbs` extension is appended automatically
|
//template: './inquiry', // `.hbs` extension is appended automatically
|
||||||
context: { // ✏️ filling curly brackets with content
|
template: join(__dirname, '../..', 'mail/templates/inquiry.hbs'),
|
||||||
name: user.firstName,
|
context: {
|
||||||
inquiry:mailInfo.sender.comments
|
// ✏️ 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
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id?: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ export interface ListingCriteria {
|
||||||
realEstateChecked: boolean;
|
realEstateChecked: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
category: 'professional|broker';
|
category: 'professional|broker';
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KeycloakUser {
|
export interface KeycloakUser {
|
||||||
|
|
@ -148,6 +149,8 @@ export interface AutoCompleteCompleteEvent {
|
||||||
export interface MailInfo {
|
export interface MailInfo {
|
||||||
sender: Sender;
|
sender: Sender;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
email: string;
|
||||||
|
listing?: BusinessListing;
|
||||||
}
|
}
|
||||||
export interface Sender {
|
export interface Sender {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { ImageType, KeyValue, KeyValueStyle } from '../models/main.model.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SelectOptionsService {
|
export class SelectOptionsService {
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
public typesOfBusiness: Array<KeyValueStyle> = [
|
public typesOfBusiness: Array<KeyValueStyle> = [
|
||||||
{ name: 'Automotive', value: '1', icon: 'fa-solid fa-car', bgColorClass: 'bg-green-100', textColorClass: 'text-green-600' },
|
{ name: 'Automotive', value: '1', icon: 'fa-solid fa-car', bgColorClass: 'bg-green-100', textColorClass: 'text-green-600' },
|
||||||
|
|
@ -39,20 +38,19 @@ export class SelectOptionsService {
|
||||||
public listingCategories: Array<KeyValue> = [
|
public listingCategories: Array<KeyValue> = [
|
||||||
{ name: 'Business', value: 'business' },
|
{ name: 'Business', value: 'business' },
|
||||||
{ name: 'Commercial Property', value: 'commercialProperty' },
|
{ name: 'Commercial Property', value: 'commercialProperty' },
|
||||||
]
|
];
|
||||||
public categories: Array<KeyValueStyle> = [
|
public categories: Array<KeyValueStyle> = [
|
||||||
{ name: 'Broker', value: 'broker', icon: 'pi-image', bgColorClass: 'bg-green-100', textColorClass: 'text-green-600' },
|
{ 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' },
|
{ name: 'Professional', value: 'professional', icon: 'pi-globe', bgColorClass: 'bg-yellow-100', textColorClass: 'text-yellow-600' },
|
||||||
]
|
];
|
||||||
public imageTypes: ImageType[] = [
|
public imageTypes: ImageType[] = [
|
||||||
{ name: 'propertyPicture', upload: 'uploadPropertyPicture', delete: 'propertyPicture' },
|
{ name: 'propertyPicture', upload: 'uploadPropertyPicture', delete: 'propertyPicture' },
|
||||||
{ name: 'companyLogo', upload: 'uploadCompanyLogo', delete: 'logo' },
|
{ name: 'companyLogo', upload: 'uploadCompanyLogo', delete: 'logo' },
|
||||||
{ name: 'profile', upload: 'uploadProfile', delete: 'profile' },
|
{ name: 'profile', upload: 'uploadProfile', delete: 'profile' },
|
||||||
]
|
];
|
||||||
private usStates = [
|
private usStates = [
|
||||||
{ name: 'ALABAMA', abbreviation: 'AL' },
|
{ name: 'ALABAMA', abbreviation: 'AL' },
|
||||||
{ name: 'ALASKA', abbreviation: 'AK' },
|
{ name: 'ALASKA', abbreviation: 'AK' },
|
||||||
{ name: 'AMERICAN SAMOA', abbreviation: 'AS'},
|
|
||||||
{ name: 'ARIZONA', abbreviation: 'AZ' },
|
{ name: 'ARIZONA', abbreviation: 'AZ' },
|
||||||
{ name: 'ARKANSAS', abbreviation: 'AR' },
|
{ name: 'ARKANSAS', abbreviation: 'AR' },
|
||||||
{ name: 'CALIFORNIA', abbreviation: 'CA' },
|
{ name: 'CALIFORNIA', abbreviation: 'CA' },
|
||||||
|
|
@ -60,7 +58,6 @@ export class SelectOptionsService {
|
||||||
{ name: 'CONNECTICUT', abbreviation: 'CT' },
|
{ name: 'CONNECTICUT', abbreviation: 'CT' },
|
||||||
{ name: 'DELAWARE', abbreviation: 'DE' },
|
{ name: 'DELAWARE', abbreviation: 'DE' },
|
||||||
{ name: 'DISTRICT OF COLUMBIA', abbreviation: 'DC' },
|
{ name: 'DISTRICT OF COLUMBIA', abbreviation: 'DC' },
|
||||||
{ name: 'FEDERATED STATES OF MICRONESIA', abbreviation: 'FM'},
|
|
||||||
{ name: 'FLORIDA', abbreviation: 'FL' },
|
{ name: 'FLORIDA', abbreviation: 'FL' },
|
||||||
{ name: 'GEORGIA', abbreviation: 'GA' },
|
{ name: 'GEORGIA', abbreviation: 'GA' },
|
||||||
{ name: 'GUAM', abbreviation: 'GU' },
|
{ name: 'GUAM', abbreviation: 'GU' },
|
||||||
|
|
@ -73,7 +70,6 @@ export class SelectOptionsService {
|
||||||
{ name: 'KENTUCKY', abbreviation: 'KY' },
|
{ name: 'KENTUCKY', abbreviation: 'KY' },
|
||||||
{ name: 'LOUISIANA', abbreviation: 'LA' },
|
{ name: 'LOUISIANA', abbreviation: 'LA' },
|
||||||
{ name: 'MAINE', abbreviation: 'ME' },
|
{ name: 'MAINE', abbreviation: 'ME' },
|
||||||
{ name: 'MARSHALL ISLANDS', abbreviation: 'MH'},
|
|
||||||
{ name: 'MARYLAND', abbreviation: 'MD' },
|
{ name: 'MARYLAND', abbreviation: 'MD' },
|
||||||
{ name: 'MASSACHUSETTS', abbreviation: 'MA' },
|
{ name: 'MASSACHUSETTS', abbreviation: 'MA' },
|
||||||
{ name: 'MICHIGAN', abbreviation: 'MI' },
|
{ name: 'MICHIGAN', abbreviation: 'MI' },
|
||||||
|
|
@ -89,13 +85,11 @@ export class SelectOptionsService {
|
||||||
{ name: 'NEW YORK', abbreviation: 'NY' },
|
{ name: 'NEW YORK', abbreviation: 'NY' },
|
||||||
{ name: 'NORTH CAROLINA', abbreviation: 'NC' },
|
{ name: 'NORTH CAROLINA', abbreviation: 'NC' },
|
||||||
{ name: 'NORTH DAKOTA', abbreviation: 'ND' },
|
{ name: 'NORTH DAKOTA', abbreviation: 'ND' },
|
||||||
{ name: 'NORTHERN MARIANA ISLANDS', abbreviation: 'MP'},
|
|
||||||
{ name: 'OHIO', abbreviation: 'OH' },
|
{ name: 'OHIO', abbreviation: 'OH' },
|
||||||
{ name: 'OKLAHOMA', abbreviation: 'OK' },
|
{ name: 'OKLAHOMA', abbreviation: 'OK' },
|
||||||
{ name: 'OREGON', abbreviation: 'OR' },
|
{ name: 'OREGON', abbreviation: 'OR' },
|
||||||
{ name: 'PALAU', abbreviation: 'PW' },
|
{ name: 'PALAU', abbreviation: 'PW' },
|
||||||
{ name: 'PENNSYLVANIA', abbreviation: 'PA' },
|
{ name: 'PENNSYLVANIA', abbreviation: 'PA' },
|
||||||
{ name: 'PUERTO RICO', abbreviation: 'PR'},
|
|
||||||
{ name: 'RHODE ISLAND', abbreviation: 'RI' },
|
{ name: 'RHODE ISLAND', abbreviation: 'RI' },
|
||||||
{ name: 'SOUTH CAROLINA', abbreviation: 'SC' },
|
{ name: 'SOUTH CAROLINA', abbreviation: 'SC' },
|
||||||
{ name: 'SOUTH DAKOTA', abbreviation: 'SD' },
|
{ name: 'SOUTH DAKOTA', abbreviation: 'SD' },
|
||||||
|
|
@ -103,13 +97,11 @@ export class SelectOptionsService {
|
||||||
{ name: 'TEXAS', abbreviation: 'TX' },
|
{ name: 'TEXAS', abbreviation: 'TX' },
|
||||||
{ name: 'UTAH', abbreviation: 'UT' },
|
{ name: 'UTAH', abbreviation: 'UT' },
|
||||||
{ name: 'VERMONT', abbreviation: 'VT' },
|
{ name: 'VERMONT', abbreviation: 'VT' },
|
||||||
{ name: 'VIRGIN ISLANDS', abbreviation: 'VI'},
|
|
||||||
{ name: 'VIRGINIA', abbreviation: 'VA' },
|
{ name: 'VIRGINIA', abbreviation: 'VA' },
|
||||||
{ name: 'WASHINGTON', abbreviation: 'WA' },
|
{ name: 'WASHINGTON', abbreviation: 'WA' },
|
||||||
{ name: 'WEST VIRGINIA', abbreviation: 'WV' },
|
{ name: 'WEST VIRGINIA', abbreviation: 'WV' },
|
||||||
{ name: 'WISCONSIN', abbreviation: 'WI' },
|
{ name: 'WISCONSIN', abbreviation: 'WI' },
|
||||||
{ name: 'WYOMING', abbreviation: 'WY' }
|
{ name: 'WYOMING', abbreviation: 'WY' },
|
||||||
]
|
];
|
||||||
public locations:Array<any> = [...this.usStates.map(state=>({name:state.name, value:state.abbreviation}))].concat({name:'CANADA',value:"CA"});
|
public locations: Array<any> = [...this.usStates.map(state => ({ name: state.name, value: state.abbreviation }))].concat({ name: 'CANADA', value: 'CA' });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,47 @@
|
||||||
import { Get, Inject, Injectable, Param } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { createClient } from 'redis';
|
import { and, eq, ilike, or, sql } from 'drizzle-orm';
|
||||||
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 { NodePgDatabase } from 'drizzle-orm/node-postgres/driver.js';
|
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 * 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()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
constructor(
|
||||||
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,private fileService:FileService) {
|
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||||
}
|
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
|
||||||
|
private fileService: FileService,
|
||||||
|
) {}
|
||||||
private getConditions(criteria: ListingCriteria): any[] {
|
private getConditions(criteria: ListingCriteria): any[] {
|
||||||
const conditions = [];
|
const conditions = [];
|
||||||
if (criteria.state) {
|
if (criteria.state) {
|
||||||
conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`);
|
conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`);
|
||||||
}
|
}
|
||||||
|
if (criteria.name) {
|
||||||
|
conditions.push(or(ilike(schema.users.firstname, `%${criteria.name}%`), ilike(schema.users.lastname, `%${criteria.name}%`)));
|
||||||
|
}
|
||||||
return conditions;
|
return conditions;
|
||||||
}
|
}
|
||||||
async getUserByMail( id:string){
|
async getUserByMail(email: string) {
|
||||||
const users = await this.conn.select().from(schema.users).where(sql`email = ${id}`) as User[]
|
const users = (await this.conn
|
||||||
const user = users[0]
|
.select()
|
||||||
user.hasCompanyLogo=this.fileService.hasCompanyLogo(id);
|
.from(schema.users)
|
||||||
user.hasProfile=this.fileService.hasProfile(id);
|
.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;
|
return user;
|
||||||
}
|
}
|
||||||
async getUserById(id: string) {
|
async getUserById(id: string) {
|
||||||
const users = await this.conn.select().from(schema.users).where(sql`id = ${id}`) as User[]
|
const users = (await this.conn
|
||||||
const user = users[0]
|
.select()
|
||||||
|
.from(schema.users)
|
||||||
|
.where(sql`id = ${id}`)) as User[];
|
||||||
|
const user = users[0];
|
||||||
user.hasCompanyLogo = this.fileService.hasCompanyLogo(id);
|
user.hasCompanyLogo = this.fileService.hasCompanyLogo(id);
|
||||||
user.hasProfile = this.fileService.hasProfile(id);
|
user.hasProfile = this.fileService.hasProfile(id);
|
||||||
return user;
|
return user;
|
||||||
|
|
@ -50,12 +58,20 @@ export class UserService {
|
||||||
async findUser(criteria: ListingCriteria) {
|
async findUser(criteria: ListingCriteria) {
|
||||||
const start = criteria.start ? criteria.start : 0;
|
const start = criteria.start ? criteria.start : 0;
|
||||||
const length = criteria.length ? criteria.length : 12;
|
const length = criteria.length ? criteria.length : 12;
|
||||||
const conditions = this.getConditions(criteria)
|
const conditions = this.getConditions(criteria);
|
||||||
const [data, total] = await Promise.all([
|
const [data, total] = await Promise.all([
|
||||||
this.conn.select().from(schema.users).where(and(...conditions)).offset(start).limit(length),
|
this.conn
|
||||||
this.conn.select({ count: sql`count(*)` }).from(schema.users).where(and(...conditions)).then((result) => Number(result[0].count)),
|
.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 };
|
return { total, data };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,5 +7,15 @@
|
||||||
"target": "http://localhost:3000",
|
"target": "http://localhost:3000",
|
||||||
"secure": false,
|
"secure": false,
|
||||||
"changeOrigin": true
|
"changeOrigin": true
|
||||||
|
},
|
||||||
|
"/logo": {
|
||||||
|
"target": "http://localhost:3000",
|
||||||
|
"secure": false,
|
||||||
|
"changeOrigin": true
|
||||||
|
},
|
||||||
|
"/profile": {
|
||||||
|
"target": "http://localhost:3000",
|
||||||
|
"secure": false,
|
||||||
|
"changeOrigin": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="col-12 md:col-6">
|
<div class="col-12 md:col-6">
|
||||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
<div class="text-500 w-full md:w-2 font-medium flex align-self-start">Description</div>
|
||||||
<div class="text-900 w-full md:w-10 line-height-3" [innerHTML]="description"></div>
|
<div class="text-900 w-full md:w-10 line-height-3" [innerHTML]="description"></div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
|
|
@ -50,16 +50,6 @@
|
||||||
<div class="text-900 w-full md:w-10">{{ listing.brokerLicencing }}</div>
|
<div class="text-900 w-full md:w-10">{{ listing.brokerLicencing }}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p-galleria [value]="propertyImages" [showIndicators]="true" [showThumbnails]="false" [responsiveOptions]="responsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5">
|
|
||||||
<ng-template pTemplate="item" let-item>
|
|
||||||
<img src="{{ environment.apiBaseUrl }}/property/{{ listing.id }}/{{ item.name }}" style="width: 100%" />
|
|
||||||
</ng-template>
|
|
||||||
<!-- <ng-template pTemplate="thumbnail" let-item>
|
|
||||||
<div class="grid grid-nogutter justify-content-center">
|
|
||||||
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{item.name}}" />
|
|
||||||
</div>
|
|
||||||
</ng-template> -->
|
|
||||||
</p-galleria>
|
|
||||||
@if(listing && user && (user.id===listing?.userId || isAdmin())){
|
@if(listing && user && (user.id===listing?.userId || isAdmin())){
|
||||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editListing', listing.id]"></button>
|
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editListing', listing.id]"></button>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Location } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
@ -46,7 +47,6 @@ export class DetailsBusinessListingComponent {
|
||||||
listing: BusinessListing;
|
listing: BusinessListing;
|
||||||
criteria: ListingCriteria;
|
criteria: ListingCriteria;
|
||||||
mailinfo: MailInfo;
|
mailinfo: MailInfo;
|
||||||
propertyImages: string[] = [];
|
|
||||||
environment = environment;
|
environment = environment;
|
||||||
user: User;
|
user: User;
|
||||||
description: SafeHtml;
|
description: SafeHtml;
|
||||||
|
|
@ -59,27 +59,28 @@ export class DetailsBusinessListingComponent {
|
||||||
private mailService: MailService,
|
private mailService: MailService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
|
private location: Location,
|
||||||
) {
|
) {
|
||||||
this.userService.getUserObservable().subscribe(user => {
|
this.userService.getUserObservable().subscribe(user => {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.mailinfo = { sender: {}, userId: '', email: user.email };
|
||||||
});
|
});
|
||||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||||
this.mailinfo = { sender: {}, userId: '' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business'));
|
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);
|
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
||||||
}
|
}
|
||||||
back() {
|
back() {
|
||||||
this.router.navigate(['businessListings']);
|
this.location.back();
|
||||||
}
|
}
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return this.userService.hasAdminRole();
|
return this.userService.hasAdminRole();
|
||||||
}
|
}
|
||||||
async mail() {
|
async mail() {
|
||||||
this.mailinfo.userId = this.listing.userId;
|
this.mailinfo.userId = this.listing.userId;
|
||||||
|
this.mailinfo.listing = this.listing;
|
||||||
await this.mailService.mail(this.mailinfo);
|
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 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Your message has been sent to the creator of the listing', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="col-12 md:col-6">
|
<div class="col-12 md:col-6">
|
||||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
<div class="text-500 w-full md:w-2 font-medium flex align-self-start">Description</div>
|
||||||
<div class="text-900 w-full md:w-10 line-height-3" [innerHTML]="description"></div>
|
<div class="text-900 w-full md:w-10 line-height-3" [innerHTML]="description"></div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Location } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
@ -59,12 +60,13 @@ export class DetailsCommercialPropertyListingComponent {
|
||||||
private mailService: MailService,
|
private mailService: MailService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
|
private location: Location,
|
||||||
) {
|
) {
|
||||||
this.userService.getUserObservable().subscribe(user => {
|
this.userService.getUserObservable().subscribe(user => {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.mailinfo = { sender: {}, userId: '', email: user.email };
|
||||||
});
|
});
|
||||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||||
this.mailinfo = { sender: {}, userId: '' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|
@ -73,13 +75,14 @@ export class DetailsCommercialPropertyListingComponent {
|
||||||
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
||||||
}
|
}
|
||||||
back() {
|
back() {
|
||||||
this.router.navigate(['commercialPropertyListings']);
|
this.location.back();
|
||||||
}
|
}
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return this.userService.hasAdminRole();
|
return this.userService.hasAdminRole();
|
||||||
}
|
}
|
||||||
async mail() {
|
async mail() {
|
||||||
this.mailinfo.userId = this.listing.userId;
|
this.mailinfo.userId = this.listing.userId;
|
||||||
|
this.mailinfo.listing = this.listing;
|
||||||
await this.mailService.mail(this.mailinfo);
|
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 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Your message has been sent to the creator of the listing', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<div class="surface-ground h-full">
|
<div class="surface-ground h-full">
|
||||||
<div class="px-6 py-5">
|
<div class="px-6 py-5">
|
||||||
|
@if (user){
|
||||||
<div class="surface-card p-4 shadow-2 border-round">
|
<div class="surface-card p-4 shadow-2 border-round">
|
||||||
<!-- <div class="flex justify-content-between align-items-center align-content-center">
|
<!-- <div class="flex justify-content-between align-items-center align-content-center">
|
||||||
<div class="font-medium text-3xl text-900 mb-3">{{listing?.title}}</div>
|
<div class="font-medium text-3xl text-900 mb-3">{{listing?.title}}</div>
|
||||||
|
|
@ -118,5 +119,6 @@
|
||||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/account']"></button>
|
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/account']"></button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
|
import { Location } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { MessageService } from 'primeng/api';
|
|
||||||
import { GalleriaModule } from 'primeng/galleria';
|
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
|
||||||
|
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { MessageService } from 'primeng/api';
|
||||||
|
import { GalleriaModule } from 'primeng/galleria';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { KeyValue, ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { KeyValue, ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
@ -13,6 +12,7 @@ import { ImageService } from '../../../services/image.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-details-user',
|
selector: 'app-details-user',
|
||||||
|
|
@ -41,6 +41,7 @@ export class DetailsUserComponent {
|
||||||
public selectOptions: SelectOptionsService,
|
public selectOptions: SelectOptionsService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
|
private location: Location,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|
@ -54,7 +55,7 @@ export class DetailsUserComponent {
|
||||||
this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices);
|
this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices);
|
||||||
}
|
}
|
||||||
back() {
|
back() {
|
||||||
this.router.navigate(['brokerListings']);
|
this.location.back();
|
||||||
}
|
}
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return this.userService.hasAdminRole();
|
return this.userService.hasAdminRole();
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<ul class="flex flex-column align-items-left gap-3 px-2 py-3 list-none surface-border">
|
<ul class="flex flex-column align-items-left gap-3 px-2 py-3 list-none surface-border">
|
||||||
<li><button pButton pRipple icon="pi pi-user" (click)="activeTabAction = 'business'" label="Businesses" [ngClass]="{ 'p-button-text text-700': activeTabAction !== 'business' }"></button></li>
|
<li><button pButton pRipple icon="pi pi-user" (click)="activeTabAction = 'business'" label="Businesses" [ngClass]="{ 'p-button-text text-700': activeTabAction !== 'business' }"></button></li>
|
||||||
<li>
|
|
||||||
<button pButton pRipple icon="pi pi-globe" (click)="activeTabAction = 'broker'" label="Professionals/Brokers Directory" [ngClass]="{ 'p-button-text text-700': activeTabAction != 'broker' }"></button>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
pButton
|
pButton
|
||||||
|
|
@ -41,13 +38,32 @@
|
||||||
[ngClass]="{ 'p-button-text text-700': activeTabAction != 'commercialProperty' }"
|
[ngClass]="{ 'p-button-text text-700': activeTabAction != 'commercialProperty' }"
|
||||||
></button>
|
></button>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<button pButton pRipple icon="pi pi-globe" (click)="activeTabAction = 'broker'" label="Professionals/Brokers Directory" [ngClass]="{ 'p-button-text text-700': activeTabAction != 'broker' }"></button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<div class="flex flex-column align-items-right gap-3 px-2 py-3 my-3 surface-border">
|
<div class="flex flex-column align-items-right gap-3 px-2 py-3 my-3 surface-border">
|
||||||
|
<p-dropdown [options]="selectOptions.states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '200px' }"></p-dropdown>
|
||||||
|
@if(activeTabAction === 'business'){
|
||||||
<p-dropdown [options]="selectOptions.typesOfBusiness" [(ngModel)]="criteria.type" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Category" [style]="{ width: '200px' }"></p-dropdown>
|
<p-dropdown [options]="selectOptions.typesOfBusiness" [(ngModel)]="criteria.type" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Category" [style]="{ width: '200px' }"></p-dropdown>
|
||||||
|
} @if(activeTabAction === 'commercialProperty'){
|
||||||
|
<p-dropdown
|
||||||
|
[options]="selectOptions.typesOfCommercialProperty"
|
||||||
|
[(ngModel)]="criteria.type"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
[showClear]="true"
|
||||||
|
placeholder="Category"
|
||||||
|
[style]="{ width: '200px' }"
|
||||||
|
></p-dropdown>
|
||||||
|
} @if(activeTabAction === 'business' || activeTabAction === 'commercialProperty'){
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '200px' }"></p-dropdown>
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '200px' }"></p-dropdown>
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '200px' }"></p-dropdown>
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '200px' }"></p-dropdown>
|
||||||
|
} @else {
|
||||||
|
<div style="height: 46px"> </div>
|
||||||
|
}
|
||||||
<button pButton pRipple label="Find" class="ml-3 font-bold" [style]="{ width: '170px' }" (click)="search()"></button>
|
<button pButton pRipple label="Find" class="ml-3 font-bold" [style]="{ width: '170px' }" (click)="search()"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,14 @@
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Location" [style]="{ width: '100%' }"></p-dropdown>
|
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Location" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 col-offset-9">
|
<div class="col-2">
|
||||||
<p-button label="Refine" (click)="search()"></p-button>
|
<p-inputGroup>
|
||||||
|
<input id="name" type="text" pInputText [(ngModel)]="criteria.name" placeholder="Name" />
|
||||||
|
<button type="button" pButton icon="pi pi-times" class="p-button-danger" (click)="reset()"></button>
|
||||||
|
</p-inputGroup>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 col-offset-7">
|
||||||
|
<p-button label="Refine" (click)="refine()"></p-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -14,13 +20,13 @@
|
||||||
<div class="surface-200 h-full">
|
<div class="surface-200 h-full">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
@for (user of users; track user.id) {
|
@for (user of users; track user) {
|
||||||
<div class="col-12 lg:col-6 xl:col-4 p-4 flex flex-column flex-grow-1">
|
<div class="col-12 lg:col-6 xl:col-4 p-4 flex flex-column">
|
||||||
<div class="surface-card shadow-2 p-2 flex flex-column flex-grow-1 justify-content-between" style="border-radius: 10px">
|
<div class="surface-card shadow-2 p-2 flex flex-column flex-grow-1 justify-content-between" style="border-radius: 10px">
|
||||||
<div class="surface-card p-4 flex flex-column align-items-center md:flex-row md:align-items-stretch h-full">
|
<div class="surface-card p-4 flex flex-column align-items-center md:flex-row md:align-items-stretch h-full">
|
||||||
<span>
|
<span>
|
||||||
@if(user.hasProfile){
|
@if(user.hasProfile){
|
||||||
<img src="{{ environment.apiBaseUrl }}/profile/{{ user.id }}.avif?_ts={{ ts }}" class="w-5rem" />
|
<img src="profile/{{ user.id }}.avif" class="w-5rem" />
|
||||||
} @else {
|
} @else {
|
||||||
<img src="assets/images/person_placeholder.jpg" class="w-5rem" />
|
<img src="assets/images/person_placeholder.jpg" class="w-5rem" />
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-3 text-right flex justify-content-between align-items-center">
|
<div class="px-4 py-3 text-right flex justify-content-between align-items-center">
|
||||||
@if(user.hasCompanyLogo){
|
@if(user.hasCompanyLogo){
|
||||||
<img src="{{ environment.apiBaseUrl }}/logo/{{ user.id }}.avif" class="rounded-image" />
|
<img src="logo/{{ user.id }}.avif" class="rounded-image" />
|
||||||
} @else {
|
} @else {
|
||||||
<img src="assets/images/placeholder.png" class="rounded-image" />
|
<img src="assets/images/placeholder.png" class="rounded-image" />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import onChange from 'on-change';
|
||||||
import { ButtonModule } from 'primeng/button';
|
import { ButtonModule } from 'primeng/button';
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
import { CheckboxModule } from 'primeng/checkbox';
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
import { DropdownModule } from 'primeng/dropdown';
|
||||||
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
import { PaginatorModule } from 'primeng/paginator';
|
import { PaginatorModule } from 'primeng/paginator';
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
import { StyleClassModule } from 'primeng/styleclass';
|
||||||
|
|
@ -22,7 +23,7 @@ import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler }
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-broker-listings',
|
selector: 'app-broker-listings',
|
||||||
standalone: true,
|
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: './broker-listings.component.html',
|
templateUrl: './broker-listings.component.html',
|
||||||
styleUrl: './broker-listings.component.scss',
|
styleUrl: './broker-listings.component.scss',
|
||||||
})
|
})
|
||||||
|
|
@ -33,7 +34,6 @@ export class BrokerListingsComponent {
|
||||||
filteredListings: Array<ListingType>;
|
filteredListings: Array<ListingType>;
|
||||||
criteria: ListingCriteria;
|
criteria: ListingCriteria;
|
||||||
realEstateChecked: boolean;
|
realEstateChecked: boolean;
|
||||||
// category: string;
|
|
||||||
maxPrice: string;
|
maxPrice: string;
|
||||||
minPrice: string;
|
minPrice: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -56,12 +56,12 @@ export class BrokerListingsComponent {
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
) {
|
) {
|
||||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||||
|
this.criteria.type = undefined;
|
||||||
this.router.getCurrentNavigation();
|
this.router.getCurrentNavigation();
|
||||||
this.activatedRoute.snapshot;
|
this.activatedRoute.snapshot;
|
||||||
this.activatedRoute.params.subscribe(params => {
|
this.activatedRoute.params.subscribe(params => {
|
||||||
if (this.activatedRoute.snapshot.fragment === '') {
|
if (this.activatedRoute.snapshot.fragment === '') {
|
||||||
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
||||||
this.first = 0;
|
|
||||||
}
|
}
|
||||||
this.init();
|
this.init();
|
||||||
});
|
});
|
||||||
|
|
@ -73,16 +73,15 @@ export class BrokerListingsComponent {
|
||||||
async init() {
|
async init() {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
refine() {
|
||||||
|
this.criteria.start = 0;
|
||||||
|
this.criteria.page = 0;
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
async search() {
|
async search() {
|
||||||
const usersReponse = await this.userService.search(this.criteria);
|
const usersReponse = await this.userService.search(this.criteria);
|
||||||
this.users = usersReponse.data;
|
this.users = usersReponse.data;
|
||||||
this.totalRecords = usersReponse.total;
|
this.totalRecords = usersReponse.total;
|
||||||
const profiles = await this.imageService.getProfileImagesForUsers(this.users.map(u => u.id));
|
|
||||||
const logos = await this.imageService.getCompanyLogosForUsers(this.users.map(u => u.id));
|
|
||||||
this.users.forEach(u => {
|
|
||||||
u.hasProfile = profiles[u.id];
|
|
||||||
u.hasCompanyLogo = logos[u.id];
|
|
||||||
});
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +92,7 @@ export class BrokerListingsComponent {
|
||||||
this.criteria.pageCount = event.pageCount;
|
this.criteria.pageCount = event.pageCount;
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
imageErrorHandler(listing: ListingType) {
|
reset() {
|
||||||
// listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann
|
this.criteria.name = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="grid p-4 align-items-center">
|
<div class="grid p-4 align-items-center">
|
||||||
|
<div class="col-2">
|
||||||
|
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="criteria.location" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown
|
<p-dropdown
|
||||||
[options]="selectOptions.typesOfBusiness"
|
[options]="selectOptions.typesOfBusiness"
|
||||||
|
|
@ -13,21 +16,24 @@
|
||||||
[style]="{ width: '100%' }"
|
[style]="{ width: '100%' }"
|
||||||
></p-dropdown>
|
></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
|
||||||
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="criteria.location" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '100%' }"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '100%' }"></p-dropdown>
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '100%' }"></p-dropdown>
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-2">
|
||||||
|
<p-inputGroup>
|
||||||
|
<input id="name" type="text" pInputText [(ngModel)]="criteria.title" placeholder="Title" />
|
||||||
|
<button type="button" pButton icon="pi pi-times" class="p-button-secondary" (click)="reset()"></button>
|
||||||
|
</p-inputGroup>
|
||||||
|
</div>
|
||||||
|
<div class="col-1" pTooltip="Real Estate excluded/included" tooltipPosition="top">
|
||||||
<!-- <p-toggleButton [(ngModel)]="checked1" onLabel="Sustainable" offLabel="Unsustainable" onIcon="pi pi-check" offIcon="pi pi-times" styleClass="mb-3 lg:mt-0 mr-4 flex-shrink-0 w-12rem"></p-toggleButton> -->
|
<!-- <p-toggleButton [(ngModel)]="checked1" onLabel="Sustainable" offLabel="Unsustainable" onIcon="pi pi-check" offIcon="pi pi-times" styleClass="mb-3 lg:mt-0 mr-4 flex-shrink-0 w-12rem"></p-toggleButton> -->
|
||||||
<p-toggleButton [(ngModel)]="criteria.realEstateChecked" onLabel="Real Estate not included" offLabel="Real Estate included"></p-toggleButton>
|
<p-toggleButton [(ngModel)]="criteria.realEstateChecked" onLabel="RE incl." offLabel="RE excl."></p-toggleButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<p-button label="Refine" (click)="search()"></p-button>
|
<p-button label="Refine" (click)="refine()"></p-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -52,7 +58,7 @@
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{ selectOptions.getState(listing.state) }}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{ selectOptions.getState(listing.state) }}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{ listing.established }}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{ listing.established }}</p>
|
||||||
<div class="mt-auto ml-auto">
|
<div class="mt-auto ml-auto">
|
||||||
<img src="{{ environment.apiBaseUrl }}/logo/{{ listing.userId }}" (error)="imageErrorHandler(listing)" class="rounded-image" />
|
<img src="{{ environment.apiBaseUrl }}/logo/{{ listing.userId }}.avif" (error)="imageErrorHandler(listing)" class="rounded-image" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-3 surface-100 text-left">
|
<div class="px-4 py-3 surface-100 text-left">
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
.search {
|
.search {
|
||||||
background-color: #343F69;
|
background-color: #343f69;
|
||||||
}
|
}
|
||||||
::ng-deep p-paginator div {
|
::ng-deep p-paginator div {
|
||||||
background-color: var(--surface-200) !important;
|
background-color: var(--surface-200) !important;
|
||||||
|
|
@ -21,3 +21,6 @@
|
||||||
padding: 1px 1px;
|
padding: 1px 1px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
::ng-deep span.p-button-label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ import onChange from 'on-change';
|
||||||
import { ButtonModule } from 'primeng/button';
|
import { ButtonModule } from 'primeng/button';
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
import { CheckboxModule } from 'primeng/checkbox';
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
import { DropdownModule } from 'primeng/dropdown';
|
||||||
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
import { PaginatorModule } from 'primeng/paginator';
|
import { PaginatorModule } from 'primeng/paginator';
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
import { StyleClassModule } from 'primeng/styleclass';
|
||||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||||
|
import { TooltipModule } from 'primeng/tooltip';
|
||||||
import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { ListingCriteria, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { ListingCriteria, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
|
|
@ -21,7 +23,21 @@ import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler }
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-business-listings',
|
selector: 'app-business-listings',
|
||||||
standalone: true,
|
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',
|
templateUrl: './business-listings.component.html',
|
||||||
styleUrl: './business-listings.component.scss',
|
styleUrl: './business-listings.component.scss',
|
||||||
})
|
})
|
||||||
|
|
@ -36,10 +52,10 @@ export class BusinessListingsComponent {
|
||||||
type: string;
|
type: string;
|
||||||
states = [];
|
states = [];
|
||||||
state: string;
|
state: string;
|
||||||
first: number = 0;
|
|
||||||
rows: number = 12;
|
|
||||||
totalRecords: number = 0;
|
totalRecords: number = 0;
|
||||||
ts = new Date().getTime();
|
ts = new Date().getTime();
|
||||||
|
first: number = 0;
|
||||||
|
rows: number = 12;
|
||||||
public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined;
|
public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -56,7 +72,6 @@ export class BusinessListingsComponent {
|
||||||
this.activatedRoute.params.subscribe(params => {
|
this.activatedRoute.params.subscribe(params => {
|
||||||
if (this.activatedRoute.snapshot.fragment === '') {
|
if (this.activatedRoute.snapshot.fragment === '') {
|
||||||
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
||||||
this.first = 0;
|
|
||||||
}
|
}
|
||||||
this.category = (<any>params).type;
|
this.category = (<any>params).type;
|
||||||
this.init();
|
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.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls }));
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
refine() {
|
||||||
|
this.criteria.start = 0;
|
||||||
|
this.criteria.page = 0;
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
async search() {
|
async search() {
|
||||||
const listingReponse = await this.listingsService.getListings(this.criteria, 'business');
|
const listingReponse = await this.listingsService.getListings(this.criteria, 'business');
|
||||||
this.listings = listingReponse.data;
|
this.listings = listingReponse.data;
|
||||||
|
|
@ -85,4 +105,7 @@ export class BusinessListingsComponent {
|
||||||
imageErrorHandler(listing: ListingType) {
|
imageErrorHandler(listing: ListingType) {
|
||||||
// listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann
|
// listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann
|
||||||
}
|
}
|
||||||
|
reset() {
|
||||||
|
this.criteria.title = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,31 @@
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Location" [style]="{ width: '100%' }"></p-dropdown>
|
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Location" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 col-offset-9">
|
<div class="col-2">
|
||||||
<p-button label="Refine" (click)="search()"></p-button>
|
<p-dropdown
|
||||||
|
[options]="selectOptions.typesOfCommercialProperty"
|
||||||
|
[(ngModel)]="criteria.type"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
[showClear]="true"
|
||||||
|
placeholder="Categorie of Property"
|
||||||
|
[style]="{ width: '100%' }"
|
||||||
|
></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<p-inputGroup>
|
||||||
|
<input id="name" type="text" pInputText [(ngModel)]="criteria.title" placeholder="Title" />
|
||||||
|
<button type="button" pButton icon="pi pi-times" class="p-button-secondary" (click)="reset()"></button>
|
||||||
|
</p-inputGroup>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 col-offset-1">
|
||||||
|
<p-button label="Refine" (click)="refine()"></p-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -19,7 +42,7 @@
|
||||||
<div class="surface-card p-2 flex flex-column flex-grow-1 justify-content-between" style="border-radius: 10px">
|
<div class="surface-card p-2 flex flex-column flex-grow-1 justify-content-between" style="border-radius: 10px">
|
||||||
<article class="flex flex-column md:flex-row w-full gap-3 p-3 surface-card">
|
<article class="flex flex-column md:flex-row w-full gap-3 p-3 surface-card">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@if (listing.imageOrder.length>0){
|
@if (listing.imageOrder?.length>0){
|
||||||
<img src="property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
|
<img src="property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
|
||||||
} @else {
|
} @else {
|
||||||
<!-- <img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{listing.imageOrder[0].name}}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem"> -->
|
<!-- <img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{listing.imageOrder[0].name}}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem"> -->
|
||||||
|
|
@ -60,9 +83,12 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-2 surface-200 flex align-items-center justify-content-center">
|
<div class="mb-2 surface-200 flex align-items-center justify-content-center">
|
||||||
|
<!-- @if(listings && listings.length>12){ -->
|
||||||
<div class="mx-1 text-color">Total number of Listings: {{ totalRecords }}</div>
|
<div class="mx-1 text-color">Total number of Listings: {{ totalRecords }}</div>
|
||||||
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]"></p-paginator>
|
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]"></p-paginator>
|
||||||
|
<!-- } -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import onChange from 'on-change';
|
||||||
import { ButtonModule } from 'primeng/button';
|
import { ButtonModule } from 'primeng/button';
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
import { CheckboxModule } from 'primeng/checkbox';
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
import { DropdownModule } from 'primeng/dropdown';
|
||||||
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
import { PaginatorModule } from 'primeng/paginator';
|
import { PaginatorModule } from 'primeng/paginator';
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
import { StyleClassModule } from 'primeng/styleclass';
|
||||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
import { ToggleButtonModule } from 'primeng/togglebutton';
|
||||||
import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
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 { environment } from '../../../../environments/environment';
|
||||||
import { ImageService } from '../../../services/image.service';
|
import { ImageService } from '../../../services/image.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
|
@ -21,7 +22,7 @@ import { createGenericObject, getCriteriaStateObject, getSessionStorageHandler }
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-commercial-property-listings',
|
selector: 'app-commercial-property-listings',
|
||||||
standalone: true,
|
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',
|
templateUrl: './commercial-property-listings.component.html',
|
||||||
styleUrl: './commercial-property-listings.component.scss',
|
styleUrl: './commercial-property-listings.component.scss',
|
||||||
})
|
})
|
||||||
|
|
@ -31,15 +32,14 @@ export class CommercialPropertyListingsComponent {
|
||||||
filteredListings: Array<CommercialPropertyListing>;
|
filteredListings: Array<CommercialPropertyListing>;
|
||||||
criteria: ListingCriteria;
|
criteria: ListingCriteria;
|
||||||
realEstateChecked: boolean;
|
realEstateChecked: boolean;
|
||||||
|
first: number = 0;
|
||||||
|
rows: number = 12;
|
||||||
maxPrice: string;
|
maxPrice: string;
|
||||||
minPrice: string;
|
minPrice: string;
|
||||||
type: string;
|
type: string;
|
||||||
states = [];
|
states = [];
|
||||||
statesSet = new Set();
|
statesSet = new Set();
|
||||||
state: string;
|
state: string;
|
||||||
first: number = 0;
|
|
||||||
rows: number = 12;
|
|
||||||
totalRecords: number = 0;
|
totalRecords: number = 0;
|
||||||
ts = new Date().getTime();
|
ts = new Date().getTime();
|
||||||
|
|
||||||
|
|
@ -52,12 +52,12 @@ export class CommercialPropertyListingsComponent {
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
) {
|
) {
|
||||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||||
|
this.criteria.type = undefined;
|
||||||
this.router.getCurrentNavigation();
|
this.router.getCurrentNavigation();
|
||||||
this.activatedRoute.snapshot;
|
this.activatedRoute.snapshot;
|
||||||
this.activatedRoute.params.subscribe(params => {
|
this.activatedRoute.params.subscribe(params => {
|
||||||
if (this.activatedRoute.snapshot.fragment === '') {
|
if (this.activatedRoute.snapshot.fragment === '') {
|
||||||
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
this.criteria = onChange(createGenericObject<ListingCriteria>(), getSessionStorageHandler);
|
||||||
this.first = 0;
|
|
||||||
}
|
}
|
||||||
this.init();
|
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.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls }));
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
refine() {
|
||||||
|
this.criteria.start = 0;
|
||||||
|
this.criteria.page = 0;
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
async search() {
|
async search() {
|
||||||
const listingReponse = await this.listingsService.getListings(this.criteria, 'commercialProperty');
|
const listingReponse = await this.listingsService.getListings(this.criteria, 'commercialProperty');
|
||||||
this.listings = listingReponse.data;
|
this.listings = listingReponse.data;
|
||||||
|
|
@ -83,7 +87,7 @@ export class CommercialPropertyListingsComponent {
|
||||||
this.criteria.pageCount = event.pageCount;
|
this.criteria.pageCount = event.pageCount;
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
imageErrorHandler(listing: ListingType) {
|
reset() {
|
||||||
// listing.hideImage = true; // Bild ausblenden, wenn es nicht geladen werden kann
|
this.criteria.title = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@
|
||||||
<span class="font-medium text-xs mb-2">(is shown in every offer)</span>
|
<span class="font-medium text-xs mb-2">(is shown in every offer)</span>
|
||||||
@if(user?.hasCompanyLogo){
|
@if(user?.hasCompanyLogo){
|
||||||
<img src="{{ companyLogoUrl }}" class="rounded-profile" />
|
<img src="{{ companyLogoUrl }}" class="rounded-profile" />
|
||||||
|
<!-- <img src="profile/{{ user.id }}.avif" class="rounded-profile" /> -->
|
||||||
} @else {
|
} @else {
|
||||||
<img src="assets/images/placeholder.png" class="rounded-profile" />
|
<img src="assets/images/placeholder.png" class="rounded-profile" />
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +122,7 @@
|
||||||
<p-divider></p-divider>
|
<p-divider></p-divider>
|
||||||
<div class="flex flex-column align-items-center flex-or">
|
<div class="flex flex-column align-items-center flex-or">
|
||||||
<span class="font-medium text-900 mb-2">Your Profile Picture</span>
|
<span class="font-medium text-900 mb-2">Your Profile Picture</span>
|
||||||
@if(user.hasProfile){
|
@if(user?.hasProfile){
|
||||||
<img src="{{ profileUrl }}" class="rounded-profile" />
|
<img src="{{ profileUrl }}" class="rounded-profile" />
|
||||||
} @else {
|
} @else {
|
||||||
<img src="assets/images/person_placeholder.jpg" class="rounded-profile" />
|
<img src="assets/images/person_placeholder.jpg" class="rounded-profile" />
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,25 @@ export class AccountComponent {
|
||||||
public dialogService: DialogService,
|
public dialogService: DialogService,
|
||||||
) {}
|
) {}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const email = this.userService.getUser().email;
|
const keycloakUser = this.userService.getKeycloakUser();
|
||||||
|
const email = keycloakUser.email;
|
||||||
|
try {
|
||||||
this.user = await this.userService.getByMail(email);
|
this.user = await this.userService.getByMail(email);
|
||||||
this.userLicensedIn = this.user.licensedIn.map(l => {
|
} 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] };
|
return { name: l.split('|')[0], value: l.split('|')[1] };
|
||||||
});
|
})
|
||||||
|
: [];
|
||||||
this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||||
if (!this.user.licensedIn || this.user.licensedIn?.length === 0) {
|
if (!this.user.licensedIn || this.user.licensedIn?.length === 0) {
|
||||||
this.user.licensedIn = [''];
|
this.user.licensedIn = [''];
|
||||||
}
|
}
|
||||||
this.profileUrl = this.user.hasProfile ? `${environment.apiBaseUrl}/profile/${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 ? `${environment.apiBaseUrl}/logo/${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) {}
|
printInvoice(invoice: Invoice) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
||||||
<p-dropdown
|
<p-dropdown id="type" [options]="typesOfBusiness" [(ngModel)]="listing.type" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Type of business" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
id="type"
|
|
||||||
[options]="selectOptions?.typesOfBusiness"
|
|
||||||
[(ngModel)]="listing.type"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
[showClear]="true"
|
|
||||||
placeholder="Type of business"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ export class EditBusinessListingComponent {
|
||||||
draggedImage: ImageProperty;
|
draggedImage: ImageProperty;
|
||||||
faTrash = faTrash;
|
faTrash = faTrash;
|
||||||
data: CommercialPropertyListing;
|
data: CommercialPropertyListing;
|
||||||
|
typesOfBusiness = [];
|
||||||
constructor(
|
constructor(
|
||||||
public selectOptions: SelectOptionsService,
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
|
@ -109,10 +110,13 @@ export class EditBusinessListingComponent {
|
||||||
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.typesOfBusiness = selectOptions.typesOfBusiness.map(e => {
|
||||||
|
return { name: e.name, value: parseInt(e.value) };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business'));
|
||||||
} else {
|
} else {
|
||||||
this.listing = createGenericObject<BusinessListing>();
|
this.listing = createGenericObject<BusinessListing>();
|
||||||
this.listing.listingsCategory = 'business';
|
this.listing.listingsCategory = 'business';
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
||||||
<p-dropdown
|
<p-dropdown
|
||||||
id="type"
|
id="type"
|
||||||
[options]="selectOptions?.typesOfCommercialProperty"
|
[options]="typesOfCommercialProperty"
|
||||||
[(ngModel)]="listing.type"
|
[(ngModel)]="listing.type"
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
optionValue="value"
|
optionValue="value"
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
[maxFileSize]="maxFileSize"
|
[maxFileSize]="maxFileSize"
|
||||||
(onSelect)="select($event)"
|
(onSelect)="select($event)"
|
||||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
||||||
[disabled]="true"
|
[disabled]="!listing.id"
|
||||||
>
|
>
|
||||||
</p-fileUpload>
|
</p-fileUpload>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ export class EditCommercialPropertyListingComponent {
|
||||||
suggestions: string[] | undefined;
|
suggestions: string[] | undefined;
|
||||||
data: BusinessListing;
|
data: BusinessListing;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
typesOfCommercialProperty = [];
|
||||||
constructor(
|
constructor(
|
||||||
public selectOptions: SelectOptionsService,
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
|
@ -103,7 +104,6 @@ export class EditCommercialPropertyListingComponent {
|
||||||
private confirmationService: ConfirmationService,
|
private confirmationService: ConfirmationService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
// this.user = this.userService.getUser();
|
|
||||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
|
|
@ -115,10 +115,13 @@ export class EditCommercialPropertyListingComponent {
|
||||||
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.typesOfCommercialProperty = selectOptions.typesOfCommercialProperty.map(e => {
|
||||||
|
return { name: e.name, value: parseInt(e.value) };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'));
|
||||||
} else {
|
} else {
|
||||||
this.listing = createGenericObject<CommercialPropertyListing>();
|
this.listing = createGenericObject<CommercialPropertyListing>();
|
||||||
this.listing.userId = await this.userService.getId();
|
this.listing.userId = await this.userService.getId();
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
|
||||||
<menu-account></menu-account>
|
|
||||||
<p-toast></p-toast>
|
|
||||||
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
|
||||||
<div class="text-900 font-semibold text-lg mt-3">{{ mode === 'create' ? 'New' : 'Edit' }} Listing</div>
|
|
||||||
<p-divider></p-divider>
|
|
||||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
|
||||||
<div class="flex-auto p-fluid">
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
|
||||||
<p-dropdown
|
|
||||||
id="listingCategory"
|
|
||||||
[options]="selectOptions?.listingCategories"
|
|
||||||
[(ngModel)]="listing.listingsCategory"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
placeholder="Listing category"
|
|
||||||
[disabled]="mode === 'edit'"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
|
||||||
<input id="email" type="text" pInputText [(ngModel)]="listing.title" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
|
||||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
|
||||||
<ng-template pTemplate="header"></ng-template>
|
|
||||||
</p-editor>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (listing.listingsCategory==='business'){
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
|
||||||
<p-dropdown
|
|
||||||
id="type"
|
|
||||||
[options]="selectOptions?.typesOfBusiness"
|
|
||||||
[(ngModel)]="listing.type"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
[showClear]="true"
|
|
||||||
placeholder="Type of business"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
} @if (listing.listingsCategory==='commercialProperty'){
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
|
||||||
<p-dropdown
|
|
||||||
id="type"
|
|
||||||
[options]="selectOptions?.typesOfCommercialProperty"
|
|
||||||
[(ngModel)]="listing.type"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
[showClear]="true"
|
|
||||||
placeholder="Property Category"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">State</label>
|
|
||||||
<p-dropdown
|
|
||||||
id="listingCategory"
|
|
||||||
[options]="selectOptions?.states"
|
|
||||||
[(ngModel)]="listing.state"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
[showClear]="true"
|
|
||||||
placeholder="State"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">City</label>
|
|
||||||
<p-autoComplete [(ngModel)]="listing.city" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (listing.listingsCategory==='commercialProperty'){
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="zipCode" class="block font-medium text-900 mb-2">Zip Code</label>
|
|
||||||
<input id="zipCode" type="text" pInputText [(ngModel)]="listing.zipCode" />
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="county" class="block font-medium text-900 mb-2">County</label>
|
|
||||||
<input id="county" type="text" pInputText [(ngModel)]="listing.county" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p-divider></p-divider>
|
|
||||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
|
||||||
<div class="flex-auto p-fluid">
|
|
||||||
@if (listing.listingsCategory==='commercialProperty'){
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
|
||||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<div class="flex flex-column align-items-center flex-or">
|
|
||||||
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
|
||||||
<p-fileUpload
|
|
||||||
mode="basic"
|
|
||||||
chooseLabel="Upload"
|
|
||||||
[customUpload]="true"
|
|
||||||
name="file"
|
|
||||||
accept="image/*"
|
|
||||||
[maxFileSize]="maxFileSize"
|
|
||||||
(onSelect)="select($event)"
|
|
||||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
|
||||||
>
|
|
||||||
</p-fileUpload>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
|
||||||
@for (image of propertyImages; track image) {
|
|
||||||
<span cdkDropList mixedCdkDropList>
|
|
||||||
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
|
||||||
<img src="{{ environment.apiBaseUrl }}/property/{{ listing.id }}/{{ image.name }}" [alt]="image.name" class="shadow-2" cdkDrag />
|
|
||||||
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image.name)"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @if (listing.listingsCategory==='business'){
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
|
||||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
|
||||||
<app-inputNumber mode="currency" currency="USD" inputId="salesRevenue" [(ngModel)]="listing.salesRevenue"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
|
||||||
<app-inputNumber mode="currency" currency="USD" inputId="cashFlow" [(ngModel)]="listing.cashFlow"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="employees" class="block font-medium text-900 mb-2">Years Established Since</label>
|
|
||||||
<app-inputNumber mode="decimal" inputId="established" [(ngModel)]="listing.established"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
|
||||||
<app-inputNumber mode="decimal" inputId="employees" [(ngModel)]="listing.employees"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-4">
|
|
||||||
<p-checkbox [binary]="true" [(ngModel)]="listing.realEstateIncluded"></p-checkbox>
|
|
||||||
<span class="ml-2 text-900">Real Estate Included</span>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-4">
|
|
||||||
<p-checkbox [binary]="true" [(ngModel)]="listing.leasedLocation"></p-checkbox>
|
|
||||||
<span class="ml-2 text-900">Leased Location</span>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-4">
|
|
||||||
<p-checkbox [binary]="true" [(ngModel)]="listing.franchiseResale"></p-checkbox>
|
|
||||||
<span class="ml-2 text-900">Franchise Re-Sale</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="supportAndTraining" class="block font-medium text-900 mb-2">Support & Training</label>
|
|
||||||
<input id="supportAndTraining" type="text" pInputText [(ngModel)]="listing.supportAndTraining" />
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="reasonForSale" class="block font-medium text-900 mb-2">Reason for Sale</label>
|
|
||||||
<textarea id="reasonForSale" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.reasonForSale"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="brokerLicensing" class="block font-medium text-900 mb-2">Broker Licensing</label>
|
|
||||||
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing" />
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="internalListingNumber" class="block font-medium text-900 mb-2">Internal Listing Number</label>
|
|
||||||
<app-inputNumber mode="decimal" inputId="internalListingNumber" type="text" [(ngModel)]="listing.internalListingNumber"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="internalListing" class="block font-medium text-900 mb-2">Internal Notes (Will not be shown on the listing, for your records only.)</label>
|
|
||||||
<input id="internalListing" type="text" pInputText [(ngModel)]="listing.internals" />
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<p-inputSwitch inputId="draft" [(ngModel)]="listing.draft"></p-inputSwitch>
|
|
||||||
<span class="ml-2 text-900 absolute translate-y-5">Draft Mode (Will not be shown as public listing)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div>
|
|
||||||
@if (mode==='create'){
|
|
||||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
} @else {
|
|
||||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p-toast></p-toast>
|
|
||||||
<p-confirmDialog></p-confirmDialog> -->
|
|
||||||
|
|
@ -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 */
|
|
||||||
}
|
|
||||||
|
|
@ -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<BusinessListing>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
import { Component } from '@angular/core';
|
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 { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.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({
|
@Component({
|
||||||
selector: 'app-favorites',
|
selector: 'app-favorites',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MenuAccountComponent, SharedModule],
|
imports: [MenuAccountComponent, SharedModule],
|
||||||
templateUrl: './favorites.component.html',
|
templateUrl: './favorites.component.html',
|
||||||
styleUrl: './favorites.component.scss'
|
styleUrl: './favorites.component.scss',
|
||||||
})
|
})
|
||||||
export class FavoritesComponent {
|
export class FavoritesComponent {
|
||||||
user: User;
|
user: User;
|
||||||
listings: Array<ListingType> =[]//= dataListings as unknown as Array<BusinessListing>;
|
listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>;
|
||||||
favorites: Array<ListingType>
|
favorites: Array<ListingType>;
|
||||||
constructor(public userService: UserService, private listingsService: ListingsService, public selectOptions: SelectOptionsService) {
|
constructor(public userService: UserService, private listingsService: ListingsService, public selectOptions: SelectOptionsService) {
|
||||||
this.user=this.userService.getUser();
|
this.user = this.userService.getKeycloakUser();
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
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 { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
|
@ -18,7 +19,7 @@ import { MenuAccountComponent } from '../../menu-account/menu-account.component'
|
||||||
export class MyListingComponent {
|
export class MyListingComponent {
|
||||||
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||||
myListings: Array<ListingType>;
|
myListings: Array<ListingType>;
|
||||||
userId: string;
|
user: User;
|
||||||
isBusinessListing = isBusinessListing;
|
isBusinessListing = isBusinessListing;
|
||||||
isCommercialPropertyListing = isCommercialPropertyListing;
|
isCommercialPropertyListing = isCommercialPropertyListing;
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -30,13 +31,15 @@ export class MyListingComponent {
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
) {}
|
) {}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.userId = await this.userService.getId();
|
const keycloakUser = this.userService.getKeycloakUser();
|
||||||
this.myListings = await this.listingsService.getListingByUserId(this.userId);
|
const email = keycloakUser.email;
|
||||||
|
this.user = await this.userService.getByMail(email);
|
||||||
|
this.myListings = await this.listingsService.getListingByUserId(this.user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteListing(listing: ListingType) {
|
async deleteListing(listing: ListingType) {
|
||||||
await this.listingsService.deleteListing(listing.id, getListingType(listing));
|
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) {
|
confirm(event: Event, listing: ListingType) {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export class UserService {
|
||||||
isLoggedIn(): boolean {
|
isLoggedIn(): boolean {
|
||||||
return this.$isLoggedIn();
|
return this.$isLoggedIn();
|
||||||
}
|
}
|
||||||
getUser(): User {
|
getKeycloakUser(): User {
|
||||||
return this.user;
|
return this.user;
|
||||||
}
|
}
|
||||||
getUserObservable(): Observable<User> {
|
getUserObservable(): Observable<User> {
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,74 @@
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
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 { ButtonModule } from 'primeng/button';
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
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 { 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 { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
||||||
import { ToastModule } from 'primeng/toast';
|
import { DividerModule } from 'primeng/divider';
|
||||||
import { AutoCompleteModule } from 'primeng/autocomplete';
|
import { DropdownModule } from 'primeng/dropdown';
|
||||||
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
|
import { InputNumberModule } from 'primeng/inputnumber';
|
||||||
import { InputSwitchModule } from 'primeng/inputswitch';
|
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({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
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: [
|
exports: [
|
||||||
CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule,RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule, AutoCompleteModule, TagModule,InputSwitchModule
|
CommonModule,
|
||||||
]
|
StyleClassModule,
|
||||||
|
DividerModule,
|
||||||
|
ButtonModule,
|
||||||
|
TableModule,
|
||||||
|
InputTextModule,
|
||||||
|
DropdownModule,
|
||||||
|
FormsModule,
|
||||||
|
ChipModule,
|
||||||
|
InputTextareaModule,
|
||||||
|
RouterModule,
|
||||||
|
FontAwesomeModule,
|
||||||
|
MenuAccountComponent,
|
||||||
|
InputNumberModule,
|
||||||
|
ConfirmDialogModule,
|
||||||
|
ConfirmPopupModule,
|
||||||
|
ToastModule,
|
||||||
|
CheckboxModule,
|
||||||
|
AutoCompleteModule,
|
||||||
|
TagModule,
|
||||||
|
InputSwitchModule,
|
||||||
|
InputGroupModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SharedModule {}
|
export class SharedModule {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue