Draft Mode inkl. Token implementiert
This commit is contained in:
parent
226d2ebc1e
commit
b4cf17b8ea
|
|
@ -48,6 +48,9 @@ public
|
|||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.env.prod
|
||||
.env.dev
|
||||
.env.test
|
||||
|
||||
# temp directory
|
||||
.temp
|
||||
|
|
|
|||
|
|
@ -6,17 +6,15 @@
|
|||
"request": "launch",
|
||||
"name": "Debug Nest Framework",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"start:debug",
|
||||
"--",
|
||||
"--inspect-brk"
|
||||
],
|
||||
"runtimeArgs": ["run", "start:debug", "--", "--inspect-brk"],
|
||||
"autoAttachChildProcesses": true,
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"stopOnEntry": false,
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"HOST_NAME": "localhost"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
|
@ -24,9 +22,7 @@
|
|||
"name": "Debug Current TS File",
|
||||
"program": "${workspaceFolder}/dist/src/drizzle/${fileBasenameNoExtension}.js",
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"outFiles": ["${workspaceFolder}/out/**/*.js"],
|
||||
"sourceMaps": true,
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
|
|
@ -35,31 +31,21 @@
|
|||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "generateDefs",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}/dist/src/drizzle/generateDefs.js",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/src/drizzle/**/*.js"
|
||||
],
|
||||
"outFiles": ["${workspaceFolder}/dist/src/drizzle/**/*.js"],
|
||||
"sourceMaps": true,
|
||||
"smartStep": true,
|
||||
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "generateTypes",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}/dist/src/drizzle/generateTypes.js",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/src/drizzle/**/*.js"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"smartStep": true,
|
||||
|
||||
},
|
||||
"smartStep": true
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "generateTypes",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}/dist/src/drizzle/generateTypes.js",
|
||||
"outFiles": ["${workspaceFolder}/dist/src/drizzle/**/*.js"],
|
||||
"sourceMaps": true,
|
||||
"smartStep": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -9,10 +9,10 @@
|
|||
"scripts": {
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start": "HOST_NAME=localhost nest start",
|
||||
"start:dev": "HOST_NAME=dev.bizmatch.net nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod": "HOST_NAME=www.bizmatch.net node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import * as dotenv from 'dotenv';
|
||||
import fs from 'fs-extra';
|
||||
import { WinstonModule, utilities as nestWinstonModuleUtilities } from 'nest-winston';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
|
@ -14,13 +17,37 @@ import { ListingsModule } from './listings/listings.module.js';
|
|||
import { MailModule } from './mail/mail.module.js';
|
||||
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware.js';
|
||||
import { SelectOptionsModule } from './select-options/select-options.module.js';
|
||||
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { UserModule } from './user/user.module.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
function loadEnvFiles() {
|
||||
// Load the .env file
|
||||
dotenv.config();
|
||||
console.log('Loaded .env file');
|
||||
|
||||
// Determine which additional env file to load
|
||||
let envFilePath = '';
|
||||
const host = process.env.HOST_NAME || '';
|
||||
|
||||
if (host.includes('localhost')) {
|
||||
envFilePath = '.env.local';
|
||||
} else if (host.includes('dev.bizmatch.net')) {
|
||||
envFilePath = '.env.dev';
|
||||
} else if (host.includes('www.bizmatch.net') || host.includes('bizmatch.net')) {
|
||||
envFilePath = '.env.prod';
|
||||
}
|
||||
|
||||
// Load the additional env file if it exists
|
||||
if (fs.existsSync(envFilePath)) {
|
||||
dotenv.config({ path: envFilePath });
|
||||
console.log(`Loaded ${envFilePath} file`);
|
||||
} else {
|
||||
console.log(`No additional .env file found for HOST_NAME: ${host}`);
|
||||
}
|
||||
}
|
||||
|
||||
loadEnvFiles();
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
|
|
@ -42,13 +69,6 @@ const __dirname = path.dirname(__filename);
|
|||
],
|
||||
// other options
|
||||
}),
|
||||
// KeycloakConnectModule.register({
|
||||
// authServerUrl: 'http://auth.bizmatch.net',
|
||||
// realm: 'dev',
|
||||
// clientId: 'dev',
|
||||
// secret: 'Yu3lETbYUphDiJxgnhhpelcJ63p2FCDM',
|
||||
// // Secret key of the client taken from keycloak server
|
||||
// }),
|
||||
GeoModule,
|
||||
UserModule,
|
||||
ListingsModule,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export class ImageController {
|
|||
@Delete('propertyPicture/:imagePath/:serial/:imagename')
|
||||
async deletePropertyImagesById(@Param('imagePath') imagePath: string, @Param('serial') serial: string, @Param('imagename') imagename: string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/property/${imagePath}/${serial}/${imagename}`);
|
||||
await this.listingService.deleteImage(imagePath, serial, imagename);
|
||||
}
|
||||
// ############
|
||||
// Profile
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { passportJwtSecret } from 'jwks-rsa';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { JwtUser } from './models/main.model';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor() {
|
||||
constructor(configService: ConfigService) {
|
||||
const realm = configService.get<string>('REALM');
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
|
|
@ -13,15 +16,16 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
|||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 5,
|
||||
jwksUri: 'https://auth.bizmatch.net/realms/dev/protocol/openid-connect/certs',
|
||||
jwksUri: `https://auth.bizmatch.net/realms/${realm}/protocol/openid-connect/certs`,
|
||||
}),
|
||||
audience: 'account', // Keycloak Client ID
|
||||
issuer: 'https://auth.bizmatch.net/realms/dev',
|
||||
authorize: '',
|
||||
issuer: `https://auth.bizmatch.net/realms/${realm}`,
|
||||
algorithms: ['RS256'],
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
async validate(payload: any): Promise<JwtUser> {
|
||||
console.log('JWT Payload:', payload); // Debugging: JWT Payload anzeigen
|
||||
if (!payload) {
|
||||
console.error('Invalid payload');
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
|||
import { Logger } from 'winston';
|
||||
import { businesses } from '../drizzle/schema.js';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
||||
import { ListingCriteria } from '../models/main.model.js';
|
||||
import { JwtUser, ListingCriteria } from '../models/main.model.js';
|
||||
import { ListingsService } from './listings.service.js';
|
||||
|
||||
@Controller('listings/business')
|
||||
|
|
@ -13,15 +13,16 @@ export class BusinessListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Get(':id')
|
||||
findById(@Param('id') id: string): any {
|
||||
return this.listingsService.findById(id, businesses);
|
||||
findById(@Request() req, @Param('id') id: string): any {
|
||||
return this.listingsService.findBusinessesById(id, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Get('user/:userid')
|
||||
findByUserId(@Request() req, @Param('userid') userid: string): any {
|
||||
return this.listingsService.findByUserId(userid, businesses, req.user?.username);
|
||||
return this.listingsService.findBusinessesByEmail(userid, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@Post('search')
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Request, UseGuards } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { CommercialPropertyListing } from 'src/models/db.model.js';
|
||||
import { Logger } from 'winston';
|
||||
import { commercials } from '../drizzle/schema.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
||||
import { ListingCriteria } from '../models/main.model.js';
|
||||
import { JwtUser, ListingCriteria } from '../models/main.model.js';
|
||||
import { ListingsService } from './listings.service.js';
|
||||
|
||||
@Controller('listings/commercialProperty')
|
||||
|
|
@ -15,16 +16,16 @@ export class CommercialPropertyListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Get(':id')
|
||||
findById(@Param('id') id: string): any {
|
||||
return this.listingsService.findById(id, commercials);
|
||||
findById(@Request() req, @Param('id') id: string): any {
|
||||
return this.listingsService.findCommercialPropertiesById(id, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Get('user/:userid')
|
||||
findByUserId(@Request() req, @Param('userid') userid: string): any {
|
||||
console.log(req.user?.username);
|
||||
return this.listingsService.findByUserId(userid, commercials, req.user?.username);
|
||||
@Get('user/:email')
|
||||
findByEmail(@Request() req, @Param('email') email: string): Promise<CommercialPropertyListing[]> {
|
||||
return this.listingsService.findCommercialPropertiesByEmail(email, req.user as JwtUser);
|
||||
}
|
||||
@Post('search')
|
||||
async find(@Body() criteria: ListingCriteria): Promise<any> {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Logger } from 'winston';
|
|||
import * as schema from '../drizzle/schema.js';
|
||||
import { PG_CONNECTION, businesses, commercials } from '../drizzle/schema.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
import { ListingCriteria } from '../models/main.model.js';
|
||||
import { JwtUser, ListingCriteria, emailToDirName } from '../models/main.model.js';
|
||||
|
||||
@Injectable()
|
||||
export class ListingsService {
|
||||
|
|
@ -64,12 +64,21 @@ export class ListingsService {
|
|||
]);
|
||||
return { total, data };
|
||||
}
|
||||
async findById(id: string, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
const result = await this.conn
|
||||
async findCommercialPropertiesById(id: string, user: JwtUser): Promise<CommercialPropertyListing> {
|
||||
let result = await this.conn
|
||||
.select()
|
||||
.from(table)
|
||||
.where(and(sql`${table.id} = ${id}`, ne(table.draft, true)));
|
||||
return result[0] as BusinessListing | CommercialPropertyListing;
|
||||
.from(commercials)
|
||||
.where(and(sql`${commercials.id} = ${id}`));
|
||||
result = result.filter(r => !r.draft || r.imagePath === emailToDirName(user.username) || user.roles.includes('ADMIN'));
|
||||
return result[0] as CommercialPropertyListing;
|
||||
}
|
||||
async findBusinessesById(id: string, user: JwtUser): Promise<CommercialPropertyListing> {
|
||||
let result = await this.conn
|
||||
.select()
|
||||
.from(businesses)
|
||||
.where(and(sql`${businesses.id} = ${id}`));
|
||||
result = result.filter(r => !r.draft || r.imageName === emailToDirName(user.username) || user.roles.includes('ADMIN'));
|
||||
return result[0] as BusinessListing;
|
||||
}
|
||||
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
||||
const result = await this.conn
|
||||
|
|
@ -78,10 +87,28 @@ export class ListingsService {
|
|||
.where(and(sql`${commercials.imagePath} = ${imagePath}`, sql`${commercials.serialId} = ${serial}`, ne(commercials.draft, true)));
|
||||
return result[0] as CommercialPropertyListing;
|
||||
}
|
||||
async findByUserId(userId: string, table: typeof businesses | typeof commercials, email: string): Promise<BusinessListing[] | CommercialPropertyListing[]> {
|
||||
return (await this.conn.select().from(table).where(eq(table.userId, userId))) as BusinessListing[] | CommercialPropertyListing[];
|
||||
async findCommercialPropertiesByEmail(email: string, user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||
const conditions = [];
|
||||
conditions.push(eq(commercials.imagePath, emailToDirName(email)));
|
||||
if (email !== user.username && !user.roles.includes('ADMIN')) {
|
||||
conditions.push(ne(commercials.draft, true));
|
||||
}
|
||||
return (await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||
}
|
||||
async findBusinessesByEmail(email: string, user: JwtUser): Promise<BusinessListing[]> {
|
||||
const conditions = [];
|
||||
conditions.push(eq(businesses.imageName, emailToDirName(email)));
|
||||
if (email !== user.username && !user.roles.includes('ADMIN')) {
|
||||
conditions.push(ne(businesses.draft, true));
|
||||
}
|
||||
return (await this.conn
|
||||
.select()
|
||||
.from(businesses)
|
||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||
}
|
||||
|
||||
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
data.created = new Date();
|
||||
data.updated = new Date();
|
||||
|
|
@ -120,8 +147,8 @@ export class ListingsService {
|
|||
// ##############################################################
|
||||
// Images for commercial Properties
|
||||
// ##############################################################
|
||||
async deleteImage(id: string, name: string) {
|
||||
const listing = (await this.findById(id, commercials)) as unknown as CommercialPropertyListing;
|
||||
async deleteImage(imagePath: string, serial: string, name: string) {
|
||||
const listing = (await this.findByImagePath(imagePath, serial)) as unknown as CommercialPropertyListing;
|
||||
const index = listing.imageOrder.findIndex(im => im === name);
|
||||
if (index > -1) {
|
||||
listing.imageOrder.splice(index, 1);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Controller, Get, Inject, Param } from '@nestjs/common';
|
||||
import { Controller, Inject } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
import { businesses, commercials } from '../drizzle/schema.js';
|
||||
import { ListingsService } from './listings.service.js';
|
||||
|
||||
@Controller('listings/undefined')
|
||||
|
|
@ -11,13 +10,13 @@ export class UnknownListingsController {
|
|||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@Get(':id')
|
||||
async findById(@Param('id') id: string): Promise<any> {
|
||||
const result = await this.listingsService.findById(id, businesses);
|
||||
if (result) {
|
||||
return result;
|
||||
} else {
|
||||
return await this.listingsService.findById(id, commercials);
|
||||
}
|
||||
}
|
||||
// @Get(':id')
|
||||
// async findById(@Param('id') id: string): Promise<any> {
|
||||
// const result = await this.listingsService.findById(id, businesses);
|
||||
// if (result) {
|
||||
// return result;
|
||||
// } else {
|
||||
// return await this.listingsService.findById(id, commercials);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ export interface KeycloakUser {
|
|||
notBefore?: number;
|
||||
access?: Access;
|
||||
}
|
||||
export interface JwtUser {
|
||||
userId: string;
|
||||
username: string;
|
||||
roles: string[];
|
||||
}
|
||||
export interface Access {
|
||||
manageGroupMembership: boolean;
|
||||
view: boolean;
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ export class DetailsUserComponent {
|
|||
|
||||
async ngOnInit() {
|
||||
this.user = await this.userService.getById(this.id);
|
||||
this.user.email;
|
||||
const results = await Promise.all([await this.listingsService.getListingByUserId(this.id, 'business'), await this.listingsService.getListingByUserId(this.id, 'commercialProperty')]);
|
||||
const results = await Promise.all([await this.listingsService.getListingsByEmail(this.user.email, 'business'), await this.listingsService.getListingsByEmail(this.user.email, 'commercialProperty')]);
|
||||
// Zuweisen der Ergebnisse zu den Member-Variablen der Klasse
|
||||
this.businessListings = results[0];
|
||||
this.commercialPropListings = results[1];
|
||||
|
|
|
|||
|
|
@ -82,58 +82,66 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<div class="flex flex-column align-items-center flex-or">
|
||||
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
||||
<span class="font-light text-sm text-900 mb-2">(Pictures can be uploaded once the listing is posted initially)</span>
|
||||
<!-- <img [src]="propertyPictureUrl" (error)="setImageToFallback($event)" class="image"/> -->
|
||||
<p-fileUpload
|
||||
mode="basic"
|
||||
chooseLabel="Upload"
|
||||
[customUpload]="true"
|
||||
name="file"
|
||||
accept="image/*"
|
||||
[maxFileSize]="maxFileSize"
|
||||
(onSelect)="select($event)"
|
||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
||||
[disabled]="!listing.id"
|
||||
>
|
||||
</p-fileUpload>
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<p-inputSwitch inputId="draft" [(ngModel)]="listing.draft"></p-inputSwitch>
|
||||
<span class="ml-2 text-900 absolute translate-y-5">Draft Mode (Will not be shown as public listing)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||
<div class="flex-auto p-fluid">
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
<div class="flex flex-column align-items-center flex-or">
|
||||
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
||||
<span class="font-light text-sm text-900 mb-2">(Pictures can be uploaded once the listing is posted initially)</span>
|
||||
<!-- <img [src]="propertyPictureUrl" (error)="setImageToFallback($event)" class="image"/> -->
|
||||
<p-fileUpload
|
||||
mode="basic"
|
||||
chooseLabel="Upload"
|
||||
[customUpload]="true"
|
||||
name="file"
|
||||
accept="image/*"
|
||||
[maxFileSize]="maxFileSize"
|
||||
(onSelect)="select($event)"
|
||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
||||
[disabled]="!listing.id"
|
||||
>
|
||||
</p-fileUpload>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (listing && listing.imageOrder?.length>0){
|
||||
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
||||
@for (image of listing.imageOrder; track listing.imageOrder) {
|
||||
<span cdkDropList mixedCdkDropList>
|
||||
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.serialId }}/{{ image }}?_ts={{ ts }}" [alt]="image" class="shadow-2" cdkDrag />
|
||||
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image)"></fa-icon>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
@if (mode==='create'){
|
||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
||||
} @else {
|
||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
||||
@if (listing && listing.imageOrder?.length>0){
|
||||
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
||||
@for (image of listing.imageOrder; track listing.imageOrder) {
|
||||
<span cdkDropList mixedCdkDropList>
|
||||
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.serialId }}/{{ image }}?_ts={{ ts }}" [alt]="image" class="shadow-2" cdkDrag />
|
||||
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image)"></fa-icon>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
@if (mode==='create'){
|
||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
||||
} @else {
|
||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmDialog></p-confirmDialog>
|
||||
</div>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmDialog></p-confirmDialog>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export class MyListingComponent {
|
|||
const keycloakUser = map2User(token);
|
||||
const email = keycloakUser.email;
|
||||
this.user = await this.userService.getByMail(email);
|
||||
const result = await Promise.all([await this.listingsService.getListingByUserId(this.user.id, 'business'), await this.listingsService.getListingByUserId(this.user.id, 'commercialProperty')]);
|
||||
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]];
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ export class MyListingComponent {
|
|||
} else {
|
||||
await this.listingsService.deleteCommercialPropertyListing(listing.id, (<CommercialPropertyListing>listing).imagePath);
|
||||
}
|
||||
const result = await Promise.all([await this.listingsService.getListingByUserId(this.user.id, 'business'), await this.listingsService.getListingByUserId(this.user.id, 'commercialProperty')]);
|
||||
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]];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ export class ListingsService {
|
|||
const result = this.http.get<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`);
|
||||
return result;
|
||||
}
|
||||
getListingByUserId(userid: string, listingsCategory: 'business' | 'commercialProperty'): Promise<ListingType[]> {
|
||||
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/user/${userid}`));
|
||||
getListingsByEmail(email: string, listingsCategory: 'business' | 'commercialProperty'): Promise<ListingType[]> {
|
||||
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/user/${email}`));
|
||||
}
|
||||
async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
||||
if (listing.id) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue