diverse BugFixes

This commit is contained in:
Andreas Knuth 2024-08-12 17:18:32 +02:00
parent 3a6a64cce9
commit ec0576e7b8
17 changed files with 118 additions and 100 deletions

View File

@ -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'),
}); });

View File

@ -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';

View File

@ -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) {

View File

@ -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 };
}
}

View File

@ -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';
}

View File

@ -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>

View File

@ -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');
}
} }

View File

@ -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>

View File

@ -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 -->

View File

@ -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">

View File

@ -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'],
}) })

View File

@ -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 id = this.user.id; 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' });
this.user = createDefaultUser(this.user.email, this.user.firstname, this.user.lastname); if (confirmed) {
this.user.customerType = 'buyer'; const id = this.user.id;
this.user.id = id; this.user = createDefaultUser(this.user.email, this.user.firstname, this.user.lastname);
this.imageService.deleteLogoImagesByMail(this.user.email); this.user.customerType = 'buyer';
this.imageService.deleteProfileImagesByMail(this.user.email); this.user.id = id;
this.imageService.deleteLogoImagesByMail(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);

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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>

View File

@ -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;
} }

View File

@ -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,