Issue: #109
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<div>
|
||||
<label for="type" class="block text-sm font-bold text-gray-700 mb-1 relative w-fit {{ labelClasses }}"
|
||||
>{{ label }} @if(validationMessage){
|
||||
<div
|
||||
attr.data-tooltip-target="tooltip-{{ name }}"
|
||||
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||
(click)="toggleTooltip($event)"
|
||||
(touchstart)="toggleTooltip($event)"
|
||||
>
|
||||
!
|
||||
</div>
|
||||
<app-tooltip id="tooltip-{{ name }}" [text]="validationMessage" [isVisible]="isTooltipVisible"></app-tooltip>
|
||||
}
|
||||
</label>
|
||||
<ng-select
|
||||
class="custom"
|
||||
[multiple]="false"
|
||||
[hideSelected]="true"
|
||||
[trackByFn]="trackByFn"
|
||||
[minTermLength]="2"
|
||||
[loading]="placeLoading"
|
||||
typeToSearchText="Please enter 2 or more characters"
|
||||
[typeahead]="placeInput$"
|
||||
ngModel="{{ formatGeoAddress(value) }}"
|
||||
(ngModelChange)="onInputChange($event)"
|
||||
>
|
||||
@for (place of places$ | async; track place.place_id) {
|
||||
<ng-option [value]="place">{{ formatPlaceAddress(place) }}</ng-option>
|
||||
}
|
||||
</ng-select>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
:host ::ng-deep .ng-select.custom .ng-select-container {
|
||||
// --tw-bg-opacity: 1;
|
||||
// background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||
// height: 42px;
|
||||
border-radius: 0.5rem;
|
||||
.ng-value-container .ng-input {
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, forwardRef, Input } from '@angular/core';
|
||||
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
import { catchError, concat, debounceTime, distinctUntilChanged, Observable, of, Subject, switchMap, tap } from 'rxjs';
|
||||
import { GeoResult } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { Place } from '../../../../../bizmatch-server/src/models/server.model';
|
||||
import { GeoService } from '../../services/geo.service';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { BaseInputComponent } from '../base-input/base-input.component';
|
||||
import { TooltipComponent } from '../tooltip/tooltip.component';
|
||||
import { ValidationMessagesService } from '../validation-messages.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-validated-location',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, NgSelectModule, TooltipComponent],
|
||||
templateUrl: './validated-location.component.html',
|
||||
styleUrl: './validated-location.component.scss',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => ValidatedLocationComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ValidatedLocationComponent extends BaseInputComponent {
|
||||
@Input() items;
|
||||
@Input() labelClasses: string;
|
||||
places$: Observable<Place[]>;
|
||||
placeInput$ = new Subject<string>();
|
||||
placeLoading = false;
|
||||
constructor(validationMessagesService: ValidationMessagesService, private geoService: GeoService, public selectOptions: SelectOptionsService) {
|
||||
super(validationMessagesService);
|
||||
}
|
||||
|
||||
override ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.loadCities();
|
||||
}
|
||||
onInputChange(event: Place): void {
|
||||
this.value = event; //{ ...event, longitude: parseFloat(event.longitude), latitude: parseFloat(event.latitude) };
|
||||
this.value = {
|
||||
id: event?.place_id,
|
||||
name: event?.address.city,
|
||||
county: event?.address.county,
|
||||
street: event?.address.road,
|
||||
housenumber: event?.address.house_number,
|
||||
state: event?.address['ISO3166-2-lvl4'].substr(3),
|
||||
latitude: event ? parseFloat(event?.lat) : undefined,
|
||||
longitude: event ? parseFloat(event?.lon) : undefined,
|
||||
};
|
||||
this.onChange(this.value);
|
||||
}
|
||||
private loadCities() {
|
||||
this.places$ = concat(
|
||||
of([]), // default items
|
||||
this.placeInput$.pipe(
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
tap(() => (this.placeLoading = true)),
|
||||
switchMap(term =>
|
||||
this.geoService.findLocationStartingWith(term).pipe(
|
||||
catchError(() => of([])), // empty list on error
|
||||
// map(cities => cities.map(city => city.city)), // transform the list of objects to a list of city names
|
||||
tap(() => (this.placeLoading = false)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
trackByFn(item: GeoResult) {
|
||||
return item.id;
|
||||
}
|
||||
compareFn = (item, selected) => {
|
||||
return item.id === selected.id;
|
||||
};
|
||||
formatGeoAddress(geoResult: GeoResult | null | undefined): string {
|
||||
// Überprüfen, ob geoResult null oder undefined ist
|
||||
if (!geoResult) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let addressParts: string[] = [];
|
||||
|
||||
// Füge Hausnummer hinzu, wenn vorhanden
|
||||
if (geoResult.housenumber) {
|
||||
addressParts.push(geoResult.housenumber);
|
||||
}
|
||||
|
||||
// Füge Straße hinzu, wenn vorhanden
|
||||
if (geoResult.street) {
|
||||
addressParts.push(geoResult.street);
|
||||
}
|
||||
|
||||
// Kombiniere Hausnummer und Straße
|
||||
let address = addressParts.join(' ');
|
||||
|
||||
// Füge Namen hinzu, wenn vorhanden
|
||||
if (geoResult.name) {
|
||||
address = address ? `${address}, ${geoResult.name}` : geoResult.name;
|
||||
}
|
||||
|
||||
// Füge County hinzu, wenn vorhanden
|
||||
if (geoResult.county) {
|
||||
address = address ? `${address}, ${geoResult.county}` : geoResult.county;
|
||||
}
|
||||
|
||||
// Füge Bundesland hinzu, wenn vorhanden
|
||||
if (geoResult.state) {
|
||||
address = address ? `${address} - ${geoResult.state}` : geoResult.state;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
formatPlaceAddress(place: Place | null | undefined): string {
|
||||
// Überprüfen, ob place null oder undefined ist
|
||||
if (!place) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const { house_number, road, city, county, state } = place.address;
|
||||
|
||||
let addressParts: string[] = [];
|
||||
|
||||
// Füge Hausnummer hinzu, wenn vorhanden
|
||||
if (house_number) {
|
||||
addressParts.push(house_number);
|
||||
}
|
||||
|
||||
// Füge Straße hinzu, wenn vorhanden
|
||||
if (road) {
|
||||
addressParts.push(road);
|
||||
}
|
||||
|
||||
// Kombiniere Hausnummer und Straße
|
||||
let address = addressParts.join(' ');
|
||||
|
||||
// Füge Stadt hinzu, wenn vorhanden
|
||||
if (city) {
|
||||
address = address ? `${address}, ${city}` : city;
|
||||
}
|
||||
|
||||
// Füge County hinzu, wenn vorhanden
|
||||
if (county) {
|
||||
address = address ? `${address}, ${county}` : county;
|
||||
}
|
||||
|
||||
// Füge Bundesland hinzu, wenn vorhanden
|
||||
if (state) {
|
||||
address = address ? `${address} - ${state}` : state;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<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.location?.name }} - {{ user.location?.state }}</span>
|
||||
</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>
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
<app-validated-input label="Your Phone Number" name="phoneNumber" [(ngModel)]="user.phoneNumber" mask="(000) 000-0000"></app-validated-input>
|
||||
<app-validated-input label="Company Website" name="companyWebsite" [(ngModel)]="user.companyWebsite"></app-validated-input>
|
||||
<!-- <app-validated-input label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-input> -->
|
||||
<app-validated-city label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-city>
|
||||
<!-- <app-validated-city label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-city> -->
|
||||
<app-validated-location label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-location>
|
||||
</div>
|
||||
|
||||
<!-- <div>
|
||||
|
||||
@@ -21,6 +21,7 @@ import { TooltipComponent } from '../../../components/tooltip/tooltip.component'
|
||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||
import { ValidatedCountyComponent } from '../../../components/validated-county/validated-county.component';
|
||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||
import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component';
|
||||
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
||||
@@ -52,6 +53,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||
ValidatedCityComponent,
|
||||
TooltipComponent,
|
||||
ValidatedCountyComponent,
|
||||
ValidatedLocationComponent,
|
||||
],
|
||||
providers: [TitleCasePipe, DatePipe],
|
||||
templateUrl: './account.component.html',
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- <app-validated-ng-select label="State" name="state" [(ngModel)]="listing.location.state" [items]="selectOptions?.states"></app-validated-ng-select>
|
||||
<app-validated-input label="City" name="city" [(ngModel)]="listing.location.city"></app-validated-input> -->
|
||||
<app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city>
|
||||
<!-- <app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city> -->
|
||||
<app-validated-location label="Location" name="location" [(ngModel)]="listing.location"></app-validated-location>
|
||||
<app-validated-price label="Price" name="price" [(ngModel)]="listing.price"></app-validated-price>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { environment } from '../../../../environments/environment';
|
||||
import { MessageService } from '../../../components/message/message.service';
|
||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||
@@ -47,6 +48,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||
ValidatedPriceComponent,
|
||||
ValidatedTextareaComponent,
|
||||
ValidatedCityComponent,
|
||||
ValidatedLocationComponent,
|
||||
],
|
||||
providers: [],
|
||||
templateUrl: './edit-business-listing.component.html',
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
</div> -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<app-validated-ng-select label="Property Category" name="type" [(ngModel)]="listing.type" [items]="typesOfCommercialProperty"></app-validated-ng-select>
|
||||
<app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city>
|
||||
<!-- <app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city> -->
|
||||
<app-validated-location label="Location" name="location" [(ngModel)]="listing.location"></app-validated-location>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex mb-4 space-x-4">
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ImageCropAndUploadComponent, UploadReponse } from '../../../components/
|
||||
import { MessageService } from '../../../components/message/message.service';
|
||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||
@@ -52,6 +53,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||
ValidatedQuillComponent,
|
||||
ValidatedNgSelectComponent,
|
||||
ValidatedPriceComponent,
|
||||
ValidatedLocationComponent,
|
||||
ValidatedCityComponent,
|
||||
ImageCropAndUploadComponent,
|
||||
],
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CityAndStateResult, CountyResult, GeoResult } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { Place } from '../../../../bizmatch-server/src/models/server.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
@@ -9,6 +10,7 @@ import { environment } from '../../environments/environment';
|
||||
})
|
||||
export class GeoService {
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
private baseUrl: string = 'https://nominatim.openstreetmap.org/search';
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
findCitiesStartingWith(prefix: string, state?: string): Observable<GeoResult[]> {
|
||||
@@ -21,4 +23,8 @@ export class GeoService {
|
||||
findCountiesStartingWith(prefix: string, states?: string[]): Observable<CountyResult[]> {
|
||||
return this.http.post<CountyResult[]>(`${this.apiBaseUrl}/bizmatch/geo/counties`, { prefix, states });
|
||||
}
|
||||
findLocationStartingWith(prefix: string): Observable<Place[]> {
|
||||
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
|
||||
return this.http.get(`${this.baseUrl}?q=${prefix},US&format=json&addressdetails=1&limit=5`, { headers }) as Observable<Place[]>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, lastValueFrom, Observable } from 'rxjs';
|
||||
import urlcat from 'urlcat';
|
||||
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { ResponseUsersArray, StatesResult, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { ResponseUsersArray, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
@@ -46,7 +46,7 @@ export class UserService {
|
||||
getNumberOfBroker(criteria?: UserListingCriteria): Observable<number> {
|
||||
return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/user/findTotal`, criteria);
|
||||
}
|
||||
async getAllStates(): Promise<any> {
|
||||
return await lastValueFrom(this.http.get<StatesResult[]>(`${this.apiBaseUrl}/bizmatch/user/states/all`));
|
||||
}
|
||||
// async getAllStates(): Promise<any> {
|
||||
// return await lastValueFrom(this.http.get<StatesResult[]>(`${this.apiBaseUrl}/bizmatch/user/states/all`));
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export function resetUserListingCriteria(criteria: UserListingCriteria) {
|
||||
|
||||
export function createMailInfo(user: User): MailInfo {
|
||||
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.location?.state, comments: null },
|
||||
email: null,
|
||||
url: environment.mailinfoUrl,
|
||||
listing: null,
|
||||
|
||||
Reference in New Issue
Block a user