Bugfixes
This commit is contained in:
parent
474d7c63d5
commit
f51a298227
|
|
@ -18,11 +18,11 @@ export class ImageController {
|
|||
private selectOptions: SelectOptionsService,
|
||||
) {}
|
||||
|
||||
@Post('uploadPropertyPicture/:id')
|
||||
@Post('uploadPropertyPicture/:imagePath')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('id') id: string) {
|
||||
const imagename = await this.fileService.storePropertyPicture(file, id);
|
||||
await this.listingService.addImage(id, imagename);
|
||||
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File, @Param('imagePath') imagePath: string) {
|
||||
const imagename = await this.fileService.storePropertyPicture(file, imagePath);
|
||||
await this.listingService.addImage(imagePath, imagename);
|
||||
}
|
||||
|
||||
@Post('uploadProfile/:id')
|
||||
|
|
|
|||
|
|
@ -68,7 +68,13 @@ export class ListingsService {
|
|||
.where(sql`${table.id} = ${id}`);
|
||||
return result[0] as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
|
||||
async findByImagePath(imagePath: string): Promise<CommercialPropertyListing> {
|
||||
const result = await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.where(sql`${commercials.imagePath} = ${imagePath}`);
|
||||
return result[0] as 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[];
|
||||
}
|
||||
|
|
@ -76,8 +82,6 @@ export class ListingsService {
|
|||
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
data.created = new Date();
|
||||
data.updated = new Date();
|
||||
data.visits = 0;
|
||||
data.lastVisit = null;
|
||||
const [createdListing] = await this.conn.insert(table).values(data).returning();
|
||||
return createdListing as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
|
|
@ -116,10 +120,9 @@ export class ListingsService {
|
|||
await this.updateListing(listing.id, listing, commercials);
|
||||
}
|
||||
}
|
||||
async addImage(id: string, imagename: string) {
|
||||
const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing;
|
||||
async addImage(imagePath: string, imagename: string) {
|
||||
const listing = (await this.findByImagePath(imagePath)) as unknown as CommercialPropertyListing;
|
||||
listing.imageOrder.push(imagename);
|
||||
listing.imagePath = listing.id;
|
||||
await this.updateListing(listing.id, listing, commercials);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export class MailService {
|
|||
phone: mailInfo.sender.phoneNumber,
|
||||
email: mailInfo.sender.email,
|
||||
id: mailInfo.listing.id,
|
||||
url: 'http://localhost:4200',
|
||||
url: mailInfo.url,
|
||||
},
|
||||
});
|
||||
return user;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export interface BusinessListing {
|
|||
updated?: Date;
|
||||
visits?: number;
|
||||
lastVisit?: Date;
|
||||
listingsCategory?: string;
|
||||
listingsCategory?: 'commercialProperty' | 'business';
|
||||
}
|
||||
|
||||
export interface CommercialPropertyListing {
|
||||
|
|
@ -91,7 +91,7 @@ export interface CommercialPropertyListing {
|
|||
updated?: Date;
|
||||
visits?: number;
|
||||
lastVisit?: Date;
|
||||
listingsCategory?: string;
|
||||
listingsCategory?: 'commercialProperty' | 'business';
|
||||
}
|
||||
export interface AreasServed {
|
||||
county: string;
|
||||
|
|
|
|||
|
|
@ -64,24 +64,24 @@ export interface ListingCriteria {
|
|||
maxPrice: number;
|
||||
realEstateChecked: boolean;
|
||||
title: string;
|
||||
category: 'professional|broker';
|
||||
category: 'professional' | 'broker';
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface KeycloakUser {
|
||||
id: string;
|
||||
createdTimestamp: number;
|
||||
username: string;
|
||||
enabled: boolean;
|
||||
totp: boolean;
|
||||
emailVerified: boolean;
|
||||
createdTimestamp?: number;
|
||||
username?: string;
|
||||
enabled?: boolean;
|
||||
totp?: boolean;
|
||||
emailVerified?: boolean;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
disableableCredentialTypes: any[];
|
||||
requiredActions: any[];
|
||||
notBefore: number;
|
||||
access: Access;
|
||||
disableableCredentialTypes?: any[];
|
||||
requiredActions?: any[];
|
||||
notBefore?: number;
|
||||
access?: Access;
|
||||
}
|
||||
export interface Access {
|
||||
manageGroupMembership: boolean;
|
||||
|
|
@ -150,6 +150,7 @@ export interface MailInfo {
|
|||
sender: Sender;
|
||||
userId: string;
|
||||
email: string;
|
||||
url: string;
|
||||
listing?: BusinessListing;
|
||||
}
|
||||
export interface Sender {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { Body, Controller, Get, Inject, Param, Post, Put, Query, Req } from '@nestjs/common';
|
||||
import { UserService } from './user.service.js';
|
||||
import { Body, Controller, Get, Inject, Param, Post, Query } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
import { User } from 'src/models/db.model.js';
|
||||
import { Logger } from 'winston';
|
||||
import { UserService } from './user.service.js';
|
||||
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
|
||||
constructor(private userService: UserService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
findByMail(@Query('mail') mail: string): any {
|
||||
|
|
@ -38,5 +40,11 @@ export class UserController {
|
|||
this.logger.info(`Found users: ${JSON.stringify(foundUsers)}`);
|
||||
return foundUsers;
|
||||
}
|
||||
|
||||
@Get('states/all')
|
||||
async getStates(): Promise<any[]> {
|
||||
this.logger.info(`Getting all states for users`);
|
||||
const result = await this.userService.getStates();
|
||||
this.logger.info(`Found ${result.length} entries`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export class UserService {
|
|||
private getConditions(criteria: ListingCriteria): any[] {
|
||||
const conditions = [];
|
||||
if (criteria.state) {
|
||||
conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`);
|
||||
//conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`);
|
||||
conditions.push(sql`${schema.users.areasServed} @> ${JSON.stringify([{ state: criteria.state }])}`);
|
||||
}
|
||||
if (criteria.name) {
|
||||
conditions.push(or(ilike(schema.users.firstname, `%${criteria.name}%`), ilike(schema.users.lastname, `%${criteria.name}%`)));
|
||||
|
|
@ -78,4 +79,9 @@ export class UserService {
|
|||
]);
|
||||
return { total, data };
|
||||
}
|
||||
async getStates(): Promise<any[]> {
|
||||
const query = sql`SELECT jsonb_array_elements(${schema.users.areasServed}) ->> 'state' AS state, COUNT(DISTINCT ${schema.users.id}) AS count FROM ${schema.users} GROUP BY state ORDER BY count DESC`;
|
||||
const result = await this.conn.execute(query);
|
||||
return result.rows;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,39 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { LoadingService } from './services/loading.service';
|
||||
import { HomeComponent } from './pages/home/home.component';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { KeycloakService } from './services/keycloak.service';
|
||||
import { KeycloakEventType } from './models/keycloak-event';
|
||||
import { createGenericObject } from './utils/utils';
|
||||
import onChange from 'on-change';
|
||||
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { ListingCriteria } from '../../../bizmatch-server/src/models/main.model';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { KeycloakService } from './services/keycloak.service';
|
||||
import { LoadingService } from './services/loading.service';
|
||||
import { UserService } from './services/user.service';
|
||||
import {ListingCriteria} from '../../../bizmatch-server/src/models/main.model'
|
||||
import { createDefaultListingCriteria } from './utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterOutlet, HeaderComponent, ProgressSpinnerModule, FooterComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
styleUrl: './app.component.scss',
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'bizmatch';
|
||||
actualRoute ='';
|
||||
listingCriteria:ListingCriteria = onChange(createGenericObject<ListingCriteria>(),(path, value, previousValue, applyData)=>{
|
||||
sessionStorage.setItem('criteria',JSON.stringify(value));
|
||||
actualRoute = '';
|
||||
listingCriteria: ListingCriteria = onChange(createDefaultListingCriteria(), (path, value, previousValue, applyData) => {
|
||||
sessionStorage.setItem('criteria', JSON.stringify(value));
|
||||
});
|
||||
public constructor(public loadingService: LoadingService, private router: Router,private activatedRoute: ActivatedRoute, private keycloakService:KeycloakService,private userService:UserService) {
|
||||
this.router.events.pipe(
|
||||
filter(event => event instanceof NavigationEnd)
|
||||
).subscribe(() => {
|
||||
public constructor(public loadingService: LoadingService, private router: Router, private activatedRoute: ActivatedRoute, private keycloakService: KeycloakService, private userService: UserService) {
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
|
||||
let currentRoute = this.activatedRoute.root;
|
||||
while (currentRoute.children[0] !== undefined) {
|
||||
currentRoute = currentRoute.children[0];
|
||||
}
|
||||
// Hier haben Sie Zugriff auf den aktuellen Route-Pfad
|
||||
this.actualRoute=currentRoute.snapshot.url[0].path
|
||||
this.actualRoute = currentRoute.snapshot.url[0].path;
|
||||
});
|
||||
}
|
||||
ngOnInit(){
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
<div class="wrapper">
|
||||
<div class="pl-3 flex align-items-center gap-2">
|
||||
<a routerLink="/home"><img src="assets/images/header-logo.png" height="40" alt="bizmatch" /></a>
|
||||
<p-tabMenu [model]="tabItems" ariaLabelledBy="label" styleClass="flex" [activeItem]="activeItem">
|
||||
</p-tabMenu>
|
||||
<p-menubar [model]="menuItems"></p-menubar>
|
||||
<div *ngIf="user$ | async as user else empty">Welcome, {{user.firstname}}</div>
|
||||
<ng-template #empty>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="pl-3 flex align-items-center gap-2">
|
||||
<a routerLink="/home"><img src="assets/images/header-logo.png" height="40" alt="bizmatch" /></a>
|
||||
<p-tabMenu [model]="tabItems" ariaLabelledBy="label" styleClass="flex" [activeItem]="activeItem"> </p-tabMenu>
|
||||
<p-menubar [model]="menuItems"></p-menubar>
|
||||
<div *ngIf="user$ | async as user; else empty">Welcome, {{ user.firstName }}</div>
|
||||
<ng-template #empty> </ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,7 @@ import { MenubarModule } from 'primeng/menubar';
|
|||
import { OverlayPanelModule } from 'primeng/overlaypanel';
|
||||
import { TabMenuModule } from 'primeng/tabmenu';
|
||||
import { Observable } from 'rxjs';
|
||||
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||
import { KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { UserService } from '../../services/user.service';
|
||||
@Component({
|
||||
|
|
@ -20,8 +20,8 @@ import { UserService } from '../../services/user.service';
|
|||
})
|
||||
export class HeaderComponent {
|
||||
public buildVersion = environment.buildVersion;
|
||||
user$: Observable<User>;
|
||||
user: User;
|
||||
user$: Observable<KeycloakUser>;
|
||||
user: KeycloakUser;
|
||||
public tabItems: MenuItem[];
|
||||
public menuItems: MenuItem[];
|
||||
activeItem;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div class="flex justify-content-between align-items-center align-content-center">
|
||||
<div class="font-medium text-3xl text-900 mb-3">{{ listing?.title }}</div>
|
||||
<!-- <button pButton pRipple type="button" label="Go back to listings" icon="pi pi-user-plus" class="mr-3 p-button-rounded"></button> -->
|
||||
@if(historyService.canGoBack()){
|
||||
@if(historyService.canGoBack){
|
||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="historyService.goBack()"></p-button>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
<div class="text-900 w-full md:w-10">{{ listing.brokerLicencing }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
@if(listing && user && (user.id===listing?.userId || isAdmin())){
|
||||
@if(listing && listingUser && (listingUser.id===listing?.userId || isAdmin())){
|
||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editBusinessListing', listing.id]"></button>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { MessageService } from 'primeng/api';
|
|||
import { GalleriaModule } from 'primeng/galleria';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
|
|
@ -48,7 +48,7 @@ export class DetailsBusinessListingComponent {
|
|||
criteria: ListingCriteria;
|
||||
mailinfo: MailInfo;
|
||||
environment = environment;
|
||||
user: User;
|
||||
user: KeycloakUser;
|
||||
listingUser: User;
|
||||
description: SafeHtml;
|
||||
private history: string[] = [];
|
||||
|
|
@ -68,7 +68,7 @@ export class DetailsBusinessListingComponent {
|
|||
this.history.push(event.urlAfterRedirects);
|
||||
}
|
||||
});
|
||||
this.mailinfo = { sender: {}, userId: '', email: '' };
|
||||
this.mailinfo = { sender: {}, userId: '', email: '', url: environment.mailinfoUrl };
|
||||
this.userService.getUserObservable().subscribe(user => {
|
||||
this.user = user;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<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> -->
|
||||
@if(historyService.canGoBack()){
|
||||
@if(historyService.canGoBack){
|
||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="historyService.goBack()"></p-button>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
@if(listing && user && (user.id===listing?.userId || isAdmin())){
|
||||
@if(listing && listingUser && (listingUser.id===listing?.userId || isAdmin())){
|
||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
<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%" />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ item }}" style="width: 100%" />
|
||||
</ng-template>
|
||||
</p-galleria>
|
||||
@if (mailinfo){
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { MessageService } from 'primeng/api';
|
|||
import { GalleriaModule } from 'primeng/galleria';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
|
|
@ -49,9 +49,10 @@ export class DetailsCommercialPropertyListingComponent {
|
|||
mailinfo: MailInfo;
|
||||
propertyImages: string[] = [];
|
||||
environment = environment;
|
||||
user: User;
|
||||
user: KeycloakUser;
|
||||
listingUser: User;
|
||||
description: SafeHtml;
|
||||
env = environment;
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
|
|
@ -63,7 +64,7 @@ export class DetailsCommercialPropertyListingComponent {
|
|||
private sanitizer: DomSanitizer,
|
||||
public historyService: HistoryService,
|
||||
) {
|
||||
this.mailinfo = { sender: {}, userId: '', email: '' };
|
||||
this.mailinfo = { sender: {}, userId: '', email: '', url: environment.mailinfoUrl };
|
||||
this.userService.getUserObservable().subscribe(user => {
|
||||
this.user = user;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,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?_ts={{ ts }}" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
|
||||
<img src="{{ env.imageBaseUrl }}/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" />
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<!-- <span class="font-medium text-500">Logo</span> -->
|
||||
<div>
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="pictures/logo/{{ user.id }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="height: 60px; max-width: 100px" />
|
||||
<img src="{{ env.imageBaseUrl }}/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" /> -->
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if(historyService.canGoBack()){
|
||||
@if(historyService.canGoBack){
|
||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="historyService.goBack()"></p-button>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -121,7 +121,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] }}?_ts={{ ts }}" class="mr-3" style="width: 45px; height: 45px" />
|
||||
<img src="{{ env.imageBaseUrl }}/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" />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,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 { ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
|
|
@ -25,7 +25,7 @@ import { SharedModule } from '../../../shared/shared/shared.module';
|
|||
export class DetailsUserComponent {
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user: User;
|
||||
user$: Observable<User>;
|
||||
user$: Observable<KeycloakUser>;
|
||||
environment = environment;
|
||||
criteria: ListingCriteria;
|
||||
businessListings: BusinessListing[];
|
||||
|
|
@ -33,6 +33,7 @@ export class DetailsUserComponent {
|
|||
companyOverview: SafeHtml;
|
||||
offeredServices: SafeHtml;
|
||||
ts = new Date().getTime();
|
||||
env = environment;
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
|
|
|
|||
|
|
@ -45,9 +45,29 @@
|
|||
</div>
|
||||
<div [ngClass]="{ 'mt-5': activeTabAction === 'business', 'mt-11': activeTabAction === 'commercialProperty', 'mt-22': activeTabAction === 'broker' }">
|
||||
<div class="flex flex-column align-items-right gap-3 px-2 py-3 my-3 surface-border">
|
||||
<p-dropdown [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '200px' }"></p-dropdown>
|
||||
<p-dropdown
|
||||
[filter]="true"
|
||||
filterBy="name"
|
||||
[options]="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
|
||||
[filter]="true"
|
||||
filterBy="name"
|
||||
[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"
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import { DropdownModule } from 'primeng/dropdown';
|
|||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { StyleClassModule } from 'primeng/styleclass';
|
||||
import { Observable } from 'rxjs';
|
||||
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, ListingCriteria } 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';
|
||||
|
|
@ -28,7 +27,7 @@ export class HomeComponent {
|
|||
maxPrice: string;
|
||||
minPrice: string;
|
||||
criteria: ListingCriteria;
|
||||
user$: Observable<User>;
|
||||
user$: Observable<KeycloakUser>;
|
||||
states = [];
|
||||
public constructor(private router: Router, private activatedRoute: ActivatedRoute, public selectOptions: SelectOptionsService, public userService: UserService, private listingsService: ListingsService) {
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
|
|
|
|||
|
|
@ -3,20 +3,10 @@
|
|||
<div class="wrapper">
|
||||
<div class="grid p-4 align-items-center">
|
||||
<div class="col-2">
|
||||
<p-dropdown
|
||||
[filter]="true"
|
||||
filterBy="name"
|
||||
[options]="selectOptions.states"
|
||||
[(ngModel)]="criteria.state"
|
||||
optionLabel="name"
|
||||
optionValue="value"
|
||||
[showClear]="true"
|
||||
placeholder="Location"
|
||||
[style]="{ width: '100%' }"
|
||||
>
|
||||
<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 }}</div>
|
||||
<div>{{ state.name }} ({{ state.count }})</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-dropdown>
|
||||
|
|
@ -42,7 +32,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?_ts={{ ts }}" class="w-5rem" />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/profile/{{ user.id }}.avif?_ts={{ ts }}" class="w-5rem" />
|
||||
} @else {
|
||||
<img src="assets/images/person_placeholder.jpg" class="w-5rem" />
|
||||
}
|
||||
|
|
@ -55,7 +45,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?_ts={{ ts }}" class="rounded-image" />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ user.id }}.avif?_ts={{ ts }}" class="rounded-image" />
|
||||
} @else {
|
||||
<img src="assets/images/placeholder.png" class="rounded-image" />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ export class BrokerListingsComponent {
|
|||
rows: number = 12;
|
||||
totalRecords: number = 0;
|
||||
ts = new Date().getTime();
|
||||
env = environment;
|
||||
public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined;
|
||||
|
||||
constructor(
|
||||
|
|
@ -81,7 +82,7 @@ export class BrokerListingsComponent {
|
|||
});
|
||||
}
|
||||
async ngOnInit() {
|
||||
const statesResult = await this.listingsService.getAllStates('business');
|
||||
const statesResult = await this.userService.getAllStates();
|
||||
this.states = statesResult.map(ls => ({ name: this.selectOptions.getState(ls.state as string), value: ls.state, count: ls.count }));
|
||||
}
|
||||
async init() {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,7 @@
|
|||
<div class="wrapper">
|
||||
<div class="grid p-4 align-items-center">
|
||||
<div class="col-2">
|
||||
<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%' }"
|
||||
>
|
||||
<p-dropdown [filter]="true" filterBy="name" [options]="states" [(ngModel)]="criteria.state" 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>
|
||||
|
|
@ -77,7 +66,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">Established: {{ listing.established }}</p>
|
||||
<div class="icon-pos">
|
||||
<img src="pictures/logo/{{ listing.userId }}.avif?_ts={{ ts }}" (error)="imageErrorHandler(listing)" class="rounded-image" />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.userId }}.avif?_ts={{ ts }}" (error)="imageErrorHandler(listing)" class="rounded-image" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 surface-100 text-left">
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ export class BusinessListingsComponent {
|
|||
ts = new Date().getTime();
|
||||
first: number = 0;
|
||||
rows: number = 12;
|
||||
env = environment;
|
||||
public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined;
|
||||
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
<article class="flex flex-column md:flex-row w-full gap-3 p-3 surface-card">
|
||||
<div class="relative">
|
||||
@if (listing.imageOrder?.length>0){
|
||||
<img src="pictures/property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}?_ts={{ ts }}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.imageOrder[0] }}?_ts={{ ts }}" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
|
||||
} @else {
|
||||
<img src="assets/images/placeholder_properties.jpg" alt="Image" class="border-round w-full h-full md:w-12rem md:h-9rem" />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export class CommercialPropertyListingsComponent {
|
|||
state: string;
|
||||
totalRecords: number = 0;
|
||||
ts = new Date().getTime();
|
||||
env = environment;
|
||||
constructor(
|
||||
public selectOptions: SelectOptionsService,
|
||||
private listingsService: ListingsService,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export class AccountComponent {
|
|||
dialogRef: DynamicDialogRef | undefined;
|
||||
environment = environment;
|
||||
editorModules = TOOLBAR_OPTIONS;
|
||||
env = environment;
|
||||
constructor(
|
||||
public userService: UserService,
|
||||
private subscriptionService: SubscriptionsService,
|
||||
|
|
@ -64,14 +65,14 @@ export class AccountComponent {
|
|||
try {
|
||||
this.user = await this.userService.getByMail(email);
|
||||
} catch (e) {
|
||||
this.user = { email, firstname: keycloakUser.firstname, lastname: keycloakUser.lastname, areasServed: [], licensedIn: [] };
|
||||
this.user = { email, firstname: keycloakUser.firstName, lastname: keycloakUser.lastName, areasServed: [], licensedIn: [], companyOverview: '', offeredServices: '' };
|
||||
this.user = await this.userService.save(this.user);
|
||||
}
|
||||
}
|
||||
|
||||
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`;
|
||||
this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${this.user.id}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo ? `${this.env.imageBaseUrl}/pictures/logo/${this.user.id}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
}
|
||||
printInvoice(invoice: Invoice) {}
|
||||
|
||||
|
|
@ -82,11 +83,11 @@ export class AccountComponent {
|
|||
|
||||
onUploadCompanyLogo(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.companyLogoUrl = `pictures/logo/${this.user.id}${uniqueSuffix}`;
|
||||
this.companyLogoUrl = `${this.env.imageBaseUrl}/pictures/logo/${this.user.id}${uniqueSuffix}`;
|
||||
}
|
||||
onUploadProfilePicture(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.profileUrl = `pictures/profile/${this.user.id}${uniqueSuffix}`;
|
||||
this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${this.user.id}${uniqueSuffix}`;
|
||||
}
|
||||
setImageToFallback(event: Event) {
|
||||
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild
|
||||
|
|
@ -142,10 +143,10 @@ export class AccountComponent {
|
|||
this.loadingService.stopLoading('uploadImage');
|
||||
if (this.type === 'company') {
|
||||
this.user.hasCompanyLogo = true; //
|
||||
this.companyLogoUrl = `pictures/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`;
|
||||
this.companyLogoUrl = `${this.env.imageBaseUrl}/pictures/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`;
|
||||
} else {
|
||||
this.user.hasProfile = true;
|
||||
this.profileUrl = `pictures/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`;
|
||||
this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`;
|
||||
}
|
||||
await this.userService.save(this.user);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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, getListingType, routeListingWithState } from '../../../utils/utils';
|
||||
import { createDefaultBusinessListing, routeListingWithState } from '../../../utils/utils';
|
||||
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
|
|
@ -118,18 +118,19 @@ export class EditBusinessListingComponent {
|
|||
if (this.mode === 'edit') {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'business'));
|
||||
} else {
|
||||
this.listing = createGenericObject<BusinessListing>();
|
||||
this.listing.listingsCategory = 'business';
|
||||
this.listing = createDefaultBusinessListing();
|
||||
this.listing.userId = await this.userService.getId();
|
||||
this.listing.title = this.data?.title;
|
||||
this.listing.description = this.data?.description;
|
||||
if (this.data) {
|
||||
this.listing.title = this.data?.title;
|
||||
this.listing.description = this.data?.description;
|
||||
}
|
||||
}
|
||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.listing = await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||
this.listing = await this.listingsService.save(this.listing, this.listing.listingsCategory);
|
||||
this.router.navigate(['editBusinessListing', this.listing.id]);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
@for (image of propertyImages; track image) {
|
||||
<span cdkDropList mixedCdkDropList>
|
||||
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
||||
<img src="pictures/property/{{ listing.imagePath }}/{{ image }}" [alt]="image" class="shadow-2" cdkDrag />
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ image }}?_ts={{ ts }}" [alt]="image" class="shadow-2" cdkDrag />
|
||||
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image)"></fa-icon>
|
||||
</div>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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, getListingType, routeListingWithState } from '../../../utils/utils';
|
||||
import { createDefaultCommercialPropertyListing, routeListingWithState } from '../../../utils/utils';
|
||||
|
||||
import { DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { HttpEventType } from '@angular/common/http';
|
||||
|
|
@ -17,6 +17,7 @@ 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, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { AutoCompleteCompleteEvent, ImageProperty } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
|
|
@ -90,6 +91,8 @@ export class EditCommercialPropertyListingComponent {
|
|||
data: BusinessListing;
|
||||
userId: string;
|
||||
typesOfCommercialProperty = [];
|
||||
env = environment;
|
||||
ts = new Date().getTime();
|
||||
constructor(
|
||||
public selectOptions: SelectOptionsService,
|
||||
private router: Router,
|
||||
|
|
@ -123,17 +126,20 @@ export class EditCommercialPropertyListingComponent {
|
|||
if (this.mode === 'edit') {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'));
|
||||
} else {
|
||||
this.listing = createGenericObject<CommercialPropertyListing>();
|
||||
this.listing = createDefaultCommercialPropertyListing();
|
||||
this.listing.userId = await this.userService.getId();
|
||||
this.listing.title = this.data?.title;
|
||||
this.listing.description = this.data?.description;
|
||||
this.listing.imagePath = uuidv4();
|
||||
if (this.data) {
|
||||
this.listing.title = this.data?.title;
|
||||
this.listing.description = this.data?.description;
|
||||
}
|
||||
}
|
||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.listing = await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||
this.listing = await this.listingsService.save(this.listing, this.listing.listingsCategory);
|
||||
this.router.navigate(['editCommercialPropertyListing', this.listing.id]);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||
}
|
||||
|
|
@ -166,7 +172,7 @@ export class EditCommercialPropertyListingComponent {
|
|||
if (cropper) {
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
cropper.getCroppedCanvas().toBlob(async blob => {
|
||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(
|
||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.imagePath).subscribe(
|
||||
async event => {
|
||||
if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
|
|
@ -194,7 +200,8 @@ export class EditCommercialPropertyListingComponent {
|
|||
rejectIcon: 'none',
|
||||
|
||||
accept: async () => {
|
||||
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
||||
this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName);
|
||||
await Promise.all([this.imageService.deleteListingImage(this.listing.id, imageName), this.listingsService.save(this.listing, 'commercialProperty')]);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, 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';
|
||||
|
|
@ -15,7 +14,7 @@ import { MenuAccountComponent } from '../../menu-account/menu-account.component'
|
|||
styleUrl: './favorites.component.scss',
|
||||
})
|
||||
export class FavoritesComponent {
|
||||
user: User;
|
||||
user: KeycloakUser;
|
||||
listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>;
|
||||
favorites: Array<ListingType>;
|
||||
constructor(public userService: UserService, private listingsService: ListingsService, public selectOptions: SelectOptionsService) {
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||
<td>{{ selectOptions.getState(listing.state) }}</td>
|
||||
<td>
|
||||
@if(isBusinessListing(listing)){
|
||||
@if(listing.listingsCategory==='business'){
|
||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editBusinessListing', listing.id]"></button>
|
||||
} @if(isCommercialPropertyListing(listing)){
|
||||
} @if(listing.listingsCategory==='commercialProperty'){
|
||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>
|
||||
}
|
||||
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event, listing)"></button>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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 { getListingType, isBusinessListing, isCommercialPropertyListing } from '../../../utils/utils';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
@Component({
|
||||
selector: 'app-my-listing',
|
||||
|
|
@ -20,8 +19,6 @@ export class MyListingComponent {
|
|||
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||
myListings: Array<ListingType>;
|
||||
user: User;
|
||||
isBusinessListing = isBusinessListing;
|
||||
isCommercialPropertyListing = isCommercialPropertyListing;
|
||||
constructor(
|
||||
public userService: UserService,
|
||||
private listingsService: ListingsService,
|
||||
|
|
@ -39,7 +36,7 @@ export class MyListingComponent {
|
|||
}
|
||||
|
||||
async deleteListing(listing: ListingType) {
|
||||
await this.listingsService.deleteListing(listing.id, getListingType(listing));
|
||||
await this.listingsService.deleteListing(listing.id, listing.listingsCategory);
|
||||
const result = await Promise.all([await this.listingsService.getListingByUserId(this.user.id, 'business'), await this.listingsService.getListingByUserId(this.user.id, 'commercialProperty')]);
|
||||
this.myListings = [...result[0], ...result[1]];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,33 @@
|
|||
import { Location } from '@angular/common';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class HistoryService {
|
||||
private history: string[] = [];
|
||||
private previousUrl: string | undefined;
|
||||
private currentUrl: string | undefined;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.history.push(event.urlAfterRedirects);
|
||||
}
|
||||
constructor(private router: Router, private location: Location) {
|
||||
this.setupRouterListener();
|
||||
}
|
||||
|
||||
private setupRouterListener(): void {
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => {
|
||||
this.previousUrl = this.currentUrl;
|
||||
this.currentUrl = event.urlAfterRedirects;
|
||||
});
|
||||
}
|
||||
|
||||
public getHistory(): string[] {
|
||||
return this.history;
|
||||
get canGoBack(): boolean {
|
||||
return !!this.previousUrl;
|
||||
}
|
||||
|
||||
public canGoBack(): boolean {
|
||||
return this.history.length > 1;
|
||||
}
|
||||
|
||||
public goBack(): void {
|
||||
if (this.canGoBack()) {
|
||||
this.history.pop();
|
||||
this.router.navigateByUrl(this.history[this.history.length - 1]);
|
||||
goBack(): void {
|
||||
if (this.canGoBack) {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,38 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ImageType } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ImageService {
|
||||
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
uploadImage(imageBlob: Blob,type:'uploadPropertyPicture'|'uploadCompanyLogo'|'uploadProfile',id:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/${type}/${id}`;
|
||||
uploadImage(imageBlob: Blob, type: 'uploadPropertyPicture' | 'uploadCompanyLogo' | 'uploadProfile', imagePath: string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/${type}/${imagePath}`;
|
||||
const formData = new FormData();
|
||||
formData.append('file', imageBlob, 'image.png');
|
||||
|
||||
return this.http.post(uploadUrl, formData,{
|
||||
return this.http.post(uploadUrl, formData, {
|
||||
// headers: this.headers,
|
||||
//reportProgress: true,
|
||||
observe: 'events',
|
||||
});
|
||||
}
|
||||
async deleteUserImage(userid:string,type:ImageType,name?:string){
|
||||
async deleteUserImage(userid: string, type: ImageType, name?: string) {
|
||||
return await lastValueFrom(this.http.delete<[]>(`${this.apiBaseUrl}/bizmatch/image/${type.delete}${userid}`));
|
||||
}
|
||||
async deleteListingImage(listingid:string,name?:string){
|
||||
async deleteListingImage(listingid: string, name?: string) {
|
||||
return await lastValueFrom(this.http.delete<[]>(`${this.apiBaseUrl}/bizmatch/image/propertyPicture/${listingid}/${name}`));
|
||||
}
|
||||
async getProfileImagesForUsers(userids:string[]){
|
||||
async getProfileImagesForUsers(userids: string[]) {
|
||||
return await lastValueFrom(this.http.get<[]>(`${this.apiBaseUrl}/bizmatch/image/profileImages/${userids.join(',')}`));
|
||||
}
|
||||
async getCompanyLogosForUsers(userids:string[]){
|
||||
async getCompanyLogosForUsers(userids: string[]) {
|
||||
return await lastValueFrom(this.http.get<[]>(`${this.apiBaseUrl}/bizmatch/image/companyLogos/${userids.join(',')}`));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { jwtDecode } from 'jwt-decode';
|
|||
import { Observable, distinctUntilChanged, filter, from, lastValueFrom, map } from 'rxjs';
|
||||
import urlcat from 'urlcat';
|
||||
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { JwtToken, ListingCriteria, ResponseUsersArray } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { JwtToken, KeycloakUser, ListingCriteria, ResponseUsersArray, StatesResult } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { KeycloakService } from './keycloak.service';
|
||||
|
||||
|
|
@ -16,8 +16,8 @@ export class UserService {
|
|||
// -----------------------------
|
||||
// Keycloak services
|
||||
// -----------------------------
|
||||
private user$ = new Observable<User>();
|
||||
private user: User;
|
||||
private user$ = new Observable<KeycloakUser>();
|
||||
private user: KeycloakUser;
|
||||
public $isLoggedIn: Signal<boolean>;
|
||||
constructor(public keycloak: KeycloakService, private http: HttpClient) {
|
||||
this.user$ = from(this.keycloak.getToken()).pipe(
|
||||
|
|
@ -56,12 +56,12 @@ export class UserService {
|
|||
this.user = this.map2User(token);
|
||||
}
|
||||
|
||||
private map2User(jwt: string): User {
|
||||
private map2User(jwt: string): KeycloakUser {
|
||||
const token = jwtDecode<JwtToken>(jwt);
|
||||
return {
|
||||
id: token.user_id,
|
||||
firstname: token.given_name,
|
||||
lastname: token.family_name,
|
||||
firstName: token.given_name,
|
||||
lastName: token.family_name,
|
||||
email: token.email,
|
||||
};
|
||||
}
|
||||
|
|
@ -69,10 +69,10 @@ export class UserService {
|
|||
isLoggedIn(): boolean {
|
||||
return this.$isLoggedIn();
|
||||
}
|
||||
getKeycloakUser(): User {
|
||||
getKeycloakUser(): KeycloakUser {
|
||||
return this.user;
|
||||
}
|
||||
getUserObservable(): Observable<User> {
|
||||
getUserObservable(): Observable<KeycloakUser> {
|
||||
return this.user$;
|
||||
}
|
||||
async getId(): Promise<string> {
|
||||
|
|
@ -119,4 +119,7 @@ export class UserService {
|
|||
async search(criteria?: ListingCriteria): Promise<ResponseUsersArray> {
|
||||
return await lastValueFrom(this.http.post<ResponseUsersArray>(`${this.apiBaseUrl}/bizmatch/user/search`, criteria));
|
||||
}
|
||||
async getAllStates(): Promise<any> {
|
||||
return await lastValueFrom(this.http.get<StatesResult[]>(`${this.apiBaseUrl}/bizmatch/user/states/all`));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,21 +3,80 @@ import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSeriali
|
|||
import { BusinessListing, CommercialPropertyListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
|
||||
export function createGenericObject<T>(): T {
|
||||
// Ein leeres Objekt vom Typ T erstellen
|
||||
const ergebnis: Partial<T> = {};
|
||||
|
||||
// Für ein reales Interface funktioniert diese direkte Iteration nicht,
|
||||
// da Interfaces zur Compile-Zeit entfernt werden. Stattdessen könnten Sie
|
||||
// ein Dummy-Objekt oder spezifische Typtransformationen verwenden.
|
||||
// Hier nur als Pseudocode dargestellt, um die Idee zu vermitteln:
|
||||
for (const key in ergebnis) {
|
||||
ergebnis[key] = null; // oder undefined, je nach Bedarf
|
||||
}
|
||||
|
||||
return ergebnis as T;
|
||||
export function createDefaultCommercialPropertyListing(): CommercialPropertyListing {
|
||||
return {
|
||||
id: undefined,
|
||||
userId: '',
|
||||
type: null,
|
||||
title: '',
|
||||
description: '',
|
||||
city: '',
|
||||
state: '',
|
||||
price: null,
|
||||
favoritesForUser: [],
|
||||
hideImage: false,
|
||||
draft: false,
|
||||
zipCode: null,
|
||||
county: '',
|
||||
email: '',
|
||||
website: '',
|
||||
phoneNumber: '',
|
||||
imageOrder: [],
|
||||
imagePath: '',
|
||||
created: null,
|
||||
updated: null,
|
||||
visits: null,
|
||||
lastVisit: null,
|
||||
listingsCategory: 'commercialProperty',
|
||||
};
|
||||
}
|
||||
export function createDefaultBusinessListing(): BusinessListing {
|
||||
return {
|
||||
id: undefined,
|
||||
userId: '',
|
||||
type: null,
|
||||
title: '',
|
||||
description: '',
|
||||
city: '',
|
||||
state: '',
|
||||
price: null,
|
||||
favoritesForUser: [],
|
||||
draft: false,
|
||||
realEstateIncluded: false,
|
||||
leasedLocation: false,
|
||||
franchiseResale: false,
|
||||
salesRevenue: null,
|
||||
cashFlow: null,
|
||||
supportAndTraining: '',
|
||||
employees: null,
|
||||
established: null,
|
||||
internalListingNumber: null,
|
||||
reasonForSale: '',
|
||||
brokerLicencing: '',
|
||||
internals: '',
|
||||
created: null,
|
||||
updated: null,
|
||||
visits: null,
|
||||
lastVisit: null,
|
||||
listingsCategory: 'business',
|
||||
};
|
||||
}
|
||||
export function createDefaultListingCriteria(): ListingCriteria {
|
||||
return {
|
||||
start: 0,
|
||||
length: 0,
|
||||
page: 0,
|
||||
pageCount: 0,
|
||||
type: 0,
|
||||
state: '',
|
||||
minPrice: 0,
|
||||
maxPrice: 0,
|
||||
realEstateChecked: false,
|
||||
title: '',
|
||||
category: 'broker',
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function createLogger(name: string, level: number = INFO, options: any = {}) {
|
||||
return _createLogger({
|
||||
name,
|
||||
|
|
@ -33,20 +92,11 @@ export const getSessionStorageHandler = function (path, value, previous, applyDa
|
|||
};
|
||||
|
||||
export function getCriteriaStateObject() {
|
||||
const initialState = createGenericObject<ListingCriteria>();
|
||||
const initialState = createDefaultListingCriteria();
|
||||
const storedState = sessionStorage.getItem('criteria');
|
||||
return storedState ? JSON.parse(storedState) : initialState;
|
||||
}
|
||||
|
||||
export function getListingType(listing: BusinessListing | CommercialPropertyListing): 'business' | 'commercialProperty' {
|
||||
return listing?.type < 100 ? 'business' : 'commercialProperty';
|
||||
}
|
||||
export function isBusinessListing(listing: BusinessListing | CommercialPropertyListing): listing is BusinessListing {
|
||||
return listing?.type < 100;
|
||||
}
|
||||
export function isCommercialPropertyListing(listing: BusinessListing | CommercialPropertyListing): listing is CommercialPropertyListing {
|
||||
return listing?.type >= 100;
|
||||
}
|
||||
export function routeListingWithState(router: Router, value: string, data: any) {
|
||||
if (value === 'business') {
|
||||
router.navigate(['createBusinessListing'], { state: { data } });
|
||||
|
|
@ -54,6 +104,7 @@ export function routeListingWithState(router: Router, value: string, data: any)
|
|||
router.navigate(['createCommercialPropertyListing'], { state: { data } });
|
||||
}
|
||||
}
|
||||
|
||||
export function resetCriteria(criteria: ListingCriteria) {
|
||||
criteria.type = null;
|
||||
criteria.state = null;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
export const environment_base = {
|
||||
// url:'http://localhost:4200',
|
||||
apiBaseUrl: 'http://localhost:3000',
|
||||
// apiAuthorizationHeader: 'Basic dGJjOnFVWDdoT25vR3hBMk4zNHVvVFZ1',
|
||||
buildVersion: '<BUILD_VERSION>',
|
||||
// keycloak: {
|
||||
// url: 'http://localhost:8080',
|
||||
// realm: 'bizmatch',
|
||||
// clientId: 'bizmatch-angular-client'
|
||||
// }
|
||||
keycloak: {
|
||||
url: 'https://auth.bizmatch.net',
|
||||
realm: 'bizmatch-dev',
|
||||
clientId: 'bizmatch-dev'
|
||||
}
|
||||
apiBaseUrl: 'http://localhost:3000',
|
||||
imageBaseUrl: 'https://dev.bizmatch.net',
|
||||
buildVersion: '<BUILD_VERSION>',
|
||||
mailinfoUrl: 'https://dev.bizmatch.net',
|
||||
keycloak: {
|
||||
url: 'https://auth.bizmatch.net',
|
||||
realm: 'bizmatch-dev',
|
||||
clientId: 'bizmatch-dev',
|
||||
},
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import {environment_base} from './environment.base'
|
||||
import { environment_base } from './environment.base';
|
||||
|
||||
export const environment = environment_base
|
||||
export const environment = environment_base;
|
||||
|
||||
environment.apiBaseUrl = "https://api-dev.bizmatch.net"
|
||||
environment.apiBaseUrl = 'https://api-dev.bizmatch.net';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {environment_base} from './environment.base'
|
||||
import { environment_base } from './environment.base';
|
||||
|
||||
export const environment = environment_base
|
||||
|
||||
environment.keycloak.clientId="dev"
|
||||
environment.keycloak.realm="dev"
|
||||
export const environment = environment_base;
|
||||
environment.mailinfoUrl = 'http://localhost:4200';
|
||||
environment.imageBaseUrl = 'http://localhost:4200';
|
||||
environment.keycloak.clientId = 'dev';
|
||||
environment.keycloak.realm = 'dev';
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Bizmatch - Find Business for sale </title>
|
||||
<meta name="description" content="Find or Sell Businesses and Restaurants">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="googlebot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
|
||||
<meta name="bingbot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="front-page - BizMatch">
|
||||
<meta property="og:description" content="We are dedicated to providing a simple to use way for people in business to get in contact with each other. We are dedicated to providing a simple to use way for people in business to get in contact with each other. Who can use our site: Brokers: We provide an avenue for business and […]">
|
||||
<meta property="og:site_name" content="BizMatch">
|
||||
<meta property="article:modified_time" content="2016-11-17T15:57:10+00:00">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<base href="/">
|
||||
<link rel="icon" href="assets/cropped-Favicon-32x32.png" sizes="32x32">
|
||||
<!-- <link rel="icon" href="cropped-Favicon-192x192.png" sizes="192x192"> -->
|
||||
<!-- <link rel="apple-touch-icon" href="cropped-Favicon-180x180.png"> -->
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
<head>
|
||||
<title>Bizmatch - Find Business for sale</title>
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta name="description" content="Find or Sell Businesses and Restaurants" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta name="googlebot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
|
||||
<meta name="bingbot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="front-page - BizMatch" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="We are dedicated to providing a simple to use way for people in business to get in contact with each other. We are dedicated to providing a simple to use way for people in business to get in contact with each other. Who can use our site: Brokers: We provide an avenue for business and […]"
|
||||
/>
|
||||
<meta property="og:site_name" content="BizMatch" />
|
||||
<meta property="article:modified_time" content="2016-11-17T15:57:10+00:00" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<base href="/" />
|
||||
<link rel="icon" href="assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
||||
<!-- <link rel="icon" href="cropped-Favicon-192x192.png" sizes="192x192"> -->
|
||||
<!-- <link rel="apple-touch-icon" href="cropped-Favicon-180x180.png"> -->
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue