Fehler behebung
This commit is contained in:
@@ -29,7 +29,8 @@ export abstract class BaseDetailsComponent {
|
||||
const latitude = this.listing.location.latitude;
|
||||
const longitude = this.listing.location.longitude;
|
||||
|
||||
if (latitude && longitude) {
|
||||
if (latitude !== null && latitude !== undefined &&
|
||||
longitude !== null && longitude !== undefined) {
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
|
||||
// Build address string from available location data
|
||||
|
||||
@@ -26,7 +26,7 @@ import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { createMailInfo, map2User } from '../../../utils/utils';
|
||||
// Import für Leaflet
|
||||
// Benannte Importe für Leaflet
|
||||
import { circle, Circle, Control, DomEvent, DomUtil, latLng, LatLngBounds, polygon, Polygon, tileLayer } from 'leaflet';
|
||||
import { circle, Circle, Control, DomEvent, DomUtil, icon, Icon, latLng, LatLngBounds, Marker, polygon, Polygon, tileLayer } from 'leaflet';
|
||||
import dayjs from 'dayjs';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
import { BaseDetailsComponent } from '../base-details.component';
|
||||
@@ -328,12 +328,18 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent {
|
||||
const county = this.listing.location.county || '';
|
||||
const state = this.listing.location.state;
|
||||
|
||||
if (latitude && longitude && cityName && state) {
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
this.mapZoom = 11; // Zoom out to show city area
|
||||
// Check if we have valid coordinates (null-safe check)
|
||||
if (latitude !== null && latitude !== undefined &&
|
||||
longitude !== null && longitude !== undefined) {
|
||||
|
||||
// Fetch city boundary from Nominatim API
|
||||
this.geoService.getCityBoundary(cityName, state).subscribe({
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
|
||||
// Case 1: City name available - show city boundary (current behavior)
|
||||
if (cityName && state) {
|
||||
this.mapZoom = 11; // Zoom to city level
|
||||
|
||||
// Fetch city boundary from Nominatim API
|
||||
this.geoService.getCityBoundary(cityName, state).subscribe({
|
||||
next: (data) => {
|
||||
if (data && data.length > 0 && data[0].geojson && data[0].geojson.type === 'Polygon') {
|
||||
const coordinates = data[0].geojson.coordinates[0]; // Get outer boundary
|
||||
@@ -426,6 +432,18 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent {
|
||||
this.useFallbackCircle(latitude, longitude, cityName, county, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Case 2: Only state available (NEW) - show state-level circle
|
||||
else if (state) {
|
||||
this.mapZoom = 6; // Zoom to state level
|
||||
// Use state-level fallback with larger radius
|
||||
this.useStateLevelFallback(latitude, longitude, county, state);
|
||||
}
|
||||
// Case 3: No location name at all - minimal marker
|
||||
else {
|
||||
this.mapZoom = 8;
|
||||
this.useMinimalMarker(latitude, longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,6 +481,184 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show state-level boundary polygon
|
||||
* Used when only state is available without city
|
||||
*/
|
||||
private useStateLevelFallback(latitude: number, longitude: number, county: string, state: string) {
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
|
||||
// Fetch state boundary from Nominatim API (similar to city boundary)
|
||||
this.geoService.getStateBoundary(state).subscribe({
|
||||
next: (data) => {
|
||||
if (data && data.length > 0 && data[0].geojson) {
|
||||
|
||||
// Handle Polygon
|
||||
if (data[0].geojson.type === 'Polygon') {
|
||||
const coordinates = data[0].geojson.coordinates[0];
|
||||
const latlngs = coordinates.map((coord: number[]) => latLng(coord[1], coord[0]));
|
||||
|
||||
const statePolygon = polygon(latlngs, {
|
||||
color: '#ef4444',
|
||||
fillColor: '#ef4444',
|
||||
fillOpacity: 0.05, // Very transparent for large area
|
||||
weight: 2
|
||||
});
|
||||
|
||||
statePolygon.bindPopup(`
|
||||
<div style="padding: 8px;">
|
||||
<strong>General Area:</strong><br/>
|
||||
${county ? county + ', ' : ''}${state}<br/>
|
||||
<small style="color: #666;">State boundary shown for privacy.<br/>Exact location provided after contact.</small>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.mapLayers = [
|
||||
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
}),
|
||||
statePolygon
|
||||
];
|
||||
|
||||
// Fit map to state bounds
|
||||
const bounds = statePolygon.getBounds();
|
||||
this.mapOptions = {
|
||||
...this.mapOptions,
|
||||
center: bounds.getCenter(),
|
||||
zoom: this.mapZoom,
|
||||
};
|
||||
}
|
||||
// Handle MultiPolygon (states with islands, etc.)
|
||||
else if (data[0].geojson.type === 'MultiPolygon') {
|
||||
const allPolygons: Polygon[] = [];
|
||||
|
||||
data[0].geojson.coordinates.forEach((polygonCoords: number[][][]) => {
|
||||
const latlngs = polygonCoords[0].map((coord: number[]) => latLng(coord[1], coord[0]));
|
||||
const statePolygon = polygon(latlngs, {
|
||||
color: '#ef4444',
|
||||
fillColor: '#ef4444',
|
||||
fillOpacity: 0.05,
|
||||
weight: 2
|
||||
});
|
||||
allPolygons.push(statePolygon);
|
||||
});
|
||||
|
||||
if (allPolygons.length > 0) {
|
||||
allPolygons[0].bindPopup(`
|
||||
<div style="padding: 8px;">
|
||||
<strong>General Area:</strong><br/>
|
||||
${county ? county + ', ' : ''}${state}<br/>
|
||||
<small style="color: #666;">State boundary shown for privacy.<br/>Exact location provided after contact.</small>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
this.mapLayers = [
|
||||
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
}),
|
||||
...allPolygons
|
||||
];
|
||||
|
||||
// Calculate combined bounds
|
||||
if (allPolygons.length > 0) {
|
||||
const bounds = new LatLngBounds([]);
|
||||
allPolygons.forEach(p => bounds.extend(p.getBounds()));
|
||||
this.mapOptions = {
|
||||
...this.mapOptions,
|
||||
center: bounds.getCenter(),
|
||||
zoom: this.mapZoom,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Fallback if unexpected format
|
||||
this.useCircleFallbackForState(latitude, longitude, county, state);
|
||||
}
|
||||
} else {
|
||||
// Fallback if no data
|
||||
this.useCircleFallbackForState(latitude, longitude, county, state);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error fetching state boundary:', err);
|
||||
// Fallback to circle on error
|
||||
this.useCircleFallbackForState(latitude, longitude, county, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback: Show circle when state boundary cannot be fetched
|
||||
*/
|
||||
private useCircleFallbackForState(latitude: number, longitude: number, county: string, state: string) {
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
|
||||
const stateCircle = circle([latitude, longitude], {
|
||||
color: '#ef4444',
|
||||
fillColor: '#ef4444',
|
||||
fillOpacity: 0.05,
|
||||
weight: 2,
|
||||
radius: 50000 // 50km
|
||||
});
|
||||
|
||||
stateCircle.bindPopup(`
|
||||
<div style="padding: 8px;">
|
||||
<strong>General Area:</strong><br/>
|
||||
${county ? county + ', ' : ''}${state}<br/>
|
||||
<small style="color: #666;">Approximate state-level location shown for privacy.<br/>Exact location provided after contact.</small>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.mapLayers = [
|
||||
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
}),
|
||||
stateCircle
|
||||
];
|
||||
|
||||
this.mapOptions = {
|
||||
...this.mapOptions,
|
||||
center: this.mapCenter,
|
||||
zoom: this.mapZoom,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show minimal marker when no location name is available
|
||||
*/
|
||||
private useMinimalMarker(latitude: number, longitude: number) {
|
||||
this.mapCenter = latLng(latitude, longitude);
|
||||
|
||||
const marker = new Marker([latitude, longitude], {
|
||||
icon: icon({
|
||||
...Icon.Default.prototype.options,
|
||||
iconUrl: 'assets/leaflet/marker-icon.png',
|
||||
iconRetinaUrl: 'assets/leaflet/marker-icon-2x.png',
|
||||
shadowUrl: 'assets/leaflet/marker-shadow.png',
|
||||
}),
|
||||
});
|
||||
|
||||
marker.bindPopup(`
|
||||
<div style="padding: 8px;">
|
||||
<strong>Location</strong><br/>
|
||||
<small style="color: #666;">Contact seller for exact address</small>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.mapLayers = [
|
||||
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
}),
|
||||
marker
|
||||
];
|
||||
|
||||
this.mapOptions = {
|
||||
...this.mapOptions,
|
||||
center: this.mapCenter,
|
||||
zoom: this.mapZoom,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Override onMapReady to show privacy-friendly address control
|
||||
*/
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"
|
||||
class="tab-link hover:cursor-pointer inline-flex items-center justify-center px-1 py-2 md:p-4 border-b-2 rounded-t-lg"
|
||||
>
|
||||
<img src="assets/images/business_logo.png" alt="Search businesses for sale" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" />
|
||||
<img src="assets/images/business_logo.png" alt="Search businesses for sale" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" width="28" height="28" />
|
||||
<span>Businesses</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -87,7 +87,7 @@
|
||||
"
|
||||
class="tab-link hover:cursor-pointer inline-flex items-center justify-center px-1 py-2 md:p-4 border-b-2 rounded-t-lg"
|
||||
>
|
||||
<img src="assets/images/properties_logo.png" alt="Search commercial properties for sale" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" />
|
||||
<img src="assets/images/properties_logo.png" alt="Search commercial properties for sale" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" width="28" height="28" />
|
||||
<span>Properties</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -131,6 +131,7 @@ input[type='text'][name='aiSearchText'] {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:active::after {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { initFlowbite } from 'flowbite';
|
||||
import { catchError, concat, distinctUntilChanged, Observable, of, Subject, switchMap, tap } from 'rxjs';
|
||||
import { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingCriteria, GeoResult, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { FaqComponent, FAQItem } from '../../components/faq/faq.component';
|
||||
@@ -127,9 +126,7 @@ export class HomeComponent {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
setTimeout(() => {
|
||||
initFlowbite();
|
||||
}, 0);
|
||||
// Flowbite is now initialized once in AppComponent
|
||||
|
||||
// Set SEO meta tags for home page
|
||||
this.seoService.updateMetaTags({
|
||||
|
||||
@@ -3,8 +3,6 @@ import { ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
import { initFlowbite } from 'flowbite';
|
||||
|
||||
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||
import { ImageCropperComponent } from 'ngx-image-cropper';
|
||||
import { QuillModule } from 'ngx-quill';
|
||||
@@ -99,9 +97,7 @@ export class AccountComponent {
|
||||
public authService: AuthService,
|
||||
) {}
|
||||
async ngOnInit() {
|
||||
setTimeout(() => {
|
||||
initFlowbite();
|
||||
}, 10);
|
||||
// Flowbite is now initialized once in AppComponent
|
||||
if (this.id) {
|
||||
this.user = await this.userService.getById(this.id);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user