BugFixes image upload, image display, new DB structure for areasServed, licenedIn

This commit is contained in:
Andreas Knuth 2024-05-13 17:31:01 -05:00
parent 5230ef1230
commit aff55c5433
34 changed files with 326 additions and 1131 deletions

View File

@ -5,7 +5,7 @@ import { join } from 'path';
import pkg from 'pg';
import { rimraf } from 'rimraf';
import sharp from 'sharp';
import { BusinessListing, CommercialPropertyListing, User } from 'src/models/db.model.js';
import { BusinessListing, CommercialPropertyListing, User, UserData } from 'src/models/db.model.js';
import * as schema from './schema.js';
const { Pool } = pkg;
@ -22,9 +22,9 @@ await db.delete(schema.users);
//Broker
let filePath = `./data/broker.json`;
let data: string = readFileSync(filePath, 'utf8');
const userData: User[] = JSON.parse(data); // Erwartet ein Array von Objekten
const usersData: UserData[] = JSON.parse(data); // Erwartet ein Array von Objekten
const generatedUserData = [];
console.log(userData.length);
console.log(usersData.length);
let i = 0,
male = 0,
female = 0;
@ -32,11 +32,32 @@ const targetPathProfile = `./pictures/profile`;
deleteFilesOfDir(targetPathProfile);
const targetPathLogo = `./pictures/logo`;
deleteFilesOfDir(targetPathLogo);
for (const user of userData) {
delete user.id;
user.licensedIn = user.licensedIn.map(l => `${l['name']}|${l['value']}`);
for (const userData of usersData) {
const user: User = { firstname: '', lastname: '', email: '' };
user.licensedIn = [];
userData.licensedIn.forEach(l => {
console.log(l['value'], l['name']);
user.licensedIn.push({ registerNo: l['value'], state: l['name'] });
});
user.areasServed = [];
user.areasServed = userData.areasServed.map(l => {
return { county: l.split(',')[0].trim(), state: l.split(',')[1].trim() };
});
user.hasCompanyLogo = true;
user.hasProfile = true;
user.firstname = userData.firstname;
user.lastname = userData.lastname;
user.email = userData.email;
user.phoneNumber = userData.phoneNumber;
user.description = userData.description;
user.companyName = userData.companyName;
user.companyOverview = userData.companyOverview;
user.companyWebsite = userData.companyWebsite;
user.companyLocation = userData.companyLocation;
user.offeredServices = userData.offeredServices;
user.gender = userData.gender;
user.created = new Date();
user.updated = new Date();
const u = await db.insert(schema.users).values(user).returning({ insertedId: schema.users.id, gender: schema.users.gender });
generatedUserData.push(u[0].insertedId);
i++;
@ -46,7 +67,7 @@ for (const user of userData) {
await storeProfilePicture(data, u[0].insertedId);
} else {
female++;
const data = readFileSync(`./pictures/profile_base/Frau_${male}.jpg`);
const data = readFileSync(`./pictures/profile_base/Frau_${female}.jpg`);
await storeProfilePicture(data, u[0].insertedId);
}
const data = readFileSync(`./pictures/logos_base/${i}.jpg`);
@ -60,6 +81,7 @@ const businessJsonData = JSON.parse(data) as BusinessListing[]; // Erwartet ein
for (const business of businessJsonData) {
delete business.id;
business.created = new Date(business.created);
business.updated = new Date(business.created);
business.userId = getRandomItem(generatedUserData);
await db.insert(schema.businesses).values(business);
}
@ -73,7 +95,9 @@ for (const commercial of commercialJsonData) {
commercial.imageOrder = getFilenames(id);
commercial.imagePath = id;
commercial.created = getRandomDateWithinLastYear();
const insertionDate = getRandomDateWithinLastYear();
commercial.created = insertionDate;
commercial.updated = insertionDate;
commercial.userId = getRandomItem(generatedUserData);
await db.insert(schema.commercials).values(commercial);
}

View File

@ -1,3 +1,9 @@
DO $$ BEGIN
CREATE TYPE "gender" AS ENUM('male', 'female');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "businesses" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"userId" uuid,
@ -38,6 +44,7 @@ CREATE TABLE IF NOT EXISTS "commercials" (
"state" char(2),
"price" double precision,
"favoritesForUser" varchar(30)[],
"listingsCategory" varchar(255),
"hideImage" boolean,
"draft" boolean,
"zipCode" integer,
@ -45,7 +52,7 @@ CREATE TABLE IF NOT EXISTS "commercials" (
"email" varchar(255),
"website" varchar(255),
"phoneNumber" varchar(255),
"imageOrder" varchar(30)[],
"imageOrder" varchar(200)[],
"imagePath" varchar(50),
"created" timestamp,
"updated" timestamp,
@ -65,10 +72,11 @@ CREATE TABLE IF NOT EXISTS "users" (
"companyWebsite" varchar(255),
"companyLocation" varchar(255),
"offeredServices" text,
"areasServed" varchar(100)[],
"areasServed" jsonb,
"hasProfile" boolean,
"hasCompanyLogo" boolean,
"licensedIn" varchar(50)[]
"licensedIn" jsonb,
"gender" "gender"
);
--> statement-breakpoint
DO $$ BEGIN

View File

@ -0,0 +1,2 @@
ALTER TABLE "users" ADD COLUMN "created" timestamp;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "updated" timestamp;

View File

@ -1 +0,0 @@
ALTER TABLE "commercials" ALTER COLUMN "imageOrder" SET DATA TYPE varchar(200)[];

View File

@ -1,7 +0,0 @@
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";

View File

@ -1 +0,0 @@
ALTER TABLE "commercials" ADD COLUMN "listingsCategory" varchar(255);

View File

@ -1,5 +1,5 @@
{
"id": "f6d421f9-2394-4a1c-9268-9e46285f0a41",
"id": "98e2be90-3301-49a8-b323-78d9d8f79cb5",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
@ -250,6 +250,12 @@
"primaryKey": false,
"notNull": false
},
"listingsCategory": {
"name": "listingsCategory",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"hideImage": {
"name": "hideImage",
"type": "boolean",
@ -294,7 +300,7 @@
},
"imageOrder": {
"name": "imageOrder",
"type": "varchar(30)[]",
"type": "varchar(200)[]",
"primaryKey": false,
"notNull": false
},
@ -421,7 +427,7 @@
},
"areasServed": {
"name": "areasServed",
"type": "varchar(100)[]",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
@ -439,7 +445,13 @@
},
"licensedIn": {
"name": "licensedIn",
"type": "varchar(50)[]",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"gender": {
"name": "gender",
"type": "gender",
"primaryKey": false,
"notNull": false
}
@ -450,7 +462,15 @@
"uniqueConstraints": {}
}
},
"enums": {},
"enums": {
"gender": {
"name": "gender",
"values": {
"male": "male",
"female": "female"
}
}
},
"schemas": {},
"_meta": {
"columns": {},

View File

@ -1,6 +1,6 @@
{
"id": "3e4b8c5f-4474-4877-abec-38283408ee34",
"prevId": "f6d421f9-2394-4a1c-9268-9e46285f0a41",
"id": "41802273-1335-433f-97cb-77774ddb3362",
"prevId": "98e2be90-3301-49a8-b323-78d9d8f79cb5",
"version": "5",
"dialect": "pg",
"tables": {
@ -250,6 +250,12 @@
"primaryKey": false,
"notNull": false
},
"listingsCategory": {
"name": "listingsCategory",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"hideImage": {
"name": "hideImage",
"type": "boolean",
@ -421,7 +427,7 @@
},
"areasServed": {
"name": "areasServed",
"type": "varchar(100)[]",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
@ -439,7 +445,25 @@
},
"licensedIn": {
"name": "licensedIn",
"type": "varchar(50)[]",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"gender": {
"name": "gender",
"type": "gender",
"primaryKey": false,
"notNull": false
},
"created": {
"name": "created",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"updated": {
"name": "updated",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
@ -450,7 +474,15 @@
"uniqueConstraints": {}
}
},
"enums": {},
"enums": {
"gender": {
"name": "gender",
"values": {
"male": "male",
"female": "female"
}
}
},
"schemas": {},
"_meta": {
"columns": {},

View File

@ -1,474 +0,0 @@
{
"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": {}
}
}

View File

@ -1,480 +0,0 @@
{
"id": "da786c6a-fd5f-4629-bd5e-3ecd42ab1f2c",
"prevId": "ad48c6eb-2d04-442f-9242-b6765553c7c4",
"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
},
"listingsCategory": {
"name": "listingsCategory",
"type": "varchar(255)",
"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": {}
}
}

View File

@ -5,29 +5,15 @@
{
"idx": 0,
"version": "5",
"when": 1714913766996,
"tag": "0000_third_spacker_dave",
"when": 1715627517508,
"tag": "0000_open_hannibal_king",
"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
},
{
"idx": 3,
"version": "5",
"when": 1715254754561,
"tag": "0003_tough_hobgoblin",
"when": 1715631674334,
"tag": "0001_charming_thundra",
"breakpoints": true
}
]

View File

@ -1,4 +1,5 @@
import { boolean, char, doublePrecision, integer, pgEnum, pgTable, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
import { boolean, char, doublePrecision, integer, jsonb, pgEnum, pgTable, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
import { AreasServed, LicensedIn } from 'src/models/db.model';
export const PG_CONNECTION = 'PG_CONNECTION';
export const genderEnum = pgEnum('gender', ['male', 'female']);
@ -14,11 +15,13 @@ export const users = pgTable('users', {
companyWebsite: varchar('companyWebsite', { length: 255 }),
companyLocation: varchar('companyLocation', { length: 255 }),
offeredServices: text('offeredServices'),
areasServed: varchar('areasServed', { length: 100 }).array(),
areasServed: jsonb('areasServed').$type<AreasServed[]>(),
hasProfile: boolean('hasProfile'),
hasCompanyLogo: boolean('hasCompanyLogo'),
licensedIn: varchar('licensedIn', { length: 50 }).array(),
licensedIn: jsonb('licensedIn').$type<LicensedIn[]>(),
gender: genderEnum('gender'),
created: timestamp('created'),
updated: timestamp('updated'),
});
export const businesses = pgTable('businesses', {

View File

@ -6,8 +6,8 @@ import { FileService } from '../file/file.service.js';
import { ListingsService } from '../listings/listings.service.js';
import { SelectOptionsService } from '../select-options/select-options.service.js';
import { commercials } from 'src/drizzle/schema.js';
import { CommercialPropertyListing } from 'src/models/db.model.js';
import { commercials } from '../drizzle/schema.js';
import { CommercialPropertyListing } from '../models/db.model.js';
@Controller('image')
export class ImageController {

View File

@ -1,27 +1,23 @@
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 { Controller, Get, Inject, Param } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import { businesses, commercials } from 'src/drizzle/schema.js';
import { businesses, commercials } from '../drizzle/schema.js';
import { ListingsService } from './listings.service.js';
@Controller('listings/undefined')
export class UnknownListingsController {
constructor(private readonly listingsService:ListingsService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
}
constructor(
private readonly listingsService: ListingsService,
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
) {}
@Get(':id')
async findById(@Param('id') id:string): Promise<any> {
const result = await this.listingsService.findById(id,businesses);
if (result){
return result
async findById(@Param('id') id: string): Promise<any> {
const result = await this.listingsService.findById(id, businesses);
if (result) {
return result;
} else {
return await this.listingsService.findById(id,commercials);
return await this.listingsService.findById(id, commercials);
}
}
}

View File

@ -1,5 +1,12 @@
<p>Hey {{ name }},</p>
<p>You got an inquiry a</p>
<p>Dear {{ name }},</p>
<p>You got an inquiry regarding your '{{title}}'' listing</p>
<p>
{{inquiry}}
Buyers Information
</p>
<p> Contact Name: {{iname}}</p>
<p> Contact Phone: {{phone}}</p>
<p> Contact Mail: {{email}}</p>
<p> Contact Name: {{iname}}</p>
<p> Comments: {{inquiry}}</p>
<p>Internal Listing Number: {{internalListingNumber}}</p>

View File

@ -1,4 +1,24 @@
export interface User {
id?: string;
firstname: string;
lastname: string;
email: string;
phoneNumber?: string;
description?: string;
companyName?: string;
companyOverview?: string;
companyWebsite?: string;
companyLocation?: string;
offeredServices?: string;
areasServed?: AreasServed[];
hasProfile?: boolean;
hasCompanyLogo?: boolean;
licensedIn?: LicensedIn[];
gender?: 'male' | 'female';
created?: Date;
updated?: Date;
}
export interface UserData {
id?: string;
firstname: string;
lastname: string;
@ -14,8 +34,10 @@ export interface User {
hasProfile?: boolean;
hasCompanyLogo?: boolean;
licensedIn?: string[];
gender?: 'male' | 'female';
created?: Date;
updated?: Date;
}
export interface BusinessListing {
id: string;
userId?: string;
@ -71,3 +93,11 @@ export interface CommercialPropertyListing {
lastVisit?: Date;
listingsCategory?: string;
}
export interface AreasServed {
county: string;
state: string;
}
export interface LicensedIn {
registerNo: string;
state: string;
}

View File

@ -2,11 +2,11 @@ import { Inject, Injectable } from '@nestjs/common';
import { and, eq, ilike, or, sql } from 'drizzle-orm';
import { NodePgDatabase } from 'drizzle-orm/node-postgres/driver.js';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { PG_CONNECTION } from 'src/drizzle/schema.js';
import { User } from 'src/models/db.model.js';
import { Logger } from 'winston';
import * as schema from '../drizzle/schema.js';
import { PG_CONNECTION } from '../drizzle/schema.js';
import { FileService } from '../file/file.service.js';
import { User } from '../models/db.model.js';
import { ListingCriteria } from '../models/main.model.js';
@Injectable()
@ -48,9 +48,13 @@ export class UserService {
}
async saveUser(user: any): Promise<User> {
if (user.id) {
user.created = new Date(user.created);
user.updated = new Date();
const [updateUser] = await this.conn.update(schema.users).set(user).where(eq(schema.users.id, user.id)).returning();
return updateUser as User;
} else {
user.created = new Date();
user.updated = new Date();
const [newUser] = await this.conn.insert(schema.users).values(user).returning();
return newUser as User;
}

View File

@ -40,7 +40,7 @@
"on-change": "^5.0.1",
"primeflex": "^3.3.1",
"primeicons": "^6.0.1",
"primeng": "^17.10.0",
"primeng": "^17.16.1",
"quill": "^1.3.7",
"rxjs": "~7.8.1",
"tslib": "^2.3.0",

View File

@ -59,6 +59,11 @@ export const routes: Routes = [
component: AccountComponent,
canActivate: [authGuard],
},
{
path: 'account/:id',
component: AccountComponent,
canActivate: [authGuard],
},
// #########
// Create, Update Listings
{

View File

@ -1,7 +1,7 @@
<div class="surface-ground h-full">
<div class="px-6 py-5">
<div class="surface-card p-4 shadow-2 border-round">
<div class="flex justify-content-between align-items-center align-content-center">
<div class="flex justify-content-between align-items-center align-content-center mb-2">
<div class="font-medium text-3xl text-900 mb-3">{{ listing?.title }}</div>
<!-- <button pButton pRipple type="button" label="Go back to listings" icon="pi pi-user-plus" class="mr-3 p-button-rounded"></button> -->
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="back()"></p-button>
@ -40,17 +40,19 @@
<div class="text-900 w-full md:w-10">{{ listing.price | currency }}</div>
</li>
</ul>
@if(listing && user && (user.id===listing?.userId || isAdmin())){
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>
}
</div>
<div class="col-12 md:col-6">
<p-galleria [value]="propertyImages" [showIndicators]="true" [showThumbnails]="false" [responsiveOptions]="responsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5">
<ng-template pTemplate="item" let-item>
<img src="pictures/property/{{ listing.imagePath }}/{{ item }}" style="width: 100%" />
</ng-template>
</p-galleria>
@if(listing && user && (user.id===listing?.userId || isAdmin())){
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>
}
</div>
@if (mailinfo){
<div class="col-12 md:col-6">
<div class="surface-card p-4 border-round p-fluid">
<div class="font-medium text-xl text-primary text-900 mb-3">Contact The Author of This Listing</div>
<div class="font-italic text-sm text-900 mb-5">Please Include your contact info below:</div>
@ -80,9 +82,9 @@
</div>
<button pButton pRipple label="Submit" icon="pi pi-file" class="w-auto" (click)="mail()"></button>
</div>
</div>
}
</div>
</div>
}
</div>
</div>

View File

@ -10,7 +10,7 @@
<div class="flex align-items-start flex-column lg:flex-row lg:justify-content-between">
<div class="flex align-items-start flex-column md:flex-row">
@if(user.hasProfile){
<img src="pictures//profile/{{ user.id }}.avif" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
<img src="pictures//profile/{{ user.id }}.avif?_ts={{ ts }}" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
} @else {
<img src="assets/images/person_placeholder.jpg" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
}
@ -34,7 +34,7 @@
<!-- <span class="font-medium text-500">Logo</span> -->
<div>
@if(user.hasCompanyLogo){
<img src="pictures/logo/{{ user.id }}.avif" class="mr-5 lg:mb-0" style="height: 60px; max-width: 100px" />
<img src="pictures/logo/{{ user.id }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="height: 60px; max-width: 100px" />
}
<!-- <img *ngIf="!user.hasCompanyLogo" src="assets/images/placeholder.png"
class="mr-5 lg:mb-0" style="height:60px;max-width:100px" /> -->
@ -74,10 +74,10 @@
<div class="text-900 w-full md:w-10" [innerHTML]="offeredServices"></div>
</li>
<li class="flex align-items-center py-3 px-2 flex-wrap">
<div class="text-500 w-full md:w-2 font-medium">Areas we serve</div>
<div class="text-500 w-full md:w-2 font-medium">Areas (Counties) we serve</div>
<div class="text-900 w-full md:w-10">
@for (area of user.areasServed; track area) {
<p-tag styleClass="mr-2" [value]="area" [rounded]="true"></p-tag>
<p-tag styleClass="mr-2" value="{{ area.county }}-{{ area.state }}" [rounded]="true"></p-tag>
}
<!-- <p-tag styleClass="mr-2" severity="success" value="Javascript" [rounded]="true"></p-tag>
<p-tag styleClass="mr-2" severity="danger" value="Python" [rounded]="true"></p-tag>
@ -87,8 +87,8 @@
<li class="flex align-items-center py-3 px-2 flex-wrap">
<div class="text-500 w-full md:w-2 font-medium">Licensed In</div>
<div class="text-900 w-full md:w-10">
@for (license of userLicensedIn; track license) {
<div>{{ license.name }} : {{ license.value }}</div>
@for (license of user.licensedIn; track license) {
<div>{{ license.state }} {{ license.registerNo }}</div>
}
</div>
</li>
@ -123,7 +123,7 @@
<div class="p-3 border-1 surface-border border-round surface-card">
<div class="text-900 mb-2 flex align-items-center">
@if (listing.imageOrder?.length>0){
<img src="pictures/property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}" class="mr-3" style="width: 45px; height: 45px" />
<img src="pictures/property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}&_ts={{ ts }}" class="mr-3" style="width: 45px; height: 45px" />
} @else {
<img src="assets/images/placeholder_properties.jpg" class="mr-3" style="width: 45px; height: 45px" />
}
@ -141,7 +141,7 @@
</div>
</div>
@if( user?.id===(user$| async)?.id || isAdmin()){
<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', user.id]"></button>
}
</div>
}

View File

@ -6,7 +6,7 @@ import { MessageService } from 'primeng/api';
import { GalleriaModule } from 'primeng/galleria';
import { Observable } from 'rxjs';
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
import { KeyValue, ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
import { ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../../environments/environment';
import { ImageService } from '../../../services/image.service';
import { ListingsService } from '../../../services/listings.service';
@ -32,7 +32,7 @@ export class DetailsUserComponent {
commercialPropListings: CommercialPropertyListing[];
companyOverview: SafeHtml;
offeredServices: SafeHtml;
userLicensedIn: KeyValue[];
ts = new Date().getTime();
constructor(
private activatedRoute: ActivatedRoute,
private router: Router,
@ -47,9 +47,7 @@ export class DetailsUserComponent {
async ngOnInit() {
this.user = await this.userService.getById(this.id);
this.userLicensedIn = this.user.licensedIn.map(l => {
return { name: l.split('|')[0], value: l.split('|')[1] };
});
const results = await Promise.all([await this.listingsService.getListingByUserId(this.id, 'business'), await this.listingsService.getListingByUserId(this.id, 'commercialProperty')]);
// Zuweisen der Ergebnisse zu den Member-Variablen der Klasse
this.businessListings = results[0];

View File

@ -42,7 +42,7 @@
<div class="surface-card p-4 flex flex-column align-items-center md:flex-row md:align-items-stretch h-full">
<span>
@if(user.hasProfile){
<img src="pictures/profile/{{ user.id }}.avif" class="w-5rem" />
<img src="pictures/profile/{{ user.id }}.avif?_ts={{ ts }}" class="w-5rem" />
} @else {
<img src="assets/images/person_placeholder.jpg" class="w-5rem" />
}
@ -55,7 +55,7 @@
</div>
<div class="px-4 py-3 text-right flex justify-content-between align-items-center">
@if(user.hasCompanyLogo){
<img src="pictures/logo/{{ user.id }}.avif" class="rounded-image" />
<img src="pictures/logo/{{ user.id }}.avif?_ts={{ ts }}" class="rounded-image" />
} @else {
<img src="assets/images/placeholder.png" class="rounded-image" />
}
@ -65,7 +65,7 @@
</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 paginator-bar">
<div class="mx-1 text-color">Total number of Professionals/Brokers: {{ totalRecords }}</div>
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]"></p-paginator>
</div>

View File

@ -39,7 +39,7 @@ import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from
NgOptimizedImage,
],
templateUrl: './broker-listings.component.html',
styleUrl: './broker-listings.component.scss',
styleUrls: ['./broker-listings.component.scss', '../../pages.scss'],
})
export class BrokerListingsComponent {
environment = environment;
@ -71,10 +71,11 @@ export class BrokerListingsComponent {
private route: ActivatedRoute,
) {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined;
this.route.data.subscribe(async () => {
if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria);
} else {
this.first = this.criteria.page * this.criteria.length;
}
this.init();
});

View File

@ -62,8 +62,8 @@
<div class="grid">
@for (listing of listings; track listing.id) {
<div class="col-12 lg:col-3 p-3">
<div class="shadow-2 border-round surface-card mb-3 h-full flex-column justify-content-between flex">
<div class="p-4 h-full flex flex-column">
<div class="shadow-2 border-round surface-card h-full flex-column justify-content-between flex">
<div class="p-4 flex flex-column relative">
<div class="flex align-items-center">
<span [class]="selectOptions.getBgColorType(listing.type)" class="inline-flex border-circle align-items-center justify-content-center mr-3" style="width: 38px; height: 38px">
<i [class]="selectOptions.getIconAndTextColorType(listing.type)" class="pi text-xl"></i>
@ -76,7 +76,7 @@
<p class="mt-0 mb-1 text-700 line-height-3">Net profit: {{ listing.cashFlow | currency }}</p>
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{ selectOptions.getState(listing.state) }}</p>
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{ listing.established }}</p>
<div class="mt-auto ml-auto">
<div class="icon-pos">
<img src="pictures/logo/{{ listing.userId }}.avif" (error)="imageErrorHandler(listing)" class="rounded-image" />
</div>
</div>
@ -87,7 +87,7 @@
</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 paginator-bar">
<div class="mx-1 text-color">Total number of Listings: {{ totalRecords }}</div>
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]"></p-paginator>
</div>

View File

@ -10,13 +10,17 @@
}
::ng-deep p-paginator div {
background-color: var(--surface-200) !important;
// background-color: var(--surface-400) !important;
}
.icon-pos {
position: absolute;
bottom: 1.5rem; /* Gleich dem Padding des Containers */
right: 1.5rem; /* Gleich dem Padding des Containers */
}
.rounded-image {
border-radius: 6px;
// width: 100px;
max-width: 100px;
height: 45px;
height: 35px;
border: 1px solid rgba(0, 0, 0, 0.2);
padding: 1px 1px;
object-fit: contain;

View File

@ -39,7 +39,7 @@ import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from
TooltipModule,
],
templateUrl: './business-listings.component.html',
styleUrl: './business-listings.component.scss',
styleUrls: ['./business-listings.component.scss', '../../pages.scss'],
})
export class BusinessListingsComponent {
environment = environment;
@ -68,10 +68,11 @@ export class BusinessListingsComponent {
private route: ActivatedRoute,
) {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined;
this.route.data.subscribe(async () => {
if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria);
} else {
this.first = this.criteria.page * this.criteria.length;
}
this.init();
});

View File

@ -91,7 +91,7 @@
}
</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 paginator-bar">
<!-- @if(listings && listings.length>12){ -->
<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>

View File

@ -5,8 +5,8 @@
background-size: cover;
margin-bottom: -1px;
}
.search{
background-color: #343F69;
.search {
background-color: #343f69;
}
::ng-deep p-paginator div {
background-color: var(--surface-200) !important;
@ -17,7 +17,7 @@
// width: 100px;
max-width: 100px;
height: 45px;
border: 1px solid rgba(0,0,0,0.2);
border: 1px solid rgba(0, 0, 0, 0.2);
padding: 1px 1px;
object-fit: contain;
}

View File

@ -24,7 +24,7 @@ import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from
standalone: true,
imports: [CommonModule, StyleClassModule, ButtonModule, CheckboxModule, InputTextModule, DropdownModule, FormsModule, StyleClassModule, ToggleButtonModule, RouterModule, PaginatorModule, InputGroupModule],
templateUrl: './commercial-property-listings.component.html',
styleUrl: './commercial-property-listings.component.scss',
styleUrls: ['./commercial-property-listings.component.scss', '../../pages.scss'],
})
export class CommercialPropertyListingsComponent {
environment = environment;
@ -41,7 +41,6 @@ export class CommercialPropertyListingsComponent {
statesSet = new Set();
state: string;
totalRecords: number = 0;
ts = new Date().getTime();
constructor(
public selectOptions: SelectOptionsService,
@ -53,10 +52,11 @@ export class CommercialPropertyListingsComponent {
private route: ActivatedRoute,
) {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined;
this.route.data.subscribe(async () => {
if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria);
} else {
this.first = this.criteria.page * this.criteria.length;
}
this.init();
});

View File

@ -4,3 +4,13 @@
height: 100%;
margin: auto;
}
:host ::ng-deep .paginator-bar {
position: sticky;
bottom: 0px;
& > button {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Schatten-/Unschärfe-Effekt */
}
& .p-paginator {
padding: 0;
}
}

View File

@ -62,17 +62,13 @@
<div class="mb-4">
<label for="areasServed" class="block font-medium text-900 mb-2">Areas We Serve</label>
<textarea id="areasServed" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="user.areasServed"></textarea>
</div>
<div>
<label for="companyOverview" class="block font-medium text-900 mb-2">Licensed In</label>
@for (licensedIn of userLicensedIn; track licensedIn.value){
@for (areasServed of user.areasServed; track areasServed){
<div class="grid">
<div class="flex col-12 md:col-6">
<p-dropdown
id="states"
[options]="selectOptions?.states"
[(ngModel)]="licensedIn.name"
[(ngModel)]="areasServed.state"
optionLabel="name"
optionValue="value"
[showClear]="true"
@ -81,7 +77,35 @@
></p-dropdown>
</div>
<div class="flex col-12 md:col-6">
<input id="companyWebsite" type="text" pInputText [(ngModel)]="licensedIn.value" placeholder="Licence Number" />
<input id="county" type="text" pInputText [(ngModel)]="areasServed.county" placeholder="Area/County Served" />
</div>
</div>
}
</div>
<div class="field mb-5 col-12 md:col-6 flex align-items-center">
<p-button class="mr-1" icon="pi pi-plus" severity="success" (click)="addArea()"></p-button>
<p-button icon="pi pi-minus" severity="danger" (click)="removeArea()" [disabled]="user.areasServed?.length < 2"></p-button>
<span class="text-xs">&nbsp;(Add more Areas or remove existing ones.)</span>
<!-- <button pButton pRipple label="Add Licence" class="w-auto" (click)="addLicence()"></button> -->
</div>
<div>
<label for="companyOverview" class="block font-medium text-900 mb-2">Licensed In</label>
@for (licensedIn of user.licensedIn; track licensedIn){
<div class="grid">
<div class="flex col-12 md:col-6">
<p-dropdown
id="states"
[options]="selectOptions?.states"
[(ngModel)]="licensedIn.state"
optionLabel="name"
optionValue="value"
[showClear]="true"
placeholder="State"
[ngStyle]="{ width: '100%' }"
></p-dropdown>
</div>
<div class="flex col-12 md:col-6">
<input id="registerNo" type="text" pInputText [(ngModel)]="licensedIn.registerNo" placeholder="Licence Number" />
</div>
</div>
}

View File

@ -10,7 +10,7 @@ import { FileUpload, FileUploadModule } from 'primeng/fileupload';
import { SelectButtonModule } from 'primeng/selectbutton';
import { lastValueFrom } from 'rxjs';
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
import { AutoCompleteCompleteEvent, Invoice, KeyValue, Subscription } from '../../../../../../bizmatch-server/src/models/main.model';
import { AutoCompleteCompleteEvent, Invoice, Subscription } from '../../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../../environments/environment';
import { ImageCropperComponent, stateOptions } from '../../../components/image-cropper/image-cropper.component';
import { GeoService } from '../../../services/geo.service';
@ -36,14 +36,13 @@ export class AccountComponent {
user: User;
subscriptions: Array<Subscription>;
userSubscriptions: Array<Subscription> = [];
maxFileSize = 1000000;
maxFileSize = 15000000;
companyLogoUrl: string;
profileUrl: string;
type: 'company' | 'profile';
dialogRef: DynamicDialogRef | undefined;
environment = environment;
editorModules = TOOLBAR_OPTIONS;
userLicensedIn: KeyValue[];
constructor(
public userService: UserService,
private subscriptionService: SubscriptionsService,
@ -57,30 +56,26 @@ export class AccountComponent {
public dialogService: DialogService,
) {}
async ngOnInit() {
if (this.id) {
this.user = await this.userService.getById(this.id);
} else {
const keycloakUser = this.userService.getKeycloakUser();
const email = keycloakUser.email;
try {
this.user = await this.userService.getByMail(email);
} catch (e) {
this.user = { email, firstname: keycloakUser.firstname, lastname: keycloakUser.lastname };
this.user = { email, firstname: keycloakUser.firstname, lastname: keycloakUser.lastname, areasServed: [], licensedIn: [] };
this.user = await this.userService.save(this.user);
}
this.userLicensedIn = this.user.licensedIn
? this.user.licensedIn.map(l => {
return { name: l.split('|')[0], value: l.split('|')[1] };
})
: [];
this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions());
if (!this.user.licensedIn || this.user.licensedIn?.length === 0) {
this.user.licensedIn = [''];
}
this.profileUrl = this.user.hasProfile ? `pictures/profile/${this.user.id}.avif` : `/assets/images/placeholder.png`;
this.companyLogoUrl = this.user.hasCompanyLogo ? `pictures/logo/${this.user.id}.avif` : `/assets/images/placeholder.png`;
this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions());
this.profileUrl = this.user.hasProfile ? `/pictures/profile/${this.user.id}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
this.companyLogoUrl = this.user.hasCompanyLogo ? `/pictures/logo/${this.user.id}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
}
printInvoice(invoice: Invoice) {}
async updateProfile(user: User) {
this.user.licensedIn = this.userLicensedIn.map(l => `${l.name}|${l.value}`);
await this.userService.save(this.user);
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Acount changes have been persisted', life: 3000 });
}
@ -104,12 +99,17 @@ export class AccountComponent {
this.suggestions = result.map(r => `${r.city} - ${r.state_code}`).slice(0, 5);
}
addLicence() {
this.userLicensedIn.push({ name: '', value: '' });
this.user.licensedIn.push({ registerNo: '', state: '' });
}
removeLicence() {
this.userLicensedIn.splice(this.user.licensedIn.length - 2, 1);
this.user.licensedIn.splice(this.user.licensedIn.length - 1, 1);
}
addArea() {
this.user.areasServed.push({ county: '', state: '' });
}
removeArea() {
this.user.areasServed.splice(this.user.areasServed.length - 1, 1);
}
select(event: any, type: 'company' | 'profile') {
const imageUrl = URL.createObjectURL(event.files[0]);
this.type = type;
@ -142,11 +142,12 @@ export class AccountComponent {
this.loadingService.stopLoading('uploadImage');
if (this.type === 'company') {
this.user.hasCompanyLogo = true; //
this.companyLogoUrl = `${environment.apiBaseUrl}/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`;
this.companyLogoUrl = `pictures/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`;
} else {
this.user.hasProfile = true;
this.profileUrl = `${environment.apiBaseUrl}/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`;
this.profileUrl = `pictures/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`;
}
await this.userService.save(this.user);
}
},
error => console.error('Fehler beim Upload:', error),

View File

@ -66,27 +66,27 @@
<div class="mb-4 col-12 md:col-6">
<label for="price" class="block font-medium text-900 mb-2">Price</label>
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
<p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></p-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>
<p-inputNumber mode="currency" currency="USD" inputId="salesRevenue" [(ngModel)]="listing.salesRevenue"></p-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>
<p-inputNumber mode="currency" currency="USD" inputId="cashFlow" [(ngModel)]="listing.cashFlow"></p-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>
<label for="established" class="block font-medium text-900 mb-2">Years Established Since</label>
<p-inputNumber mode="decimal" inputId="established" [(ngModel)]="listing.established"></p-inputNumber>
</div>
<div class="mb-4 col-12 md:col-6">
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
<app-inputNumber mode="decimal" inputId="employees" [(ngModel)]="listing.employees"></app-inputNumber>
<p-inputNumber mode="decimal" inputId="employees" [(ngModel)]="listing.employees"></p-inputNumber>
</div>
</div>
<div class="grid">
@ -119,7 +119,7 @@
</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>
<p-inputNumber mode="decimal" inputId="internalListingNumber" type="text" [(ngModel)]="listing.internalListingNumber"></p-inputNumber>
</div>
</div>
<div class="mb-4">