diverse BugFixes
This commit is contained in:
parent
3a6a64cce9
commit
ec0576e7b8
|
|
@ -102,7 +102,7 @@ const USStates = z.enum([
|
||||||
'WY',
|
'WY',
|
||||||
]);
|
]);
|
||||||
export const AreasServedSchema = z.object({
|
export const AreasServedSchema = z.object({
|
||||||
county: z.string().nonempty('County is required'),
|
county: z.string().optional().nullable(),
|
||||||
state: z.string().nonempty('State is required'),
|
state: z.string().nonempty('State is required'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,7 @@ export interface CommercialPropertyListingCriteria extends ListCriteria {
|
||||||
criteriaType: 'commercialPropertyListings';
|
criteriaType: 'commercialPropertyListings';
|
||||||
}
|
}
|
||||||
export interface UserListingCriteria extends ListCriteria {
|
export interface UserListingCriteria extends ListCriteria {
|
||||||
firstname: string;
|
brokerName: string;
|
||||||
lastname: string;
|
|
||||||
companyName: string;
|
companyName: string;
|
||||||
counties: string[];
|
counties: string[];
|
||||||
criteriaType: 'brokerListings';
|
criteriaType: 'brokerListings';
|
||||||
|
|
|
||||||
|
|
@ -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 { User, UserSchema } from '../models/db.model.js';
|
import { User, UserSchema } from '../models/db.model.js';
|
||||||
import { createDefaultUser, emailToDirName, JwtUser, UserListingCriteria } from '../models/main.model.js';
|
import { createDefaultUser, emailToDirName, JwtUser, UserListingCriteria } from '../models/main.model.js';
|
||||||
import { convertDrizzleUserToUser, convertUserToDrizzleUser, getDistanceQuery } from '../utils.js';
|
import { convertDrizzleUserToUser, convertUserToDrizzleUser, getDistanceQuery, splitName } from '../utils.js';
|
||||||
|
|
||||||
type CustomerSubType = (typeof customerSubTypeEnum.enumValues)[number];
|
type CustomerSubType = (typeof customerSubTypeEnum.enumValues)[number];
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -37,12 +37,9 @@ export class UserService {
|
||||||
whereConditions.push(inArray(schema.users.customerSubType, criteria.types as CustomerSubType[]));
|
whereConditions.push(inArray(schema.users.customerSubType, criteria.types as CustomerSubType[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.firstname) {
|
if (criteria.brokerName) {
|
||||||
whereConditions.push(ilike(schema.users.firstname, `%${criteria.firstname}%`));
|
const { firstname, lastname } = splitName(criteria.brokerName);
|
||||||
}
|
whereConditions.push(or(ilike(schema.users.firstname, `%${firstname}%`), ilike(schema.users.lastname, `%${lastname}%`)));
|
||||||
|
|
||||||
if (criteria.lastname) {
|
|
||||||
whereConditions.push(ilike(schema.users.lastname, `%${criteria.lastname}%`));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.companyName) {
|
if (criteria.companyName) {
|
||||||
|
|
|
||||||
|
|
@ -139,3 +139,16 @@ function unflattenObject(obj: any, separator: string = '_'): any {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
export function splitName(fullName: string): { firstname: string; lastname: string } {
|
||||||
|
const parts = fullName.trim().split(/\s+/); // Teile den Namen am Leerzeichen auf
|
||||||
|
|
||||||
|
if (parts.length === 1) {
|
||||||
|
// Falls es nur ein Teil gibt, ist firstname und lastname gleich
|
||||||
|
return { firstname: parts[0], lastname: parts[0] };
|
||||||
|
} else {
|
||||||
|
// Ansonsten ist der letzte Teil der lastname, der Rest der firstname
|
||||||
|
const lastname = parts.pop()!;
|
||||||
|
const firstname = parts.join(' ');
|
||||||
|
return { firstname, lastname };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
interface KeyValue {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-customer-sub-type',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, FormsModule],
|
||||||
|
template: `
|
||||||
|
<ng-container [ngSwitch]="customerSubType">
|
||||||
|
<span *ngSwitchCase="'broker'" class="bg-blue-100 text-blue-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-blue-400 border border-blue-400">Broker</span>
|
||||||
|
<span *ngSwitchCase="'cpa'" class="bg-gray-100 text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-gray-400 border border-gray-500">CPA</span>
|
||||||
|
<span *ngSwitchCase="'attorney'" class="bg-red-100 text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">Attorney</span>
|
||||||
|
<span *ngSwitchCase="'titleCompany'" class="bg-green-100 text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">Title Company</span>
|
||||||
|
<span *ngSwitchCase="'surveyor'" class="bg-yellow-100 text-yellow-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-yellow-300 border border-yellow-300">Surveyor</span>
|
||||||
|
<span *ngSwitchCase="'appraiser'" class="bg-pink-100 text-pink-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-pink-400 border border-pink-400">Appraiser</span>
|
||||||
|
<span *ngSwitchDefault class="text-gray-500">Unknown</span>
|
||||||
|
</ng-container>
|
||||||
|
`,
|
||||||
|
styles: [],
|
||||||
|
})
|
||||||
|
export class CustomerSubTypeComponent {
|
||||||
|
@Input() customerSubType: 'broker' | 'cpa' | 'attorney' | 'surveyor' | 'appraiser' | 'titleCompany';
|
||||||
|
}
|
||||||
|
|
@ -154,6 +154,7 @@
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/account" (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">Account</a>
|
<a routerLink="/account" (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">Account</a>
|
||||||
</li>
|
</li>
|
||||||
|
@if(user.customerType==='professional' || isAdmin()){
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/createBusinessListing" (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"
|
<a routerLink="/createBusinessListing" (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"
|
||||||
>Create Listing</a
|
>Create Listing</a
|
||||||
|
|
@ -162,6 +163,7 @@
|
||||||
<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>
|
<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>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -82,40 +82,6 @@ export class HeaderComponent {
|
||||||
this.criteria = getCriteriaProxy(this.baseRoute, this);
|
this.criteria = getCriteriaProxy(this.baseRoute, this);
|
||||||
this.searchService.search(this.criteria);
|
this.searchService.search(this.criteria);
|
||||||
}
|
}
|
||||||
// getCriteriaProxy(path:string):BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria{
|
|
||||||
// if ('businessListings' === path) {
|
|
||||||
// return this.createEnhancedProxy(getCriteriaStateObject('business'));
|
|
||||||
// } else if ('commercialPropertyListings' === path) {
|
|
||||||
// return this.createEnhancedProxy(getCriteriaStateObject('commercialProperty'));
|
|
||||||
// } else if ('brokerListings' === path) {
|
|
||||||
// return this.createEnhancedProxy(getCriteriaStateObject('broker'));
|
|
||||||
// } else {
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// private createEnhancedProxy(obj: any) {
|
|
||||||
// const component = this;
|
|
||||||
|
|
||||||
// const sessionStorageHandler = function (path, value, previous, applyData) {
|
|
||||||
// let criteriaType = '';
|
|
||||||
// if ('/businessListings' === window.location.pathname) {
|
|
||||||
// criteriaType = 'business';
|
|
||||||
// } else if ('/commercialPropertyListings' === window.location.pathname) {
|
|
||||||
// criteriaType = 'commercialProperty';
|
|
||||||
// } else if ('/brokerListings' === window.location.pathname) {
|
|
||||||
// criteriaType = 'broker';
|
|
||||||
// }
|
|
||||||
// sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return onChange(obj, function (path, value, previous, applyData) {
|
|
||||||
// // Call the original sessionStorageHandler
|
|
||||||
// sessionStorageHandler.call(this, path, value, previous, applyData);
|
|
||||||
|
|
||||||
// // Notify about the criteria change using the component's context
|
|
||||||
// component.criteriaChangeService.notifyCriteriaChange();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {}
|
||||||
|
|
||||||
|
|
@ -183,4 +149,7 @@ export class HeaderComponent {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isAdmin() {
|
||||||
|
return this.keycloakService.getUserRoles(true).includes('ADMIN');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,14 @@
|
||||||
<ng-option [value]="city">{{ city.name }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
<ng-option [value]="city">{{ city.name }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select> -->
|
</ng-select> -->
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-gray-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city
|
||||||
|
label="Company Location - City"
|
||||||
|
name="city"
|
||||||
|
[ngModel]="criteria.city"
|
||||||
|
(ngModelChange)="setCity($event)"
|
||||||
|
labelClasses="text-gray-900 font-medium"
|
||||||
|
[state]="criteria.state"
|
||||||
|
></app-validated-city>
|
||||||
</div>
|
</div>
|
||||||
<!-- New section for city search type -->
|
<!-- New section for city search type -->
|
||||||
<div *ngIf="criteria.city">
|
<div *ngIf="criteria.city">
|
||||||
|
|
@ -408,6 +415,15 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="brokername" class="block mb-2 text-sm font-medium text-gray-900">Broker Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="brokername"
|
||||||
|
[(ngModel)]="criteria.brokerName"
|
||||||
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<!-- New section for radius selection -->
|
<!-- New section for radius selection -->
|
||||||
<div *ngIf="criteria.city && criteria.searchType === 'radius'" class="space-y-2">
|
<div *ngIf="criteria.city && criteria.searchType === 'radius'" class="space-y-2">
|
||||||
<label class="block mb-2 text-sm font-medium text-gray-900">Select Radius (in miles)</label>
|
<label class="block mb-2 text-sm font-medium text-gray-900">Select Radius (in miles)</label>
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,10 @@
|
||||||
<span class="font-semibold w-40 p-2">Company Location</span>
|
<span class="font-semibold w-40 p-2">Company Location</span>
|
||||||
<span class="p-2 flex-grow">{{ user.companyLocation.name }} - {{ user.companyLocation.state }}</span>
|
<span class="p-2 flex-grow">{{ user.companyLocation.name }} - {{ user.companyLocation.state }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center bg-gray-100">
|
||||||
|
<span class="font-semibold w-40 p-2">Professional Type</span>
|
||||||
|
<span class="p-2 flex-grow">{{ selectOptions.getCustomerSubType(user.customerSubType) }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,8 @@
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<p class="text-sm text-gray-800 mb-2">{{ user.description }}</p>
|
<p class="text-sm text-gray-800 mb-2">{{ user.description }}</p>
|
||||||
<h3 class="text-lg font-semibold">{{ user.firstname }} {{ user.lastname }}</h3>
|
<h3 class="text-lg font-semibold">{{ user.firstname }} {{ user.lastname }}</h3>
|
||||||
<p class="text-sm text-gray-600">{{ user.companyName }}</p>
|
<app-customer-sub-type [customerSubType]="user.customerSubType"></app-customer-sub-type>
|
||||||
|
<p class="text-sm text-gray-600 mt-1">{{ user.companyName }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex justify-between items-center">
|
<div class="mt-4 flex justify-between items-center">
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { LISTINGS_PER_PAGE, ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { LISTINGS_PER_PAGE, ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { CustomerSubTypeComponent } from '../../../components/customer-sub-type/customer-sub-type.component';
|
||||||
import { PaginatorComponent } from '../../../components/paginator/paginator.component';
|
import { PaginatorComponent } from '../../../components/paginator/paginator.component';
|
||||||
import { ImageService } from '../../../services/image.service';
|
import { ImageService } from '../../../services/image.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
|
@ -16,7 +17,7 @@ import { getCriteriaStateObject } from '../../../utils/utils';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-broker-listings',
|
selector: 'app-broker-listings',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, RouterModule, NgOptimizedImage, PaginatorComponent],
|
imports: [CommonModule, FormsModule, RouterModule, NgOptimizedImage, PaginatorComponent, CustomerSubTypeComponent],
|
||||||
templateUrl: './broker-listings.component.html',
|
templateUrl: './broker-listings.component.html',
|
||||||
styleUrls: ['./broker-listings.component.scss', '../../pages.scss'],
|
styleUrls: ['./broker-listings.component.scss', '../../pages.scss'],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -128,12 +128,18 @@ export class AccountComponent {
|
||||||
|
|
||||||
async updateProfile(user: User) {
|
async updateProfile(user: User) {
|
||||||
if (this.user.customerType === 'buyer') {
|
if (this.user.customerType === 'buyer') {
|
||||||
|
const confirmed = await this.confirmationService.showConfirmation({ message: 'Are you sure you want to switch to Buyer ? All your listings as well as all your professionals informations will be deleted' });
|
||||||
|
if (confirmed) {
|
||||||
const id = this.user.id;
|
const id = this.user.id;
|
||||||
this.user = createDefaultUser(this.user.email, this.user.firstname, this.user.lastname);
|
this.user = createDefaultUser(this.user.email, this.user.firstname, this.user.lastname);
|
||||||
this.user.customerType = 'buyer';
|
this.user.customerType = 'buyer';
|
||||||
this.user.id = id;
|
this.user.id = id;
|
||||||
this.imageService.deleteLogoImagesByMail(this.user.email);
|
this.imageService.deleteLogoImagesByMail(this.user.email);
|
||||||
this.imageService.deleteProfileImagesByMail(this.user.email);
|
this.imageService.deleteProfileImagesByMail(this.user.email);
|
||||||
|
} else {
|
||||||
|
this.user.customerType = 'professional';
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.userService.save(this.user);
|
await this.userService.save(this.user);
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto pt-2">
|
||||||
<!-- <div class="grid-container"> -->
|
<!-- <div class="grid-container"> -->
|
||||||
<!-- @for (image of listing.imageOrder; track image) {
|
<!-- @for (image of listing.imageOrder; track image) {
|
||||||
<div cdkDrag class="grid-item">
|
<div cdkDrag class="grid-item">
|
||||||
|
|
@ -132,22 +132,25 @@
|
||||||
<app-drag-drop-mixed [listing]="listing" [ts]="ts" (imageOrderChanged)="imageOrderChanged($event)" (imageToDelete)="deleteConfirm($event)"></app-drag-drop-mixed>
|
<app-drag-drop-mixed [listing]="listing" [ts]="ts" (imageOrderChanged)="imageOrderChanged($event)" (imageToDelete)="deleteConfirm($event)"></app-drag-drop-mixed>
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
@if (mode!=='create'){
|
|
||||||
<div class="bg-white p-4 rounded-lg shadow">
|
<div class="bg-white px-4 pb-4 rounded-lg shadow">
|
||||||
<h2 class="text-lg font-semibold mb-2">Property Pictures</h2>
|
<h2 class="text-lg font-semibold mb-2">Property Pictures</h2>
|
||||||
|
@if(mode === 'create'){
|
||||||
<p class="text-sm text-gray-500 mb-4">(Pictures can be uploaded once the listing is posted initially)</p>
|
<p class="text-sm text-gray-500 mb-4">(Pictures can be uploaded once the listing is posted initially)</p>
|
||||||
|
} @if(mode !== 'create'){
|
||||||
<button
|
<button
|
||||||
(click)="openFileDialog()"
|
(click)="uploadPropertyPicture()"
|
||||||
class="flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
class="flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<svg class="mr-2 h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="mr-2 h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
<input type="file" #fileInput style="display: none" (change)="fileChangeEvent($event)" accept="image/*" />
|
}
|
||||||
|
<!-- <input type="file" #fileInput style="display: none" (change)="fileChangeEvent($event)" accept="image/*" /> -->
|
||||||
</div>
|
</div>
|
||||||
} @if (mode==='create'){
|
@if (mode==='create'){
|
||||||
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 mt-3 rounded-md hover:bg-blue-600">Post Listing</button>
|
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 mt-3 rounded-md hover:bg-blue-600">Post Listing</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 mt-3 rounded-md hover:bg-blue-600">Update Listing</button>
|
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 mt-3 rounded-md hover:bg-blue-600">Update Listing</button>
|
||||||
|
|
@ -156,15 +159,6 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Modal -->
|
|
||||||
<div *ngIf="showModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full flex items-center justify-center">
|
<app-image-crop-and-upload [uploadParams]="uploadParams" (uploadFinished)="uploadFinished($event)"></app-image-crop-and-upload>
|
||||||
<div class="bg-white p-5 rounded-lg shadow-xl" style="width: 90%; max-width: 600px">
|
|
||||||
<h3 class="text-lg font-semibold mb-4">Crop Image</h3>
|
|
||||||
<image-cropper [imageChangedEvent]="imageChangedEvent" [maintainAspectRatio]="true" [aspectRatio]="16 / 9" format="png" (imageCropped)="imageCropped($event)"></image-cropper>
|
|
||||||
<div class="mt-4 flex justify-end">
|
|
||||||
<button (click)="closeModal()" class="mr-2 px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300">Cancel</button>
|
|
||||||
<button (click)="uploadImage()" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Upload</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<app-confirmation></app-confirmation>
|
<app-confirmation></app-confirmation>
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,16 @@ import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { NgSelectModule } from '@ng-select/ng-select';
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { NgxCurrencyDirective } from 'ngx-currency';
|
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||||
import { ImageCroppedEvent, ImageCropperComponent } from 'ngx-image-cropper';
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
||||||
import { QuillModule } from 'ngx-quill';
|
import { QuillModule } from 'ngx-quill';
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, createDefaultCommercialPropertyListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { AutoCompleteCompleteEvent, ImageProperty, UploadParams, createDefaultCommercialPropertyListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
||||||
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
||||||
import { DragDropMixedComponent } from '../../../components/drag-drop-mixed/drag-drop-mixed.component';
|
import { DragDropMixedComponent } from '../../../components/drag-drop-mixed/drag-drop-mixed.component';
|
||||||
|
import { ImageCropAndUploadComponent, UploadReponse } from '../../../components/image-crop-and-upload/image-crop-and-upload.component';
|
||||||
import { MessageService } from '../../../components/message/message.service';
|
import { MessageService } from '../../../components/message/message.service';
|
||||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||||
|
|
@ -52,6 +53,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
ValidatedNgSelectComponent,
|
ValidatedNgSelectComponent,
|
||||||
ValidatedPriceComponent,
|
ValidatedPriceComponent,
|
||||||
ValidatedCityComponent,
|
ValidatedCityComponent,
|
||||||
|
ImageCropAndUploadComponent,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
templateUrl: './edit-commercial-property-listing.component.html',
|
templateUrl: './edit-commercial-property-listing.component.html',
|
||||||
|
|
@ -101,9 +103,10 @@ export class EditCommercialPropertyListingComponent {
|
||||||
quillModules = {
|
quillModules = {
|
||||||
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
||||||
};
|
};
|
||||||
showModal = false;
|
//showModal = false;
|
||||||
imageChangedEvent: any = '';
|
imageChangedEvent: any = '';
|
||||||
croppedImage: Blob | null = null;
|
croppedImage: Blob | null = null;
|
||||||
|
uploadParams: UploadParams;
|
||||||
constructor(
|
constructor(
|
||||||
public selectOptions: SelectOptionsService,
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
|
@ -179,30 +182,12 @@ export class EditCommercialPropertyListingComponent {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
||||||
this.suggestions = result.map(r => r.name).slice(0, 5);
|
this.suggestions = result.map(r => r.name).slice(0, 5);
|
||||||
}
|
}
|
||||||
openFileDialog() {
|
|
||||||
this.fileInput.nativeElement.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
fileChangeEvent(event: any): void {
|
uploadPropertyPicture() {
|
||||||
this.imageChangedEvent = event;
|
this.uploadParams = { type: 'uploadPropertyPicture', imagePath: this.listing.imagePath, serialId: this.listing.serialId };
|
||||||
this.showModal = true;
|
|
||||||
}
|
}
|
||||||
|
async uploadFinished(response: UploadReponse) {
|
||||||
imageCropped(event: ImageCroppedEvent) {
|
if (response.success) {
|
||||||
this.croppedImage = event.blob;
|
|
||||||
}
|
|
||||||
|
|
||||||
closeModal() {
|
|
||||||
this.imageChangedEvent = null;
|
|
||||||
this.croppedImage = null;
|
|
||||||
this.showModal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async uploadImage() {
|
|
||||||
if (this.croppedImage) {
|
|
||||||
await this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId);
|
|
||||||
this.ts = new Date().getTime();
|
|
||||||
this.closeModal();
|
|
||||||
this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing;
|
this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<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="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" mask="(000) 000-0000"></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>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,9 @@ export class SelectOptionsService {
|
||||||
getCustomerType(value: string): string {
|
getCustomerType(value: string): string {
|
||||||
return this.customerTypes.find(c => c.value === value)?.name;
|
return this.customerTypes.find(c => c.value === value)?.name;
|
||||||
}
|
}
|
||||||
|
getCustomerSubType(value: string): string {
|
||||||
|
return this.customerSubTypes.find(c => c.value === value)?.name;
|
||||||
|
}
|
||||||
getGender(value: string): string {
|
getGender(value: string): string {
|
||||||
return this.gender.find(c => c.value === value)?.name;
|
return this.gender.find(c => c.value === value)?.name;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,8 +145,7 @@ export function createEmptyUserListingCriteria(): UserListingCriteria {
|
||||||
types: [],
|
types: [],
|
||||||
prompt: '',
|
prompt: '',
|
||||||
criteriaType: 'brokerListings',
|
criteriaType: 'brokerListings',
|
||||||
firstname: '',
|
brokerName: '',
|
||||||
lastname: '',
|
|
||||||
companyName: '',
|
companyName: '',
|
||||||
counties: [],
|
counties: [],
|
||||||
state: '',
|
state: '',
|
||||||
|
|
@ -156,7 +155,7 @@ export function createEmptyUserListingCriteria(): UserListingCriteria {
|
||||||
}
|
}
|
||||||
export function createMailInfo(user: User): MailInfo {
|
export function createMailInfo(user: User): MailInfo {
|
||||||
return {
|
return {
|
||||||
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.companyLocation.state, comments: null },
|
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.companyLocation?.state, comments: null },
|
||||||
email: null,
|
email: null,
|
||||||
url: environment.mailinfoUrl,
|
url: environment.mailinfoUrl,
|
||||||
listing: null,
|
listing: null,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue