feat: Initialize BizMatch application with core UI components, routing, listing pages, backend services, migration scripts, and vulnerability management.
This commit is contained in:
@@ -32,8 +32,8 @@
|
||||
class="text-primary-600 dark:text-primary-500 hover:underline"> {{ detail.user.firstname }} {{
|
||||
detail.user.lastname }} </a>
|
||||
<img *ngIf="detail.user.hasCompanyLogo"
|
||||
[src]="detail.imageBaseUrl + '/pictures/logo/' + detail.imagePath + '.avif?_ts=' + detail.ts"
|
||||
class="mr-5 lg:mb-0" style="max-height: 30px; max-width: 100px" width="100" height="30" />
|
||||
[ngSrc]="detail.imageBaseUrl + '/pictures/logo/' + detail.imagePath + '.avif?_ts=' + detail.ts"
|
||||
class="mr-5 lg:mb-0" style="max-height: 30px; max-width: 100px" width="100" height="30" alt="Company logo for {{ detail.user.firstname }} {{ detail.user.lastname }}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,6 +149,35 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- FAQ Section for AEO (Answer Engine Optimization) -->
|
||||
@if(propertyFAQs && propertyFAQs.length > 0) {
|
||||
<div class="container mx-auto p-4 mt-8">
|
||||
<div class="bg-white rounded-lg drop-shadow-custom-bg-mobile md:drop-shadow-custom-bg p-6">
|
||||
<h2 class="text-2xl font-bold mb-6 text-gray-900">Frequently Asked Questions</h2>
|
||||
<div class="space-y-4">
|
||||
@for (faq of propertyFAQs; track $index) {
|
||||
<details class="group border border-gray-200 rounded-lg overflow-hidden hover:border-primary-300 transition-colors">
|
||||
<summary class="flex items-center justify-between cursor-pointer p-4 bg-gray-50 hover:bg-gray-100 transition-colors">
|
||||
<h3 class="text-lg font-semibold text-gray-900">{{ faq.question }}</h3>
|
||||
<svg class="w-5 h-5 text-gray-600 group-open:rotate-180 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</summary>
|
||||
<div class="p-4 bg-white border-t border-gray-200">
|
||||
<p class="text-gray-700 leading-relaxed">{{ faq.answer }}</p>
|
||||
</div>
|
||||
</details>
|
||||
}
|
||||
</div>
|
||||
<div class="mt-6 p-4 bg-primary-50 border-l-4 border-primary-500 rounded">
|
||||
<p class="text-sm text-gray-700">
|
||||
<strong class="text-primary-700">Have more questions?</strong> Contact the seller directly using the form above or reach out to our support team for assistance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Related Listings Section for SEO Internal Linking -->
|
||||
@if(relatedListings && relatedListings.length > 0) {
|
||||
<div class="container mx-auto p-4 mt-8">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, NgZone } from '@angular/core';
|
||||
import { NgOptimizedImage } from '@angular/common';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { LeafletModule } from '@bluehalo/ngx-leaflet';
|
||||
@@ -33,7 +34,7 @@ import { ShareButton } from 'ngx-sharebuttons/button';
|
||||
@Component({
|
||||
selector: 'app-details-commercial-property-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, GalleryModule, LeafletModule, BreadcrumbsComponent, ShareButton],
|
||||
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, GalleryModule, LeafletModule, BreadcrumbsComponent, ShareButton, NgOptimizedImage],
|
||||
providers: [],
|
||||
templateUrl: './details-commercial-property-listing.component.html',
|
||||
styleUrl: '../details.scss',
|
||||
@@ -73,6 +74,7 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon
|
||||
images: Array<ImageItem> = [];
|
||||
relatedListings: CommercialPropertyListing[] = [];
|
||||
breadcrumbs: BreadcrumbItem[] = [];
|
||||
propertyFAQs: Array<{ question: string; answer: string }> = [];
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
@@ -174,7 +176,13 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon
|
||||
{ name: 'Commercial Properties', url: '/commercialPropertyListings' },
|
||||
{ name: propertyData.propertyType, url: `/details-commercial-property/${this.listing.id}` }
|
||||
]);
|
||||
this.seoService.injectMultipleSchemas([realEstateSchema, breadcrumbSchema]);
|
||||
|
||||
// Generate FAQ for AEO (Answer Engine Optimization)
|
||||
this.propertyFAQs = this.generatePropertyFAQ();
|
||||
const faqSchema = this.seoService.generateFAQPageSchema(this.propertyFAQs);
|
||||
|
||||
// Inject all schemas including FAQ
|
||||
this.seoService.injectMultipleSchemas([realEstateSchema, breadcrumbSchema, faqSchema]);
|
||||
|
||||
// Generate breadcrumbs for navigation
|
||||
this.breadcrumbs = [
|
||||
@@ -213,6 +221,60 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate dynamic FAQ based on commercial property listing data
|
||||
* Provides AEO (Answer Engine Optimization) content
|
||||
*/
|
||||
private generatePropertyFAQ(): Array<{ question: string; answer: string }> {
|
||||
const faqs: Array<{ question: string; answer: string }> = [];
|
||||
|
||||
// FAQ 1: What type of property is this?
|
||||
faqs.push({
|
||||
question: 'What type of commercial property is this?',
|
||||
answer: `This is a ${this.selectOptions.getCommercialProperty(this.listing.type)} property located in ${this.listing.location.name || this.listing.location.county}, ${this.selectOptions.getState(this.listing.location.state)}.`
|
||||
});
|
||||
|
||||
// FAQ 2: What is the asking price?
|
||||
if (this.listing.price) {
|
||||
faqs.push({
|
||||
question: 'What is the asking price for this property?',
|
||||
answer: `The asking price for this commercial property is $${this.listing.price.toLocaleString()}.`
|
||||
});
|
||||
} else {
|
||||
faqs.push({
|
||||
question: 'What is the asking price for this property?',
|
||||
answer: 'The asking price is available upon request. Please contact the seller for detailed pricing information.'
|
||||
});
|
||||
}
|
||||
|
||||
// FAQ 3: Where is the property located?
|
||||
faqs.push({
|
||||
question: 'Where is this commercial property located?',
|
||||
answer: `The property is located in ${this.listing.location.name || this.listing.location.county}, ${this.selectOptions.getState(this.listing.location.state)}.${this.listing.location.street ? ' The exact address will be provided after initial contact.' : ''}`
|
||||
});
|
||||
|
||||
// FAQ 4: How long has the property been listed?
|
||||
const daysListed = this.getDaysListed();
|
||||
faqs.push({
|
||||
question: 'How long has this property been on the market?',
|
||||
answer: `This property was listed on ${this.dateInserted()} and has been on the market for ${daysListed} ${daysListed === 1 ? 'day' : 'days'}.`
|
||||
});
|
||||
|
||||
// FAQ 5: How can I schedule a viewing?
|
||||
faqs.push({
|
||||
question: 'How can I schedule a property viewing?',
|
||||
answer: 'To schedule a viewing of this commercial property, please use the contact form above to get in touch with the listing agent. They will coordinate a convenient time for you to visit the property.'
|
||||
});
|
||||
|
||||
// FAQ 6: What is the zoning for this property?
|
||||
faqs.push({
|
||||
question: 'What is this property suitable for?',
|
||||
answer: `This ${this.selectOptions.getCommercialProperty(this.listing.type)} property is ideal for various commercial uses. Contact the seller for specific zoning information and permitted use details.`
|
||||
});
|
||||
|
||||
return faqs;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.validationMessagesService.clearMessages(); // Löschen Sie alle bestehenden Validierungsnachrichten
|
||||
this.seoService.clearStructuredData(); // Clean up SEO structured data
|
||||
|
||||
Reference in New Issue
Block a user