parent
ede8b66d83
commit
630c31cfc9
|
|
@ -1,5 +1,5 @@
|
||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { and, count, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm';
|
import { and, arrayContains, count, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm';
|
||||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
|
|
@ -186,7 +186,14 @@ export class BusinessListingService {
|
||||||
|
|
||||||
return listings.map(l => convertDrizzleBusinessToBusiness(l));
|
return listings.map(l => convertDrizzleBusinessToBusiness(l));
|
||||||
}
|
}
|
||||||
|
// #### Find Favorites ########################################
|
||||||
|
async findFavoriteListings(user: JwtUser): Promise<BusinessListing[]> {
|
||||||
|
const userFavorites = await this.conn
|
||||||
|
.select()
|
||||||
|
.from(businesses)
|
||||||
|
.where(arrayContains(businesses.favoritesForUser, [user.username]));
|
||||||
|
return userFavorites.map(l => convertDrizzleBusinessToBusiness(l));
|
||||||
|
}
|
||||||
// #### CREATE ########################################
|
// #### CREATE ########################################
|
||||||
async createListing(data: BusinessListing): Promise<BusinessListing> {
|
async createListing(data: BusinessListing): Promise<BusinessListing> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -236,6 +243,15 @@ export class BusinessListingService {
|
||||||
async deleteListing(id: string): Promise<void> {
|
async deleteListing(id: string): Promise<void> {
|
||||||
await this.conn.delete(businesses).where(eq(businesses.id, id));
|
await this.conn.delete(businesses).where(eq(businesses.id, id));
|
||||||
}
|
}
|
||||||
|
// #### DELETE Favorite ###################################
|
||||||
|
async deleteFavorite(id: string, user: JwtUser): Promise<void> {
|
||||||
|
await this.conn
|
||||||
|
.update(businesses)
|
||||||
|
.set({
|
||||||
|
favoritesForUser: sql`array_remove(${businesses.favoritesForUser}, ${user.username})`,
|
||||||
|
})
|
||||||
|
.where(sql`${businesses.id} = ${id}`);
|
||||||
|
}
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
// States
|
// States
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGuards } from '@nestjs/common';
|
||||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { BusinessListing } from 'src/models/db.model.js';
|
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
|
import { JwtAuthGuard } from '../jwt-auth/jwt-auth.guard.js';
|
||||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
||||||
|
import { BusinessListing } from '../models/db.model.js';
|
||||||
import { BusinessListingCriteria, JwtUser } from '../models/main.model.js';
|
import { BusinessListingCriteria, JwtUser } from '../models/main.model.js';
|
||||||
import { BusinessListingService } from './business-listing.service.js';
|
import { BusinessListingService } from './business-listing.service.js';
|
||||||
|
|
||||||
|
|
@ -18,7 +19,11 @@ export class BusinessListingsController {
|
||||||
findById(@Request() req, @Param('id') id: string): any {
|
findById(@Request() req, @Param('id') id: string): any {
|
||||||
return this.listingsService.findBusinessesById(id, req.user as JwtUser);
|
return this.listingsService.findBusinessesById(id, req.user as JwtUser);
|
||||||
}
|
}
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get('favorites/all')
|
||||||
|
findFavorites(@Request() req): any {
|
||||||
|
return this.listingsService.findFavoriteListings(req.user as JwtUser);
|
||||||
|
}
|
||||||
@UseGuards(OptionalJwtAuthGuard)
|
@UseGuards(OptionalJwtAuthGuard)
|
||||||
@Get('user/:userid')
|
@Get('user/:userid')
|
||||||
findByUserId(@Request() req, @Param('userid') userid: string): Promise<BusinessListing[]> {
|
findByUserId(@Request() req, @Param('userid') userid: string): Promise<BusinessListing[]> {
|
||||||
|
|
@ -54,4 +59,9 @@ export class BusinessListingsController {
|
||||||
getStates(): any {
|
getStates(): any {
|
||||||
return this.listingsService.getStates();
|
return this.listingsService.getStates();
|
||||||
}
|
}
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Delete('favorites/:id')
|
||||||
|
deleteFavorite(@Request() req, @Param('id') id: string) {
|
||||||
|
this.listingsService.deleteFavorite(id, req.user as JwtUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGu
|
||||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
import { FileService } from '../file/file.service.js';
|
import { FileService } from '../file/file.service.js';
|
||||||
|
import { JwtAuthGuard } from '../jwt-auth/jwt-auth.guard.js';
|
||||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
||||||
import { CommercialPropertyListing } from '../models/db.model';
|
import { CommercialPropertyListing } from '../models/db.model';
|
||||||
import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model.js';
|
import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model.js';
|
||||||
|
|
@ -20,7 +21,11 @@ export class CommercialPropertyListingsController {
|
||||||
findById(@Request() req, @Param('id') id: string): any {
|
findById(@Request() req, @Param('id') id: string): any {
|
||||||
return this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser);
|
return this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser);
|
||||||
}
|
}
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get('favorites/all')
|
||||||
|
findFavorites(@Request() req): any {
|
||||||
|
return this.listingsService.findFavoriteListings(req.user as JwtUser);
|
||||||
|
}
|
||||||
@UseGuards(OptionalJwtAuthGuard)
|
@UseGuards(OptionalJwtAuthGuard)
|
||||||
@Get('user/:email')
|
@Get('user/:email')
|
||||||
findByEmail(@Request() req, @Param('email') email: string): Promise<CommercialPropertyListing[]> {
|
findByEmail(@Request() req, @Param('email') email: string): Promise<CommercialPropertyListing[]> {
|
||||||
|
|
@ -55,4 +60,9 @@ export class CommercialPropertyListingsController {
|
||||||
this.listingsService.deleteListing(id);
|
this.listingsService.deleteListing(id);
|
||||||
this.fileService.deleteDirectoryIfExists(imagePath);
|
this.fileService.deleteDirectoryIfExists(imagePath);
|
||||||
}
|
}
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Delete('favorites/:id')
|
||||||
|
deleteFavorite(@Request() req, @Param('id') id: string) {
|
||||||
|
this.listingsService.deleteFavorite(id, req.user as JwtUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { and, count, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm';
|
import { and, arrayContains, count, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm';
|
||||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
|
|
@ -10,7 +10,7 @@ import { FileService } from '../file/file.service.js';
|
||||||
import { GeoService } from '../geo/geo.service.js';
|
import { GeoService } from '../geo/geo.service.js';
|
||||||
import { CommercialPropertyListing, CommercialPropertyListingSchema } from '../models/db.model.js';
|
import { CommercialPropertyListing, CommercialPropertyListingSchema } from '../models/db.model.js';
|
||||||
import { CommercialPropertyListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js';
|
import { CommercialPropertyListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js';
|
||||||
import { convertCommercialToDrizzleCommercial, convertDrizzleCommercialToCommercial, getDistanceQuery } from '../utils.js';
|
import { convertCommercialToDrizzleCommercial, convertDrizzleBusinessToBusiness, convertDrizzleCommercialToCommercial, getDistanceQuery } from '../utils.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommercialPropertyService {
|
export class CommercialPropertyService {
|
||||||
|
|
@ -123,6 +123,14 @@ export class CommercialPropertyService {
|
||||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||||
return listings.map(l => convertDrizzleCommercialToCommercial(l)) as CommercialPropertyListing[];
|
return listings.map(l => convertDrizzleCommercialToCommercial(l)) as CommercialPropertyListing[];
|
||||||
}
|
}
|
||||||
|
// #### Find Favorites ########################################
|
||||||
|
async findFavoriteListings(user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||||
|
const userFavorites = await this.conn
|
||||||
|
.select()
|
||||||
|
.from(commercials)
|
||||||
|
.where(arrayContains(commercials.favoritesForUser, [user.username]));
|
||||||
|
return userFavorites.map(l => convertDrizzleBusinessToBusiness(l));
|
||||||
|
}
|
||||||
// #### Find by imagePath ########################################
|
// #### Find by imagePath ########################################
|
||||||
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
||||||
const result = await this.conn
|
const result = await this.conn
|
||||||
|
|
@ -202,6 +210,15 @@ export class CommercialPropertyService {
|
||||||
async deleteListing(id: string): Promise<void> {
|
async deleteListing(id: string): Promise<void> {
|
||||||
await this.conn.delete(commercials).where(eq(commercials.id, id));
|
await this.conn.delete(commercials).where(eq(commercials.id, id));
|
||||||
}
|
}
|
||||||
|
// #### DELETE Favorite ###################################
|
||||||
|
async deleteFavorite(id: string, user: JwtUser): Promise<void> {
|
||||||
|
await this.conn
|
||||||
|
.update(commercials)
|
||||||
|
.set({
|
||||||
|
favoritesForUser: sql`array_remove(${commercials.favoritesForUser}, ${user.username})`,
|
||||||
|
})
|
||||||
|
.where(sql`${commercials.id} = ${id}`);
|
||||||
|
}
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
// States
|
// States
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
"ngx-image-cropper": "^8.0.0",
|
"ngx-image-cropper": "^8.0.0",
|
||||||
"ngx-mask": "^18.0.0",
|
"ngx-mask": "^18.0.0",
|
||||||
"ngx-quill": "^26.0.5",
|
"ngx-quill": "^26.0.5",
|
||||||
|
"ngx-sharebuttons": "^15.0.3",
|
||||||
"ngx-stripe": "^18.1.0",
|
"ngx-stripe": "^18.1.0",
|
||||||
"on-change": "^5.0.1",
|
"on-change": "^5.0.1",
|
||||||
"rxjs": "~7.8.1",
|
"rxjs": "~7.8.1",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- <div class="container"> -->
|
<!-- <div class="container"> -->
|
||||||
<div class="min-h-screen flex flex-col" [ngClass]="{ 'bg-slate-100': actualRoute !== 'home' }">
|
<div class="min-h-screen flex flex-col" [ngClass]="{ 'bg-slate-100 print:bg-white': actualRoute !== 'home' }">
|
||||||
@if (actualRoute !=='home'){
|
@if (actualRoute !=='home'){
|
||||||
<header></header>
|
<header></header>
|
||||||
}
|
}
|
||||||
|
|
@ -32,25 +32,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<!-- <div *ngIf="loadingService.isLoading$ | async" class="spinner-overlay">
|
|
||||||
<div class="spinner-container">
|
|
||||||
<ng-container *ngIf="loadingService.loadingText$ | async as loadingText">
|
|
||||||
<div *ngIf="loadingText" class="spinner-text">{{ loadingText }}</div>
|
|
||||||
</ng-container>
|
|
||||||
<div role="status">
|
|
||||||
<svg aria-hidden="true" class="inline w-10 h-10 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
|
||||||
fill="currentFill"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<app-message-container></app-message-container>
|
<app-message-container></app-message-container>
|
||||||
<app-search-modal></app-search-modal>
|
<app-search-modal></app-search-modal>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@a
|
||||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
import { KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular';
|
import { KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular';
|
||||||
import { provideQuillConfig } from 'ngx-quill';
|
import { provideQuillConfig } from 'ngx-quill';
|
||||||
|
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
|
||||||
|
import { shareIcons } from 'ngx-sharebuttons/icons';
|
||||||
import { provideNgxStripe } from 'ngx-stripe';
|
import { provideNgxStripe } from 'ngx-stripe';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { customKeycloakAdapter } from '../keycloak';
|
import { customKeycloakAdapter } from '../keycloak';
|
||||||
|
|
@ -54,6 +56,13 @@ export const appConfig: ApplicationConfig = {
|
||||||
provide: 'TIMEOUT_DURATION',
|
provide: 'TIMEOUT_DURATION',
|
||||||
useValue: 5000, // Standard-Timeout von 5 Sekunden
|
useValue: 5000, // Standard-Timeout von 5 Sekunden
|
||||||
},
|
},
|
||||||
|
provideShareButtonsOptions(
|
||||||
|
shareIcons(),
|
||||||
|
withConfig({
|
||||||
|
debug: true,
|
||||||
|
sharerMethod: SharerMethods.Anchor,
|
||||||
|
}),
|
||||||
|
),
|
||||||
provideRouter(
|
provideRouter(
|
||||||
routes,
|
routes,
|
||||||
withEnabledBlockingInitialNavigation(),
|
withEnabledBlockingInitialNavigation(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<footer class="bg-white px-4 py-2 md:px-6 mt-auto w-full">
|
<footer class="bg-white px-4 py-2 md:px-6 mt-auto w-full print:hidden">
|
||||||
<div class="container mx-auto flex flex-col lg:flex-row justify-between items-center">
|
<div class="container mx-auto flex flex-col lg:flex-row justify-between items-center">
|
||||||
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
||||||
<!-- <img src="assets/images/header-logo.png" alt="BizMatch Logo" class="h-8 mb-2 lg:mb-0 lg:mr-4" /> -->
|
<!-- <img src="assets/images/header-logo.png" alt="BizMatch Logo" class="h-8 mb-2 lg:mb-0 lg:mr-4" /> -->
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<nav class="bg-white border-gray-200 dark:bg-gray-900">
|
<nav class="bg-white border-gray-200 dark:bg-gray-900 print:hidden">
|
||||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
||||||
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||||
<img src="assets/images/header-logo.png" class="h-8" alt="Flowbite Logo" />
|
<img src="assets/images/header-logo.png" class="h-8" alt="Flowbite Logo" />
|
||||||
|
|
@ -18,14 +18,14 @@
|
||||||
}
|
}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex text-sm bg-gray-200 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
class="flex text-sm bg-gray-400 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||||
id="user-menu-button"
|
id="user-menu-button"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
|
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
|
||||||
data-dropdown-placement="bottom"
|
data-dropdown-placement="bottom"
|
||||||
>
|
>
|
||||||
<span class="sr-only">Open user menu</span>
|
<span class="sr-only">Open user menu</span>
|
||||||
@if(user){
|
@if(user?.hasProfile){
|
||||||
<img class="w-8 h-8 rounded-full object-cover" src="{{ profileUrl }}" alt="user photo" />
|
<img class="w-8 h-8 rounded-full object-cover" src="{{ profileUrl }}" alt="user photo" />
|
||||||
} @else {
|
} @else {
|
||||||
<i class="flex justify-center items-center text-stone-50 w-8 h-8 rounded-full fa-solid fa-bars"></i>
|
<i class="flex justify-center items-center text-stone-50 w-8 h-8 rounded-full fa-solid fa-bars"></i>
|
||||||
|
|
@ -57,6 +57,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/myListings" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Listings</a>
|
<a routerLink="/myListings" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Listings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a routerLink="/myFavorites" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Favorites</a>
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/emailUs" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">EMail Us</a>
|
<a routerLink="/emailUs" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">EMail Us</a>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="bg-white rounded-lg shadow-lg overflow-hidden relative">
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden relative">
|
||||||
<button
|
<button
|
||||||
(click)="historyService.goBack()"
|
(click)="historyService.goBack()"
|
||||||
class="absolute top-4 right-4 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
|
class="absolute top-4 right-4 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50 print:hidden"
|
||||||
>
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -23,78 +23,64 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="py-4 print:hidden">
|
||||||
@if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
@if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
||||||
|
<div class="inline">
|
||||||
|
<button class="share share-edit text-white font-bold text-xs py-1.5 px-2 inline-flex items-center" [routerLink]="['/editBusinessListing', listing.id]">
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i>
|
||||||
|
<span class="ml-2">Edit</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
} @if(user){
|
||||||
|
<div class="inline">
|
||||||
|
<button class="share share-save text-white font-bold text-xs py-1.5 px-2 inline-flex items-center" (click)="save()" [disabled]="listing.favoritesForUser.includes(user.email)">
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i>
|
||||||
|
@if(listing.favoritesForUser.includes(user.email)){
|
||||||
|
<span class="ml-2">Saved ...</span>
|
||||||
|
}@else {
|
||||||
|
<span class="ml-2">Save</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<share-button button="print" showText="true"></share-button>
|
||||||
|
<!-- <share-button button="email" showText="true"></share-button> -->
|
||||||
|
<div class="inline">
|
||||||
|
<button class="share share-email text-white font-bold text-xs py-1.5 px-2 inline-flex items-center">
|
||||||
|
<i class="fa-solid fa-envelope"></i>
|
||||||
|
<span class="ml-2">Email</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<share-button button="facebook" showText="true"></share-button>
|
||||||
|
<share-button button="x" showText="true"></share-button>
|
||||||
|
<share-button button="linkedin" showText="true"></share-button>
|
||||||
|
</div>
|
||||||
|
<!-- @if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
||||||
<button
|
<button
|
||||||
[routerLink]="['/editBusinessListing', listing.id]"
|
[routerLink]="['/editBusinessListing', listing.id]"
|
||||||
class="w-full sm:w-auto px-4 py-2 mt-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
|
class="w-full sm:w-auto px-4 py-2 mt-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
}
|
} -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right column -->
|
<!-- Right column -->
|
||||||
<div class="w-full lg:w-1/2 mt-6 lg:mt-0">
|
<div class="w-full lg:w-1/2 mt-6 lg:mt-0 print:hidden">
|
||||||
<h3 class="text-lg font-semibold mb-4">Contact the Author of this Listing</h3>
|
<h3 class="text-lg font-semibold mb-4">Contact the Author of this Listing</h3>
|
||||||
<p class="text-sm mb-4">Please include your contact info below</p>
|
<p class="text-sm mb-4">Please include your contact info below</p>
|
||||||
<form class="space-y-4">
|
<form class="space-y-4">
|
||||||
<!-- <div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="name" class="block text-sm font-medium text-gray-700">Your Name</label>
|
|
||||||
<input type="text" id="name" name="name" [(ngModel)]="mailinfo.sender.name" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Your Email</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
[(ngModel)]="mailinfo.sender.email"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<app-validated-input label="Your Name" name="name" [(ngModel)]="mailinfo.sender.name"></app-validated-input>
|
<app-validated-input label="Your Name" name="name" [(ngModel)]="mailinfo.sender.name"></app-validated-input>
|
||||||
<app-validated-input label="Your Email" name="email" [(ngModel)]="mailinfo.sender.email" kind="email"></app-validated-input>
|
<app-validated-input label="Your Email" name="email" [(ngModel)]="mailinfo.sender.email" kind="email"></app-validated-input>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="phone" class="block text-sm font-medium text-gray-700">Phone Number</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
[(ngModel)]="mailinfo.sender.phoneNumber"
|
|
||||||
placeholder="(123) 456-7890"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="country" class="block text-sm font-medium text-gray-700">Country/State</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="country"
|
|
||||||
name="country"
|
|
||||||
[(ngModel)]="mailinfo.sender.state"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<app-validated-input label="Phone Number" name="phoneNumber" [(ngModel)]="mailinfo.sender.phoneNumber" kind="tel"></app-validated-input>
|
<app-validated-input label="Phone Number" name="phoneNumber" [(ngModel)]="mailinfo.sender.phoneNumber" kind="tel"></app-validated-input>
|
||||||
<app-validated-input label="Country/State" name="state" [(ngModel)]="mailinfo.sender.state"></app-validated-input>
|
<app-validated-input label="Country/State" name="state" [(ngModel)]="mailinfo.sender.state"></app-validated-input>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>
|
|
||||||
<label for="comments" class="block text-sm font-medium text-gray-700">Questions/Comments</label>
|
|
||||||
<textarea
|
|
||||||
id="comments"
|
|
||||||
name="comments"
|
|
||||||
rows="4"
|
|
||||||
[(ngModel)]="mailinfo.sender.comments"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
></textarea>
|
|
||||||
</div> -->
|
|
||||||
<div>
|
<div>
|
||||||
<app-validated-textarea label="Questions/Comments" name="comments" [(ngModel)]="mailinfo.sender.comments"></app-validated-textarea>
|
<app-validated-textarea label="Questions/Comments" name="comments" [(ngModel)]="mailinfo.sender.comments"></app-validated-textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -116,123 +102,3 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <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="font-medium text-3xl text-900 mb-3">{{ listing?.title }}</div>
|
|
||||||
@if(historyService.canGoBack){
|
|
||||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="historyService.goBack()"></p-button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if(listing){
|
|
||||||
<div class="grid">
|
|
||||||
<div class="col-12 md:col-6">
|
|
||||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium flex">Description</div>
|
|
||||||
<div class="text-900 w-full md:w-9 line-height-3 flex flex-column" [innerHTML]="description"></div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Category</div>
|
|
||||||
<div class="text-900 w-full md:w-9">
|
|
||||||
<p-chip [label]="selectOptions.getBusiness(listing.type)"></p-chip>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Located in</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.city }}, {{ selectOptions.getState(listing.state) }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Asking Price</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.price | currency }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Sales revenue</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.salesRevenue | currency }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Cash flow</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.cashFlow | currency }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Type of Real Estate</div>
|
|
||||||
<div class="text-900 w-full md:w-9">
|
|
||||||
@if (listing.realEstateIncluded){
|
|
||||||
<p-chip label="Real Estate Included"></p-chip>
|
|
||||||
} @if (listing.leasedLocation){
|
|
||||||
<p-chip label="Leased Location"></p-chip>
|
|
||||||
} @if (listing.franchiseResale){
|
|
||||||
<p-chip label="Franchise Re-Sale"></p-chip>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Employees</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.employees }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Established since</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.established }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Support & Training</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.supportAndTraining }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Reason for Sale</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.reasonForSale }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
|
||||||
<div class="text-500 w-full md:w-3 font-medium">Broker licensing</div>
|
|
||||||
<div class="text-900 w-full md:w-9">{{ listing.brokerLicencing }}</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
@if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
|
||||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/editBusinessListing', listing.id]"></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
<div class="grid formgrid p-fluid">
|
|
||||||
<div class="field mb-4 col-12 md:col-6">
|
|
||||||
<label for="name" class="font-medium text-900">Your Name</label>
|
|
||||||
<input id="name" type="text" pInputText [(ngModel)]="mailinfo.sender.name" />
|
|
||||||
</div>
|
|
||||||
<div class="field mb-4 col-12 md:col-6">
|
|
||||||
<label for="email" class="font-medium text-900">Your Email</label>
|
|
||||||
<input id="email" type="text" pInputText [(ngModel)]="mailinfo.sender.email" />
|
|
||||||
</div>
|
|
||||||
<div class="field mb-4 col-12 md:col-6">
|
|
||||||
<label for="phoneNumber" class="font-medium text-900">Phone Number</label>
|
|
||||||
<p-inputMask mask="(999) 999-9999" placeholder="(123) 456-7890" [(ngModel)]="mailinfo.sender.phoneNumber"></p-inputMask>
|
|
||||||
</div>
|
|
||||||
<div class="field mb-4 col-12 md:col-6">
|
|
||||||
<label for="state" class="font-medium text-900">Country/State</label>
|
|
||||||
<input id="state" type="text" pInputText [(ngModel)]="mailinfo.sender.state" />
|
|
||||||
</div>
|
|
||||||
<div class="surface-border border-top-1 opacity-50 mb-4 col-12"></div>
|
|
||||||
<div class="field mb-4 col-12">
|
|
||||||
<label for="notes" class="font-medium text-900">Questions/Comments</label>
|
|
||||||
<textarea id="notes" pInputTextarea [autoResize]="true" [rows]="5" [(ngModel)]="mailinfo.sender.comments"></textarea>
|
|
||||||
</div>
|
|
||||||
@if(listingUser){
|
|
||||||
<div class="surface-border mb-4 col-12 flex align-items-center">
|
|
||||||
Listing by <a routerLink="/details-user/{{ listingUser.id }}" class="mr-2 font-semibold">{{ listingUser.firstname }} {{ listingUser.lastname }}</a>
|
|
||||||
@if(listingUser.hasCompanyLogo){
|
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="max-height: 30px; max-width: 100px" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<button pButton pRipple label="Submit" icon="pi pi-file" class="w-auto" (click)="mail()"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Component } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
import { ShareButton } from 'ngx-sharebuttons/button';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
@ -21,7 +22,7 @@ import { createMailInfo, map2User } from '../../../utils/utils';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-details-business-listing',
|
selector: 'app-details-business-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent],
|
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ShareButton],
|
||||||
providers: [],
|
providers: [],
|
||||||
templateUrl: './details-business-listing.component.html',
|
templateUrl: './details-business-listing.component.html',
|
||||||
styleUrl: '../details.scss',
|
styleUrl: '../details.scss',
|
||||||
|
|
@ -145,4 +146,11 @@ export class DetailsBusinessListingComponent {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
save() {
|
||||||
|
this.listing.favoritesForUser.push(this.user.email);
|
||||||
|
this.listingsService.save(this.listing, 'business');
|
||||||
|
}
|
||||||
|
isAlreadyFavorite() {
|
||||||
|
return this.listing.favoritesForUser.includes(this.user.email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,3 +51,22 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
button.share {
|
||||||
|
font-size: 13px;
|
||||||
|
transform: translateY(-2px) scale(1.03);
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-left: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
i {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.share-edit {
|
||||||
|
background-color: #0088cc;
|
||||||
|
}
|
||||||
|
.share-save {
|
||||||
|
background-color: #e60023;
|
||||||
|
}
|
||||||
|
.share-email {
|
||||||
|
background-color: #ff961c;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,54 @@
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<!-- Anzahl der Spalten auf 3 reduziert und den Abstand erhöht -->
|
||||||
|
@for (listing of listings; track listing.id) {
|
||||||
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden hover:shadow-xl">
|
||||||
|
<!-- Hover-Effekt hinzugefügt -->
|
||||||
|
<div class="p-6 flex flex-col h-full relative z-[0]">
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<i [class]="selectOptions.getIconAndTextColorType(listing.type)" class="mr-2 text-xl"></i>
|
||||||
|
<!-- Icon vergrößert -->
|
||||||
|
<span [class]="selectOptions.getTextColorType(listing.type)" class="font-bold text-lg">{{ selectOptions.getBusiness(listing.type) }}</span>
|
||||||
|
<!-- Schriftgröße erhöht -->
|
||||||
|
</div>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">
|
||||||
|
<!-- Überschrift vergrößert -->
|
||||||
|
{{ listing.title }}
|
||||||
|
@if(listing.draft){
|
||||||
|
<span class="bg-red-100 text-red-800 text-sm font-medium me-2 ml-2 px-2.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300">Draft</span>
|
||||||
|
}
|
||||||
|
</h2>
|
||||||
|
<!-- State Badge -->
|
||||||
|
<span class="w-fit inline-flex items-center justify-center px-2 py-1 mb-4 text-xs font-bold leading-none bg-gray-200 text-gray-700 rounded-full">
|
||||||
|
{{ selectOptions.getState(listing.location.state) }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Asking Price hervorgehoben -->
|
||||||
|
<p class="text-base font-bold text-gray-800 mb-2">
|
||||||
|
<strong>Asking price:</strong> <span class="text-green-600"> {{ listing.price | currency : 'USD' : 'symbol' : '1.0-0' }}</span>
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-gray-600 mb-2"><strong>Sales revenue:</strong> {{ listing.salesRevenue | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
|
||||||
|
<p class="text-sm text-gray-600 mb-2"><strong>Net profit:</strong> {{ listing.cashFlow | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
|
||||||
|
<p class="text-sm text-gray-600 mb-2"><strong>Location:</strong> {{ listing.location.name }}</p>
|
||||||
|
<p class="text-sm text-gray-600 mb-4"><strong>Established:</strong> {{ listing.established }}</p>
|
||||||
|
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" alt="Company logo" class="absolute bottom-[80px] right-[20px] h-[45px] w-auto" />
|
||||||
|
<!-- Position und Größe des Bildes angepasst -->
|
||||||
|
<div class="flex-grow"></div>
|
||||||
|
<button
|
||||||
|
class="bg-green-500 text-white px-5 py-3 rounded-full w-full flex items-center justify-center mt-4 transition-colors duration-200 hover:bg-green-600"
|
||||||
|
[routerLink]="['/details-business-listing', listing.id]"
|
||||||
|
>
|
||||||
|
<!-- Button-Größe und Hover-Effekt verbessert -->
|
||||||
|
View Full Listing
|
||||||
|
<i class="fas fa-arrow-right ml-2"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="container mx-auto p-4">
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
@for (listing of listings; track listing.id) {
|
@for (listing of listings; track listing.id) {
|
||||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
|
@ -28,7 +78,7 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
@if(pageCount>1){
|
@if(pageCount>1){
|
||||||
<app-paginator [page]="page" [pageCount]="pageCount" (pageChange)="onPageChange($event)"></app-paginator>
|
<app-paginator [page]="page" [pageCount]="pageCount" (pageChange)="onPageChange($event)"></app-paginator>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,85 @@
|
||||||
<!--
|
<div class="container mx-auto p-4">
|
||||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<h1 class="text-2xl font-bold md:mb-4">My Favorites</h1>
|
||||||
<menu-account></menu-account>
|
|
||||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
<!-- Desktop view -->
|
||||||
<div class="text-900 font-semibold text-lg mt-3">My Favorites</div>
|
<div class="hidden md:block">
|
||||||
<p-divider></p-divider>
|
<table class="w-full bg-white shadow-md rounded-lg overflow-hidden">
|
||||||
<p-table [value]="favorites" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id" [paginator]="true" [rows]="10" [rowsPerPageOptions]="[10, 20, 50]" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
|
<thead class="bg-gray-100">
|
||||||
<ng-template pTemplate="header">
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="wide-column">Title</th>
|
<th class="py-2 px-4 text-left">Title</th>
|
||||||
<th>Category</th>
|
<th class="py-2 px-4 text-left">Category</th>
|
||||||
<th>Located in</th>
|
<th class="py-2 px-4 text-left">Located in</th>
|
||||||
<th></th>
|
<th class="py-2 px-4 text-left">Price</th>
|
||||||
|
<th class="py-2 px-4 text-left">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</thead>
|
||||||
<ng-template pTemplate="body" let-listing>
|
<tbody>
|
||||||
<tr>
|
@for(listing of favorites; track listing){
|
||||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
<tr class="border-b">
|
||||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
<td class="py-2 px-4">{{ listing.title }}</td>
|
||||||
<td>{{ selectOptions.getState(listing.state) }}</td>
|
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
|
||||||
<td>
|
<td class="py-2 px-4">{{ listing.location.name }}, {{ listing.location.state }}</td>
|
||||||
<button pButton pRipple icon="pi pi-eye" class="p-button-rounded p-button-success mr-2" [routerLink]="['/details',listing.id]"></button>
|
<td class="py-2 px-4">${{ listing.price.toLocaleString() }}</td>
|
||||||
|
<td class="py-2 px-4 flex">
|
||||||
|
@if(listing.listingsCategory==='business'){
|
||||||
|
<button class="bg-green-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" [routerLink]="['/details-business-listing', listing.id]">
|
||||||
|
<i class="fa-regular fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
} @if(listing.listingsCategory==='commercialProperty'){
|
||||||
|
<button class="bg-green-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" [routerLink]="['/details-business-listing', listing.id]">
|
||||||
|
<i class="fa-regular fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button class="bg-red-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" (click)="confirmDelete(listing)">
|
||||||
|
<i class="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
}
|
||||||
</p-table>
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile view -->
|
||||||
|
<div class="md:hidden">
|
||||||
|
<div *ngFor="let listing of favorites" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
||||||
|
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
||||||
|
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name }}, {{ listing.location.state }}</p>
|
||||||
|
<p class="text-gray-600 mb-2">Price: ${{ listing.price.toLocaleString() }}</p>
|
||||||
|
<div class="flex justify-start">
|
||||||
|
@if(listing.listingsCategory==='business'){
|
||||||
|
<button class="bg-green-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" [routerLink]="['/details-business-listing', listing.id]">
|
||||||
|
<i class="fa-regular fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
} @if(listing.listingsCategory==='commercialProperty'){
|
||||||
|
<button class="bg-green-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" [routerLink]="['/details-business-listing', listing.id]">
|
||||||
|
<i class="fa-regular fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button class="bg-red-500 text-white w-10 h-10 flex items-center justify-center rounded-full mr-2" (click)="confirmDelete(listing)">
|
||||||
|
<i class="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="flex items-center justify-between mt-4">
|
||||||
|
<p class="text-sm text-gray-600">Showing 1 to 2 of 2 entries</p>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<button class="px-2 py-1 border rounded-l-md bg-gray-100"><<</button>
|
||||||
|
<button class="px-2 py-1 border-t border-b bg-gray-100"><</button>
|
||||||
|
<button class="px-2 py-1 border bg-blue-500 text-white">1</button>
|
||||||
|
<button class="px-2 py-1 border-t border-b bg-gray-100">></button>
|
||||||
|
<button class="px-2 py-1 border rounded-r-md bg-gray-100">>></button>
|
||||||
|
<select class="ml-2 border rounded-md px-2 py-1">
|
||||||
|
<option>10</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-confirmation></app-confirmation>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { KeycloakUser, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { BusinessListing, CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { KeycloakUser } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
||||||
|
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
|
|
@ -9,18 +12,56 @@ import { map2User } from '../../../utils/utils';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-favorites',
|
selector: 'app-favorites',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule],
|
imports: [SharedModule, ConfirmationComponent],
|
||||||
templateUrl: './favorites.component.html',
|
templateUrl: './favorites.component.html',
|
||||||
styleUrl: './favorites.component.scss',
|
styleUrl: './favorites.component.scss',
|
||||||
})
|
})
|
||||||
export class FavoritesComponent {
|
export class FavoritesComponent {
|
||||||
user: KeycloakUser;
|
user: KeycloakUser;
|
||||||
listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>;
|
// listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>;
|
||||||
favorites: Array<ListingType>;
|
favorites: Array<BusinessListing | CommercialPropertyListing>;
|
||||||
constructor(public keycloakService: KeycloakService, private listingsService: ListingsService, public selectOptions: SelectOptionsService) {}
|
constructor(public keycloakService: KeycloakService, private listingsService: ListingsService, public selectOptions: SelectOptionsService, private confirmationService: ConfirmationService) {}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const token = await this.keycloakService.getToken();
|
const token = await this.keycloakService.getToken();
|
||||||
this.user = map2User(token);
|
this.user = map2User(token);
|
||||||
this.favorites = this.listings.filter(l => l.favoritesForUser?.includes(this.user.id));
|
const result = await Promise.all([await this.listingsService.getFavoriteListings('business'), await this.listingsService.getFavoriteListings('commercialProperty')]);
|
||||||
|
this.favorites = [...result[0], ...result[1]];
|
||||||
|
}
|
||||||
|
async confirmDelete(listing: BusinessListing | CommercialPropertyListing) {
|
||||||
|
const confirmed = await this.confirmationService.showConfirmation({ message: `Are you sure you want to remove this listing from your Favorites?` });
|
||||||
|
if (confirmed) {
|
||||||
|
// this.messageService.showMessage('Listing has been deleted');
|
||||||
|
this.deleteListing(listing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async deleteListing(listing: BusinessListing | CommercialPropertyListing) {
|
||||||
|
if (listing.listingsCategory === 'business') {
|
||||||
|
await this.listingsService.removeFavorite(listing.id, 'business');
|
||||||
|
} else {
|
||||||
|
await this.listingsService.removeFavorite(listing.id, 'commercialProperty');
|
||||||
|
}
|
||||||
|
const result = await Promise.all([await this.listingsService.getFavoriteListings('business'), await this.listingsService.getFavoriteListings('commercialProperty')]);
|
||||||
|
this.favorites = [...result[0], ...result[1]];
|
||||||
|
}
|
||||||
|
// listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||||
|
// myListings: Array<ListingType>;
|
||||||
|
// user: User;
|
||||||
|
// constructor(
|
||||||
|
// public userService: UserService,
|
||||||
|
// public keycloakService: KeycloakService,
|
||||||
|
// private listingsService: ListingsService,
|
||||||
|
// private cdRef: ChangeDetectorRef,
|
||||||
|
// public selectOptions: SelectOptionsService,
|
||||||
|
// private messageService: MessageService,
|
||||||
|
// private confirmationService: ConfirmationService,
|
||||||
|
// ) {}
|
||||||
|
// async ngOnInit() {
|
||||||
|
// // const keycloakUser = this.userService.getKeycloakUser();
|
||||||
|
// const token = await this.keycloakService.getToken();
|
||||||
|
// const keycloakUser = map2User(token);
|
||||||
|
// const email = keycloakUser.email;
|
||||||
|
// this.user = await this.userService.getByMail(email);
|
||||||
|
// const result = await Promise.all([await this.listingsService.getListingsByEmail(this.user.email, 'business'), await this.listingsService.getListingsByEmail(this.user.email, 'commercialProperty')]);
|
||||||
|
// this.myListings = [...result[0], ...result[1]];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
<div class="bg-white rounded-lg shadow-md p-6">
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
<h1 class="text-2xl font-bold mb-4">My Listings</h1>
|
<h1 class="text-2xl font-bold md:mb-4">My Listings</h1>
|
||||||
|
|
||||||
<!-- Desktop view -->
|
<!-- Desktop view -->
|
||||||
<div class="hidden md:block">
|
<div class="hidden md:block">
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<th class="py-2 px-4 text-left">Title</th>
|
<th class="py-2 px-4 text-left">Title</th>
|
||||||
<th class="py-2 px-4 text-left">Category</th>
|
<th class="py-2 px-4 text-left">Category</th>
|
||||||
<th class="py-2 px-4 text-left">Located in</th>
|
<th class="py-2 px-4 text-left">Located in</th>
|
||||||
|
<th class="py-2 px-4 text-left">Price</th>
|
||||||
<th class="py-2 px-4 text-left">Actions</th>
|
<th class="py-2 px-4 text-left">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -17,7 +18,8 @@
|
||||||
<tr *ngFor="let listing of myListings" class="border-b">
|
<tr *ngFor="let listing of myListings" class="border-b">
|
||||||
<td class="py-2 px-4">{{ listing.title }}</td>
|
<td class="py-2 px-4">{{ listing.title }}</td>
|
||||||
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
|
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
|
||||||
<td class="py-2 px-4">{{ listing.location.state }}</td>
|
<td class="py-2 px-4">{{ listing.location.name }}, {{ listing.location.state }}</td>
|
||||||
|
<td class="py-2 px-4">${{ listing.price.toLocaleString() }}</td>
|
||||||
<td class="py-2 px-4">
|
<td class="py-2 px-4">
|
||||||
@if(listing.listingsCategory==='business'){
|
@if(listing.listingsCategory==='business'){
|
||||||
<button class="bg-green-500 text-white p-2 rounded-full mr-2" [routerLink]="['/editBusinessListing', listing.id]">
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2" [routerLink]="['/editBusinessListing', listing.id]">
|
||||||
|
|
@ -52,8 +54,9 @@
|
||||||
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
||||||
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
||||||
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
||||||
<p class="text-gray-600 mb-4">Located in: {{ listing.location.name }} - {{ listing.location.state }}</p>
|
<p class="text-gray-600 mb-2">Located in: {{ listing.location.name }} - {{ listing.location.state }}</p>
|
||||||
<div class="flex justify-end">
|
<p class="text-gray-600 mb-2">Price: ${{ listing.price.toLocaleString() }}</p>
|
||||||
|
<div class="flex justify-start">
|
||||||
<button class="bg-green-500 text-white p-2 rounded-full mr-2">
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
|
|
@ -72,7 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between mt-4">
|
<!-- <div class="flex items-center justify-between mt-4">
|
||||||
<p class="text-sm text-gray-600">Showing 1 to 2 of 2 entries</p>
|
<p class="text-sm text-gray-600">Showing 1 to 2 of 2 entries</p>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<button class="px-2 py-1 border rounded-l-md bg-gray-100"><<</button>
|
<button class="px-2 py-1 border rounded-l-md bg-gray-100"><<</button>
|
||||||
|
|
@ -84,52 +87,7 @@
|
||||||
<option>10</option>
|
<option>10</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-confirmation></app-confirmation>
|
<app-confirmation></app-confirmation>
|
||||||
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
|
||||||
<menu-account></menu-account>
|
|
||||||
<p-toast></p-toast>
|
|
||||||
<p-confirmPopup></p-confirmPopup>
|
|
||||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
|
||||||
<div class="text-900 font-semibold text-lg mt-3">My Listings</div>
|
|
||||||
<p-divider></p-divider>
|
|
||||||
<p-table
|
|
||||||
[value]="myListings"
|
|
||||||
[tableStyle]="{ 'min-width': '50rem' }"
|
|
||||||
dataKey="id"
|
|
||||||
[paginator]="true"
|
|
||||||
[rows]="10"
|
|
||||||
[rowsPerPageOptions]="[10, 20, 50]"
|
|
||||||
[showCurrentPageReport]="true"
|
|
||||||
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
|
|
||||||
>
|
|
||||||
<ng-template pTemplate="header">
|
|
||||||
<tr>
|
|
||||||
<th class="wide-column">Title</th>
|
|
||||||
<th>Category</th>
|
|
||||||
<th>Located in</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template pTemplate="body" let-listing>
|
|
||||||
<tr>
|
|
||||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
|
||||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
|
||||||
<td>{{ selectOptions.getState(listing.state) }}</td>
|
|
||||||
<td>
|
|
||||||
@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(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>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
</p-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, lastValueFrom } from 'rxjs';
|
import { Observable, lastValueFrom } from 'rxjs';
|
||||||
import { BusinessListing } from '../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, CommercialPropertyListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
import {
|
import {
|
||||||
BusinessListingCriteria,
|
BusinessListingCriteria,
|
||||||
CommercialPropertyListingCriteria,
|
CommercialPropertyListingCriteria,
|
||||||
|
|
@ -40,6 +40,9 @@ export class ListingsService {
|
||||||
getListingsByEmail(email: string, listingsCategory: 'business' | 'commercialProperty'): Promise<ListingType[]> {
|
getListingsByEmail(email: string, listingsCategory: 'business' | 'commercialProperty'): Promise<ListingType[]> {
|
||||||
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/user/${email}`));
|
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/user/${email}`));
|
||||||
}
|
}
|
||||||
|
getFavoriteListings(listingsCategory: 'business' | 'commercialProperty'): Promise<ListingType[]> {
|
||||||
|
return lastValueFrom(this.http.get<BusinessListing[] | CommercialPropertyListing[]>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/favorites/all`));
|
||||||
|
}
|
||||||
async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
||||||
if (listing.id) {
|
if (listing.id) {
|
||||||
return await lastValueFrom(this.http.put<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`, listing));
|
return await lastValueFrom(this.http.put<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`, listing));
|
||||||
|
|
@ -57,4 +60,7 @@ export class ListingsService {
|
||||||
async deleteCommercialPropertyListing(id: string, imagePath: string) {
|
async deleteCommercialPropertyListing(id: string, imagePath: string) {
|
||||||
await lastValueFrom(this.http.delete<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/${id}/${imagePath}`));
|
await lastValueFrom(this.http.delete<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/${id}/${imagePath}`));
|
||||||
}
|
}
|
||||||
|
async removeFavorite(id: string, listingsCategory?: 'business' | 'commercialProperty') {
|
||||||
|
await lastValueFrom(this.http.delete<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/favorites/${id}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,7 @@
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:title" content="front-page - BizMatch" />
|
<meta property="og:title" content="front-page - BizMatch" />
|
||||||
<meta
|
<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." />
|
||||||
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="og:site_name" content="BizMatch" />
|
||||||
<meta property="article:modified_time" content="2016-11-17T15:57:10+00:00" />
|
<meta property="article:modified_time" content="2016-11-17T15:57:10+00:00" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<meta property="og:image:width" content="1200" />
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
@import 'tailwindcss/base';
|
@import 'tailwindcss/base';
|
||||||
@import 'tailwindcss/components';
|
@import 'tailwindcss/components';
|
||||||
@import 'tailwindcss/utilities';
|
@import 'tailwindcss/utilities';
|
||||||
|
@import 'ngx-sharebuttons/themes/default';
|
||||||
:root {
|
:root {
|
||||||
--text-color-secondary: rgba(255, 255, 255);
|
--text-color-secondary: rgba(255, 255, 255);
|
||||||
--wrapper-width: 1491px;
|
--wrapper-width: 1491px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue