images based on http-server, filter dropdowns

This commit is contained in:
Andreas Knuth 2024-05-10 17:19:36 +02:00
parent d508415de4
commit 5230ef1230
21 changed files with 224 additions and 149 deletions

View File

@ -1,38 +1,30 @@
import { MiddlewareConsumer, Module } from '@nestjs/common'; import { MiddlewareConsumer, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { WinstonModule, utilities as nestWinstonModuleUtilities } from 'nest-winston';
import path from 'path';
import { fileURLToPath } from 'url';
import * as winston from 'winston';
import { AppController } from './app.controller.js'; import { AppController } from './app.controller.js';
import { AppService } from './app.service.js'; import { AppService } from './app.service.js';
import { FileService } from './file/file.service.js';
import { AuthService } from './auth/auth.service.js';
import { AuthController } from './auth/auth.controller.js';
import { ConfigModule } from '@nestjs/config';
import { SelectOptionsController } from './select-options/select-options.controller.js';
import { SelectOptionsService } from './select-options/select-options.service.js';
import { SubscriptionsController } from './subscriptions/subscriptions.controller.js';
import { RedisModule } from './redis/redis.module.js';
import { ListingsService } from './listings/listings.service.js';
import { ServeStaticModule } from '@nestjs/serve-static';
import path, { join } from 'path';
import { fileURLToPath } from 'url';
import { utilities as nestWinstonModuleUtilities, WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import { MailModule } from './mail/mail.module.js';
import { AuthModule } from './auth/auth.module.js'; import { AuthModule } from './auth/auth.module.js';
import { FileService } from './file/file.service.js';
import { GeoModule } from './geo/geo.module.js'; import { GeoModule } from './geo/geo.module.js';
import { UserModule } from './user/user.module.js';
import { ListingsModule } from './listings/listings.module.js';
import { SelectOptionsModule } from './select-options/select-options.module.js';
import { CommercialPropertyListingsController } from './listings/commercial-property-listings.controller.js';
import { ImageModule } from './image/image.module.js'; import { ImageModule } from './image/image.module.js';
import { ListingsModule } from './listings/listings.module.js';
import { MailModule } from './mail/mail.module.js';
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware.js'; import { RequestDurationMiddleware } from './request-duration/request-duration.middleware.js';
import { SelectOptionsModule } from './select-options/select-options.module.js';
import { SubscriptionsController } from './subscriptions/subscriptions.controller.js';
import { UserModule } from './user/user.module.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: [ConfigModule.forRoot({isGlobal: true}), MailModule, AuthModule, imports: [
ServeStaticModule.forRoot({ ConfigModule.forRoot({ isGlobal: true }),
rootPath: join(__dirname, '../..', 'pictures'), // `public` ist das Verzeichnis, wo Ihre statischen Dateien liegen MailModule,
}), AuthModule,
WinstonModule.forRoot({ WinstonModule.forRoot({
transports: [ transports: [
new winston.transports.Console({ new winston.transports.Console({
@ -53,7 +45,6 @@ const __dirname = path.dirname(__filename);
UserModule, UserModule,
ListingsModule, ListingsModule,
SelectOptionsModule, SelectOptionsModule,
RedisModule,
ImageModule, ImageModule,
], ],
controllers: [AppController, SubscriptionsController], controllers: [AppController, SubscriptionsController],

View File

@ -1,20 +1,15 @@
import { Injectable, Module, OnModuleInit } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { BusinessListingsController } from './business-listings.controller.js';
import { ListingsService } from './listings.service.js';
import { CommercialPropertyListingsController } from './commercial-property-listings.controller.js';
import { RedisModule } from '../redis/redis.module.js';
import { FileService } from '../file/file.service.js';
import { UnknownListingsController } from './unknown-listings.controller.js';
import { UserModule } from '../user/user.module.js';
import { BrokerListingsController } from './broker-listings.controller.js';
import { UserService } from '../user/user.service.js';
import { Client, Connection } from 'pg';
import { drizzle } from 'drizzle-orm/node-postgres';
import { DrizzleModule } from '../drizzle/drizzle.module.js'; import { DrizzleModule } from '../drizzle/drizzle.module.js';
import { FileService } from '../file/file.service.js';
import { UserService } from '../user/user.service.js';
import { BrokerListingsController } from './broker-listings.controller.js';
import { BusinessListingsController } from './business-listings.controller.js';
import { CommercialPropertyListingsController } from './commercial-property-listings.controller.js';
import { ListingsService } from './listings.service.js';
import { UnknownListingsController } from './unknown-listings.controller.js';
@Module({ @Module({
imports: [RedisModule,DrizzleModule], imports: [DrizzleModule],
controllers: [BusinessListingsController, CommercialPropertyListingsController, UnknownListingsController, BrokerListingsController], controllers: [BusinessListingsController, CommercialPropertyListingsController, UnknownListingsController, BrokerListingsController],
providers: [ListingsService, FileService, UserService], providers: [ListingsService, FileService, UserService],
exports: [ListingsService], exports: [ListingsService],

View File

@ -11,21 +11,17 @@ import { MailController } from './mail.controller.js';
import { MailService } from './mail.service.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);
const user = process.env.amazon_user;
const password = process.env.amazon_password;
@Module({ @Module({
imports: [ imports: [
DrizzleModule, DrizzleModule,
UserModule, UserModule,
MailerModule.forRoot({ MailerModule.forRoot({
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
// or
transport: { transport: {
host: 'email-smtp.us-east-2.amazonaws.com', host: 'email-smtp.us-east-2.amazonaws.com',
secure: false, secure: false,
port: 587, port: 587,
// auth: {
// user: 'andreas.knuth@gmail.com',
// pass: 'ksnh xjae dqbv xana',
// },
auth: { auth: {
user: 'AKIAU6GDWVAQ2QNFLNWN', user: 'AKIAU6GDWVAQ2QNFLNWN',
pass: 'BDE9nZv/ARbpotim1mIOir52WgIbpSi9cv1oJoH8oEf7', pass: 'BDE9nZv/ARbpotim1mIOir52WgIbpSi9cv1oJoH8oEf7',

View File

@ -1,30 +0,0 @@
// redis.module.ts
import { Module } from '@nestjs/common';
@Module({
providers: [
// {
// provide: 'REDIS_OPTIONS',
// useValue: {
// url: 'redis://localhost:6379'
// }
// },
// {
// inject: ['REDIS_OPTIONS'],
// provide: 'REDIS_CLIENT',
// useFactory: async (options: { url: string }) => {
// const client = createClient(options);
// await client.connect();
// return client;
// }
// }
],
// exports:['REDIS_CLIENT']
})
export class RedisModule {}
export const REDIS_CLIENT = "REDIS_CLIENT";
// redis.service.ts
import { Injectable } from '@nestjs/common';
import { createClient } from 'redis';

View File

@ -1,14 +1,12 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { DrizzleModule } from '../drizzle/drizzle.module.js';
import { FileService } from '../file/file.service.js';
import { UserController } from './user.controller.js'; import { UserController } from './user.controller.js';
import { UserService } from './user.service.js'; import { UserService } from './user.service.js';
import { RedisModule } from '../redis/redis.module.js';
import { FileService } from '../file/file.service.js';
import { DrizzleModule } from '../drizzle/drizzle.module.js';
@Module({ @Module({
imports: [DrizzleModule], imports: [DrizzleModule],
controllers: [UserController], controllers: [UserController],
providers: [UserService,FileService] providers: [UserService, FileService],
}) })
export class UserModule { export class UserModule {}
}

32
bizmatch/certs/cert.pem Normal file
View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIUa+QJdPmuRDNbuf/nzb2J+6ii5nwwDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCREUxDDAKBgNVBAgMA05SVzEQMA4GA1UEBwwHRVJLUkFU
SDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTI0MDUxMDEy
NTQxNVoXDTI1MDUxMDEyNTQxNVowUDELMAkGA1UEBhMCREUxDDAKBgNVBAgMA05S
VzEQMA4GA1UEBwwHRVJLUkFUSDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxiTSQDCC/i3n
X6bMKpl0baUgjbzYDc7ZrvIYfj/t25sdv0E/07ysbXNuldzCX6Rnva/1wVZS30zy
vQm8cVM074oP9qy7wKeIU15nEwRe03P5zipix1WXGWXHY+ShZ2MHy/iDQ1XzpO3p
xXs2vxuJZSUoz1M0c+/pTWBx2D790l5qNkt2sbk5NaHfPQDuw+y2cXDqmJqcCi9I
rYbaQGhXeb/IRu8pW4UwasVsq7DxGlDX8k8Dva5O0Ixf+muqQELuMdeTtR/PoFxw
2F+qOUlS2ujuyQkLOvVZOTalxRfWMuexaQzLlQO91MDehTrOFuMUBCKhYztgZKe2
k9z0fTJmtLyxMPTQuZCv1Gnrw6hcVxjiFQ8YP2ni+ekb86dIA3llH8r+4xEGygfB
QxHiBH9uO8Q9MFpfU2CPE7GxQoB17fu4KqaK0ucVnNM+rJcsNom9svixb5C4CkS/
S1/KQVDi8mrYwQIOP+Y4YLuNvSvUlitZXq8h0ogVqNMl2+R0CYX4lk/mkOEeCeGW
yG4ek2GQxZNLAnoMoLb+kHnVhPaV0SWW052wvXZzOrIMrlkSZK6yYim3JPsD8hc/
284lNEFL3DknICPsVFd64LjwPxA0J5AqyhQAvpXyFVHUUA5+h2EATrBh/Fp9cw84
AkEeVArMWOlx5cg7nAdgQaD5XUaBp7kCAwEAAaNTMFEwHQYDVR0OBBYEFMSO9FoT
nqjHpniyExGf53tV/TAhMB8GA1UdIwQYMBaAFMSO9FoTnqjHpniyExGf53tV/TAh
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADbsIroXoCIe8adn
zh/WNZUUXLDW3JU2QyyYKhnZgqE0Wqh5QLgNwd5ZfH3Iaqhf0xFH9jUeEAWWA17d
hVy4rWsC200DRZ5BYOaffqdflpDE3yAk+8p3kAWjogaCX1wvBuLZ9BHWpuqQ72ec
zYM2ag2wWfBpicXl4BaSnsx1xErxKvxgixgGy6BhcErmfnYtJDnU2Cl+MSMXb758
7hAl9JiJAH8OuaAjvbkhSVTZQFjDgCGHHQ8mR8IksWdGGe/LN/yoWc4lk7lv7vmu
YBfP/SNZvxBzZlw1fXcdz1Wirljy7yz3+s59Knkc2jysysFC4LkZlUy0unmGoPy0
D1XdXyDMy05eoUaeRnM0rfzUxYfXMA3sQlsWw7he6fD8YylVedXxd3mcfK0jle0y
VkDyreZ9+mc/4vmjW0KpOfFGvhhAS9L1D8K3bKpky3HoHSqK1Nb8Ymh/WkhOpHwg
unUyIKdRHvGeWkUXQaLbRKI6w2BQwT7oKDOD60cJG26U3XcYarevz9qHsZX865tj
4xZrp+IUr8OkYBnRrmx2TZ70goRXI77nHVzHmY+xHhjvPJOZOcUAvEHU+5VY3ucN
0noEqiYzb77LcqVbbL3cywDLiyfdx9/x8TU1iYPA+IMwhYb/tLBFzFWmR7znw6On
D775XK/EVryozX/6GmtG+XGZs+57
-----END CERTIFICATE-----

52
bizmatch/certs/key.pem Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
qs7lw2IbVhzRkVryySXsCXn2O1iu
-----END PRIVATE KEY-----

View File

@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve & http-server ../bizmatch-server",
"build": "ng build", "build": "ng build",
"build.dev": "ng build --configuration dev", "build.dev": "ng build --configuration dev",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
@ -55,6 +55,7 @@
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jasmine": "~5.1.4", "@types/jasmine": "~5.1.4",
"@types/node": "^20.11.20", "@types/node": "^20.11.20",
"http-server": "^14.1.1",
"jasmine-core": "~5.1.2", "jasmine-core": "~5.1.2",
"karma": "~6.4.2", "karma": "~6.4.2",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",

View File

@ -3,19 +3,8 @@
"target": "http://localhost:3000", "target": "http://localhost:3000",
"secure": false "secure": false
}, },
"/property": { "/pictures": {
"target": "http://localhost:3000", "target": "http://localhost:8080",
"secure": false, "secure": false
"changeOrigin": true
},
"/logo": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true
},
"/profile": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true
} }
} }

View File

@ -87,17 +87,17 @@ export class HeaderComponent {
{ {
label: 'Businesses for Sale', label: 'Businesses for Sale',
routerLink: '/businessListings', routerLink: '/businessListings',
fragment: '', state: {},
}, },
{ {
label: 'Commercial Property', label: 'Commercial Property',
routerLink: '/commercialPropertyListings', routerLink: '/commercialPropertyListings',
fragment: '', state: {},
}, },
{ {
label: 'Professionals/Brokers Directory', label: 'Professionals/Brokers Directory',
routerLink: '/brokerListings', routerLink: '/brokerListings',
fragment: '', state: {},
}, },
]; ];

View File

@ -42,13 +42,8 @@
</ul> </ul>
<p-galleria [value]="propertyImages" [showIndicators]="true" [showThumbnails]="false" [responsiveOptions]="responsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5"> <p-galleria [value]="propertyImages" [showIndicators]="true" [showThumbnails]="false" [responsiveOptions]="responsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5">
<ng-template pTemplate="item" let-item> <ng-template pTemplate="item" let-item>
<img src="property/{{ listing.imagePath }}/{{ item }}" style="width: 100%" /> <img src="pictures/property/{{ listing.imagePath }}/{{ item }}" style="width: 100%" />
</ng-template> </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> </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]="['/editCommercialPropertyListing', listing.id]"></button> <button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>

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 lg:flex-row lg:justify-content-between">
<div class="flex align-items-start flex-column md:flex-row"> <div class="flex align-items-start flex-column md:flex-row">
@if(user.hasProfile){ @if(user.hasProfile){
<img src="{{ environment.apiBaseUrl }}/profile/{{ user.id }}.avif" class="mr-5 mb-3 lg:mb-0" style="width: 90px" /> <img src="pictures//profile/{{ user.id }}.avif" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
} @else { } @else {
<img src="assets/images/person_placeholder.jpg" class="mr-5 mb-3 lg:mb-0" style="width: 90px" /> <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> --> <!-- <span class="font-medium text-500">Logo</span> -->
<div> <div>
@if(user.hasCompanyLogo){ @if(user.hasCompanyLogo){
<img src="{{ environment.apiBaseUrl }}/logo/{{ user.id }}.avif" class="mr-5 lg:mb-0" style="height: 60px; max-width: 100px" /> <img src="pictures/logo/{{ user.id }}.avif" class="mr-5 lg:mb-0" style="height: 60px; max-width: 100px" />
} }
<!-- <img *ngIf="!user.hasCompanyLogo" src="assets/images/placeholder.png" <!-- <img *ngIf="!user.hasCompanyLogo" src="assets/images/placeholder.png"
class="mr-5 lg:mb-0" style="height:60px;max-width:100px" /> --> class="mr-5 lg:mb-0" style="height:60px;max-width:100px" /> -->
@ -123,7 +123,7 @@
<div class="p-3 border-1 surface-border border-round surface-card"> <div class="p-3 border-1 surface-border border-round surface-card">
<div class="text-900 mb-2 flex align-items-center"> <div class="text-900 mb-2 flex align-items-center">
@if (listing.imageOrder?.length>0){ @if (listing.imageOrder?.length>0){
<img src="property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}" class="mr-3" style="width: 45px; height: 45px" /> <img src="pictures/property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}" class="mr-3" style="width: 45px; height: 45px" />
} @else { } @else {
<img src="assets/images/placeholder_properties.jpg" class="mr-3" style="width: 45px; height: 45px" /> <img src="assets/images/placeholder_properties.jpg" class="mr-3" style="width: 45px; height: 45px" />
} }

View File

@ -54,7 +54,7 @@ export class HomeComponent {
} }
search() { search() {
const data = { keep: true }; const data = { keep: true };
this.router.navigate([`${this.activeTabAction}Listings`], { state: { data } }); this.router.navigate([`${this.activeTabAction}Listings`]);
} }
login() { login() {

View File

@ -3,7 +3,23 @@
<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"> <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
[filter]="true"
filterBy="name"
[options]="selectOptions.states"
[(ngModel)]="criteria.state"
optionLabel="name"
optionValue="value"
[showClear]="true"
placeholder="Location"
[style]="{ width: '100%' }"
>
<ng-template let-state pTemplate="item">
<div class="flex align-items-center gap-2">
<div>{{ state.name }}</div>
</div>
</ng-template>
</p-dropdown>
</div> </div>
<div class="col-2"> <div class="col-2">
<p-inputGroup> <p-inputGroup>
@ -26,7 +42,7 @@
<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="profile/{{ user.id }}.avif" class="w-5rem" /> <img src="pictures/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" />
} }
@ -39,7 +55,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="logo/{{ user.id }}.avif" class="rounded-image" /> <img src="pictures/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" />
} }

View File

@ -1,4 +1,4 @@
import { CommonModule } from '@angular/common'; import { CommonModule, NgOptimizedImage } from '@angular/common';
import { ChangeDetectorRef, Component } from '@angular/core'; import { ChangeDetectorRef, Component } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { ActivatedRoute, Router, RouterModule } from '@angular/router';
@ -23,7 +23,21 @@ import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from
@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, InputGroupModule], imports: [
CommonModule,
StyleClassModule,
ButtonModule,
CheckboxModule,
InputTextModule,
DropdownModule,
FormsModule,
StyleClassModule,
ToggleButtonModule,
RouterModule,
PaginatorModule,
InputGroupModule,
NgOptimizedImage,
],
templateUrl: './broker-listings.component.html', templateUrl: './broker-listings.component.html',
styleUrl: './broker-listings.component.scss', styleUrl: './broker-listings.component.scss',
}) })
@ -59,7 +73,7 @@ export class BrokerListingsComponent {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined; this.criteria.type = undefined;
this.route.data.subscribe(async () => { this.route.data.subscribe(async () => {
if (!this.router.getCurrentNavigation().extras.state) { if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria); resetCriteria(this.criteria);
} }
this.init(); this.init();
@ -67,7 +81,7 @@ export class BrokerListingsComponent {
} }
async ngOnInit() { async ngOnInit() {
const statesResult = await this.listingsService.getAllStates('business'); const statesResult = await this.listingsService.getAllStates('business');
this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); this.states = statesResult.map(ls => ({ name: this.selectOptions.getState(ls.state as string), value: ls.state, count: ls.count }));
} }
async init() { async init() {
this.search(); this.search();

View File

@ -3,10 +3,29 @@
<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"> <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> <p-dropdown
[filter]="true"
filterBy="name"
[options]="states"
[(ngModel)]="criteria.state"
optionLabel="criteria.location"
optionLabel="name"
optionValue="value"
[showClear]="true"
placeholder="State"
[style]="{ width: '100%' }"
>
<ng-template let-state pTemplate="item">
<div class="flex align-items-center gap-2">
<div>{{ state.name }} ({{ state.count }})</div>
</div>
</ng-template>
</p-dropdown>
</div> </div>
<div class="col-2"> <div class="col-2">
<p-dropdown <p-dropdown
[filter]="true"
filterBy="name"
[options]="selectOptions.typesOfBusiness" [options]="selectOptions.typesOfBusiness"
[(ngModel)]="criteria.type" [(ngModel)]="criteria.type"
optionLabel="name" optionLabel="name"
@ -58,7 +77,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 }}.avif" (error)="imageErrorHandler(listing)" class="rounded-image" /> <img src="pictures/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">

View File

@ -70,7 +70,7 @@ export class BusinessListingsComponent {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined; this.criteria.type = undefined;
this.route.data.subscribe(async () => { this.route.data.subscribe(async () => {
if (!this.router.getCurrentNavigation().extras.state) { if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria); resetCriteria(this.criteria);
} }
this.init(); this.init();
@ -79,7 +79,7 @@ export class BusinessListingsComponent {
async ngOnInit() {} async ngOnInit() {}
async init() { async init() {
const statesResult = await this.listingsService.getAllStates('business'); const statesResult = await this.listingsService.getAllStates('business');
this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); this.states = statesResult.map(ls => ({ name: this.selectOptions.getState(ls.state as string), value: ls.state, count: ls.count }));
this.search(); this.search();
} }
refine() { refine() {

View File

@ -3,10 +3,18 @@
<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"> <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 [filter]="true" filterBy="name" [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Location" [style]="{ width: '100%' }">
<ng-template let-state pTemplate="item">
<div class="flex align-items-center gap-2">
<div>{{ state.name }} ({{ state.count }})</div>
</div>
</ng-template>
</p-dropdown>
</div> </div>
<div class="col-2"> <div class="col-2">
<p-dropdown <p-dropdown
[filter]="true"
filterBy="name"
[options]="selectOptions.typesOfCommercialProperty" [options]="selectOptions.typesOfCommercialProperty"
[(ngModel)]="criteria.type" [(ngModel)]="criteria.type"
optionLabel="name" optionLabel="name"
@ -43,9 +51,8 @@
<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="pictures/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="assets/images/placeholder_properties.jpg" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" /> <img src="assets/images/placeholder_properties.jpg" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
} }
<p class="absolute px-2 py-1 border-round-lg text-sm font-normal text-white mt-0 mb-0" style="background-color: rgba(255, 255, 255, 0.3); backdrop-filter: invert(30%); top: 3%; left: 3%"> <p class="absolute px-2 py-1 border-round-lg text-sm font-normal text-white mt-0 mb-0" style="background-color: rgba(255, 255, 255, 0.3); backdrop-filter: invert(30%); top: 3%; left: 3%">

View File

@ -55,7 +55,7 @@ export class CommercialPropertyListingsComponent {
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
this.criteria.type = undefined; this.criteria.type = undefined;
this.route.data.subscribe(async () => { this.route.data.subscribe(async () => {
if (!this.router.getCurrentNavigation().extras.state) { if (this.router.getCurrentNavigation().extras.state) {
resetCriteria(this.criteria); resetCriteria(this.criteria);
} }
this.init(); this.init();
@ -63,8 +63,8 @@ export class CommercialPropertyListingsComponent {
} }
async ngOnInit() {} async ngOnInit() {}
async init() { async init() {
const statesResult = await this.listingsService.getAllStates('business'); const statesResult = await this.listingsService.getAllStates('commercialProperty');
this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); this.states = statesResult.map(ls => ({ name: this.selectOptions.getState(ls.state as string), value: ls.state, count: ls.count }));
this.search(); this.search();
} }
refine() { refine() {

View File

@ -74,8 +74,8 @@ export class AccountComponent {
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 ? `profile/${this.user.id}.avif` : `/assets/images/placeholder.png`; this.profileUrl = this.user.hasProfile ? `pictures/profile/${this.user.id}.avif` : `/assets/images/placeholder.png`;
this.companyLogoUrl = this.user.hasCompanyLogo ? `logo/${this.user.id}.avif` : `/assets/images/placeholder.png`; this.companyLogoUrl = this.user.hasCompanyLogo ? `pictures/logo/${this.user.id}.avif` : `/assets/images/placeholder.png`;
} }
printInvoice(invoice: Invoice) {} printInvoice(invoice: Invoice) {}
@ -87,11 +87,11 @@ export class AccountComponent {
onUploadCompanyLogo(event: any) { onUploadCompanyLogo(event: any) {
const uniqueSuffix = '?_ts=' + new Date().getTime(); const uniqueSuffix = '?_ts=' + new Date().getTime();
this.companyLogoUrl = `${environment.apiBaseUrl}/logo/${this.user.id}${uniqueSuffix}`; //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`; this.companyLogoUrl = `pictures/logo/${this.user.id}${uniqueSuffix}`;
} }
onUploadProfilePicture(event: any) { onUploadProfilePicture(event: any) {
const uniqueSuffix = '?_ts=' + new Date().getTime(); const uniqueSuffix = '?_ts=' + new Date().getTime();
this.profileUrl = `${environment.apiBaseUrl}/profile/${this.user.id}${uniqueSuffix}`; //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`; this.profileUrl = `pictures/profile/${this.user.id}${uniqueSuffix}`;
} }
setImageToFallback(event: Event) { setImageToFallback(event: Event) {
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild (event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild

View File

@ -103,7 +103,7 @@
@for (image of propertyImages; track image) { @for (image of propertyImages; track image) {
<span cdkDropList mixedCdkDropList> <span cdkDropList mixedCdkDropList>
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap"> <div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
<img src="property/{{ listing.imagePath }}/{{ image }}" [alt]="image" class="shadow-2" cdkDrag /> <img src="pictures/property/{{ listing.imagePath }}/{{ image }}" [alt]="image" class="shadow-2" cdkDrag />
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image)"></fa-icon> <fa-icon [icon]="faTrash" (click)="deleteConfirm(image)"></fa-icon>
</div> </div>
</span> </span>