neue Landing page
This commit is contained in:
parent
b39370a6b5
commit
eb23bebc10
|
|
@ -20,6 +20,7 @@
|
||||||
"@angular/core": "^18.1.3",
|
"@angular/core": "^18.1.3",
|
||||||
"@angular/fire": "^18.0.1",
|
"@angular/fire": "^18.0.1",
|
||||||
"@angular/forms": "^18.1.3",
|
"@angular/forms": "^18.1.3",
|
||||||
|
"@angular/google-maps": "^18.2.14",
|
||||||
"@angular/platform-browser": "^18.1.3",
|
"@angular/platform-browser": "^18.1.3",
|
||||||
"@angular/platform-browser-dynamic": "^18.1.3",
|
"@angular/platform-browser-dynamic": "^18.1.3",
|
||||||
"@angular/platform-server": "^18.1.3",
|
"@angular/platform-server": "^18.1.3",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
<main class="flex-1">
|
<main class="flex-1">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<app-footer></app-footer>
|
<app-footer></app-footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,38 +43,11 @@ export class AppComponent {
|
||||||
while (currentRoute.children[0] !== undefined) {
|
while (currentRoute.children[0] !== undefined) {
|
||||||
currentRoute = currentRoute.children[0];
|
currentRoute = currentRoute.children[0];
|
||||||
}
|
}
|
||||||
// Hier haben Sie Zugriff auf den aktuellen Route-Pfad
|
|
||||||
this.actualRoute = currentRoute.snapshot.url[0].path;
|
this.actualRoute = currentRoute.snapshot.url[0].path;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {}
|
||||||
// this.keycloakService.keycloakEvents$.subscribe({
|
|
||||||
// next: event => {
|
|
||||||
// if (event.type === KeycloakEventType.OnTokenExpired) {
|
|
||||||
// this.handleTokenExpiration();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
// private async handleTokenExpiration(): Promise<void> {
|
|
||||||
// try {
|
|
||||||
// // Versuche, den Token zu erneuern
|
|
||||||
// const refreshed = await this.keycloakService.updateToken();
|
|
||||||
// if (!refreshed) {
|
|
||||||
// // Wenn der Token nicht erneuert werden kann, leite zur Login-Seite weiter
|
|
||||||
// this.keycloakService.login({
|
|
||||||
// redirectUri: window.location.href, // oder eine andere Seite
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// if (error.error === 'invalid_grant' && error.error_description === 'Token is not active') {
|
|
||||||
// // Hier wird der Fehler "invalid_grant" abgefangen
|
|
||||||
// this.keycloakService.login({
|
|
||||||
// redirectUri: window.location.href,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@HostListener('window:keydown', ['$event'])
|
@HostListener('window:keydown', ['$event'])
|
||||||
handleKeyboardEvent(event: KeyboardEvent) {
|
handleKeyboardEvent(event: KeyboardEvent) {
|
||||||
if (event.shiftKey && event.ctrlKey && event.key === 'V') {
|
if (event.shiftKey && event.ctrlKey && event.key === 'V') {
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,14 @@ export const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'businessListings',
|
path: 'businessListings',
|
||||||
component: BusinessListingsComponent,
|
component: BusinessListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'commercialPropertyListings',
|
path: 'commercialPropertyListings',
|
||||||
component: CommercialPropertyListingsComponent,
|
component: CommercialPropertyListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'brokerListings',
|
path: 'brokerListings',
|
||||||
component: BrokerListingsComponent,
|
component: BrokerListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: 'home',
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,79 @@
|
||||||
<footer class="bg-white px-4 py-2 md:px-6 mt-auto w-full print:hidden">
|
<ng-template #otherRoute>
|
||||||
<div class="container mx-auto flex flex-col lg:flex-row justify-between items-center">
|
<footer class="bg-white px-4 py-2 md:px-6 mt-auto w-full print:hidden">
|
||||||
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
<div class="container mx-auto flex flex-col lg:flex-row justify-between items-center">
|
||||||
<!-- <img src="assets/images/header-logo.png" alt="BizMatch Logo" class="h-8 mb-2 lg:mb-0 lg:mr-4" /> -->
|
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
||||||
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
<!-- <img src="assets/images/header-logo.png" alt="BizMatch Logo" class="h-8 mb-2 lg:mb-0 lg:mr-4" /> -->
|
||||||
<img src="assets/images/header-logo.png" class="h-8" class="h-8 mb-2 lg:mb-0 lg:mr-4" />
|
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||||
</a>
|
<img src="assets/images/header-logo.png" class="h-8" class="h-8 mb-2 lg:mb-0 lg:mr-4" />
|
||||||
<p class="text-sm text-gray-600 text-center lg:text-left">© {{ currentYear }} Bizmatch All rights reserved.</p>
|
</a>
|
||||||
</div>
|
<p class="text-sm text-gray-600 text-center lg:text-left">© {{ currentYear }} Bizmatch All rights reserved.</p>
|
||||||
|
|
||||||
<div class="flex flex-col lg:flex-row items-center order-3 lg:order-2">
|
|
||||||
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="terms-of-use" data-drawer-show="terms-of-use" aria-controls="terms-of-use">Terms of use</a>
|
|
||||||
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="privacy" data-drawer-show="privacy" aria-controls="privacy">Privacy statement</a>
|
|
||||||
<!-- <a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" routerLink="/pricingOverview">Pricing</a> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col lg:flex-row items-center order-2 lg:order-3">
|
|
||||||
<div class="mb-4 lg:mb-0 lg:mr-6 text-center lg:text-right">
|
|
||||||
<p class="text-sm text-gray-600 mb-1 lg:mb-2">BizMatch, Inc., 1001 Blucher Street, Corpus</p>
|
|
||||||
<p class="text-sm text-gray-600">Christi, Texas 78401</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4 lg:mb-0 flex flex-col items-center lg:items-end">
|
<div class="flex flex-col lg:flex-row items-center order-3 lg:order-2">
|
||||||
<a class="text-sm text-gray-600 mb-1 lg:mb-2 hover:text-blue-600 w-full"> <i class="fas fa-phone-alt mr-2"></i>1-800-840-6025 </a>
|
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="terms-of-use" data-drawer-show="terms-of-use" aria-controls="terms-of-use">Terms of use</a>
|
||||||
<a class="text-sm text-gray-600 hover:text-blue-600"> <i class="fas fa-envelope mr-2"></i>info@bizmatch.net </a>
|
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="privacy" data-drawer-show="privacy" aria-controls="privacy">Privacy statement</a>
|
||||||
|
<!-- <a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" routerLink="/pricingOverview">Pricing</a> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col lg:flex-row items-center order-2 lg:order-3">
|
||||||
|
<div class="mb-4 lg:mb-0 lg:mr-6 text-center lg:text-right">
|
||||||
|
<p class="text-sm text-gray-600 mb-1 lg:mb-2">BizMatch, Inc., 1001 Blucher Street, Corpus</p>
|
||||||
|
<p class="text-sm text-gray-600">Christi, Texas 78401</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4 lg:mb-0 flex flex-col items-center lg:items-end">
|
||||||
|
<a class="text-sm text-gray-600 mb-1 lg:mb-2 hover:text-blue-600 w-full"> <i class="fas fa-phone-alt mr-2"></i>1-800-840-6025 </a>
|
||||||
|
<a class="text-sm text-gray-600 hover:text-blue-600"> <i class="fas fa-envelope mr-2"></i>info@bizmatch.net </a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</ng-template>
|
||||||
|
<footer *ngIf="isHomeRoute; else otherRoute" class="bg-gray-800 text-white pt-12 pb-4">
|
||||||
|
<div class="container mx-auto px-6">
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div class="w-full md:w-1/3 mb-8 md:mb-0">
|
||||||
|
<h3 class="text-xl font-semibold mb-4">BizMatch</h3>
|
||||||
|
<p class="mb-2">Your trusted partner in business brokerage.</p>
|
||||||
|
<p class="mb-2">TREC License #0516 788</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full md:w-1/3 mb-8 md:mb-0">
|
||||||
|
<h3 class="text-xl font-semibold mb-4">Quick Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a href="#" class="text-gray-300 hover:text-white">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a href="#services" class="text-gray-300 hover:text-white">Services</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a href="#location" class="text-gray-300 hover:text-white">Location</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a href="#contact" class="text-gray-300 hover:text-white">Contact</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a data-drawer-target="terms-of-use" data-drawer-show="terms-of-use" aria-controls="terms-of-use" class="text-gray-300 hover:text-white">Terms of use</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a data-drawer-target="privacy" data-drawer-show="privacy" aria-controls="privacy" class="text-gray-300 hover:text-white">Privacy statement</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full md:w-1/3">
|
||||||
|
<h3 class="text-xl font-semibold mb-4">Contact Us</h3>
|
||||||
|
<p class="mb-2">1001 Blucher Street</p>
|
||||||
|
<p class="mb-2">Corpus Christi, TX 78401</p>
|
||||||
|
<p class="mb-4">United States</p>
|
||||||
|
<p class="mb-2">1-800-840-6025</p>
|
||||||
|
<p class="mb-2">info@bizmatch.net</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-4 text-center">
|
||||||
|
<p class="text-sm text-gray-400 mt-4">© 2025 BizMatch. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
:host {
|
:host {
|
||||||
// position: absolute;
|
|
||||||
// bottom: 0px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
div {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
@media (max-width: 1023px) {
|
@media (max-width: 1023px) {
|
||||||
.order-2 {
|
.order-2 {
|
||||||
order: 2;
|
order: 2;
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@ export class FooterComponent {
|
||||||
privacyVisible = false;
|
privacyVisible = false;
|
||||||
termsVisible = false;
|
termsVisible = false;
|
||||||
currentYear: number = new Date().getFullYear();
|
currentYear: number = new Date().getFullYear();
|
||||||
|
isHomeRoute = false;
|
||||||
constructor(private router: Router) {}
|
constructor(private router: Router) {}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
|
this.isHomeRoute = event.url === '/home';
|
||||||
initFlowbite();
|
initFlowbite();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<nav class="bg-white border-gray-200 dark:bg-gray-900 print:hidden">
|
<nav class="bg-white border-gray-200 dark:bg-gray-900 print:hidden">
|
||||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
<div class="flex flex-wrap items-center justify-between mx-auto p-4">
|
||||||
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||||
<img src="assets/images/header-logo.png" class="h-8" alt="Flowbite Logo" />
|
<img src="assets/images/header-logo.png" class="h-8" alt="Flowbite Logo" />
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -1,183 +1,243 @@
|
||||||
<header class="w-full flex justify-between items-center p-4 bg-white top-0 z-10 h-16 md:h-20">
|
<!-- Navigation -->
|
||||||
<img src="assets/images/header-logo.png" alt="Logo" class="h-8 md:h-10" />
|
<nav class="bg-white">
|
||||||
<div class="hidden md:flex items-center space-x-4">
|
<div class="container mx-auto px-6 py-3 flex justify-between items-center">
|
||||||
@if(user){
|
<div class="flex items-center">
|
||||||
<a routerLink="/account" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Account</a>
|
<a href="#" class="text-2xl font-bold text-blue-800">
|
||||||
} @else {
|
<img src="assets/images/header-logo.png" alt="BizMatch.net" class="h-10" />
|
||||||
<!-- <a routerLink="/pricing" class="text-gray-800">Pricing</a> -->
|
</a>
|
||||||
<a routerLink="/login" [queryParams]="{ mode: 'login' }" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Log In</a>
|
</div>
|
||||||
<a routerLink="/login" [queryParams]="{ mode: 'register' }" class="text-white bg-blue-600 px-4 py-2 rounded">Register</a>
|
<div class="hidden md:flex items-center space-x-8">
|
||||||
<!-- <a routerLink="/login" class="text-blue-500 hover:underline">Login/Register</a> -->
|
<a href="#" class="text-gray-800 hover:text-blue-600">Home</a>
|
||||||
}
|
<a routerLink="/businessListings" class="text-blue-700 hover:font-bold">Businesses</a>
|
||||||
|
<a href="#services" class="text-gray-800 hover:text-blue-600">Services</a>
|
||||||
|
<a href="#location" class="text-gray-800 hover:text-blue-600">Location</a>
|
||||||
|
<a href="#contact" class="text-gray-800 hover:text-blue-600">Contact</a>
|
||||||
|
</div>
|
||||||
|
<div class="md:hidden">
|
||||||
|
<button class="text-gray-800 focus:outline-none" (click)="toggleMobileMenu()">
|
||||||
|
<svg class="h-6 w-6 fill-current" viewBox="0 0 24 24">
|
||||||
|
<path d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button (click)="toggleMenu()" class="md:hidden text-gray-600">
|
<!-- Mobile menu (only shows when toggleMobileMenu is true) -->
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<div *ngIf="showMobileMenu" class="md:hidden bg-white py-2 px-4">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
<a href="#" class="block py-2 text-gray-800 hover:text-blue-600">Home</a>
|
||||||
</svg>
|
<a href="#services" class="block py-2 text-gray-800 hover:text-blue-600">Services</a>
|
||||||
</button>
|
<a href="#location" class="block py-2 text-gray-800 hover:text-blue-600">Location</a>
|
||||||
</header>
|
<a href="#contact" class="block py-2 text-gray-800 hover:text-blue-600">Contact</a>
|
||||||
|
|
||||||
<div *ngIf="isMenuOpen" class="fixed inset-0 bg-gray-800 bg-opacity-75 z-20">
|
|
||||||
<div class="flex flex-col items-center justify-center h-full">
|
|
||||||
<!-- <a href="#" class="text-white text-xl py-2">Pricing</a> -->
|
|
||||||
@if(user){
|
|
||||||
<a routerLink="/account" class="text-white text-xl py-2">Account</a>
|
|
||||||
} @else {
|
|
||||||
<a routerLink="/login" [queryParams]="{ mode: 'login' }" class="text-white text-xl py-2">Log In</a>
|
|
||||||
<a routerLink="/login" [queryParams]="{ mode: 'register' }" class="text-white text-xl py-2">Register</a>
|
|
||||||
}
|
|
||||||
<button (click)="toggleMenu()" class="text-white mt-4">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
<!-- ==== ANPASSUNGEN START ==== -->
|
<!-- Hero Section (made narrower) -->
|
||||||
<!-- 1. px-4 für <main> (vorher px-2 sm:px-4) -->
|
<section class="hero-section flex items-center px-[2rem] py-[5rem]">
|
||||||
<main class="flex flex-col items-center justify-center px-4 w-full flex-grow">
|
<div class="container mx-auto px-6 flex flex-col">
|
||||||
<div class="bg-cover-custom py-12 md:py-20 lg:py-32 flex flex-col w-full rounded-xl lg:rounded-2xl md:drop-shadow-custom-md lg:drop-shadow-custom-lg min-h-[calc(100vh_-_20rem)] lg:min-h-[calc(100vh_-_10rem)]">
|
<!-- max-w-5xl makes it narrower -->
|
||||||
<div class="flex justify-center w-full">
|
<div class="flex flex-col md:flex-row items-center">
|
||||||
<!-- 3. Für Mobile: m-2 statt max-w-xs; ab sm: wieder max-width und kein Margin -->
|
<div class="md:w-1/2 text-white">
|
||||||
<div class="w-full m-2 sm:m-0 sm:max-w-md md:max-w-xl lg:max-w-2xl xl:max-w-3xl">
|
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight mb-4">Connect with Your Ideal Business Opportunity</h1>
|
||||||
<!-- Schriftgrößen angepasst (wie vorher) -->
|
<p class="text-xl mb-8">BizMatch is your trusted partner in buying, selling, and valuing businesses in Texas.</p>
|
||||||
<h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-blue-900 mb-3 sm:mb-4 text-center">Find businesses for sale.</h1>
|
</div>
|
||||||
<!-- Abstand unten angepasst (wie vorher) -->
|
<div class="md:w-1/2 flex justify-center">
|
||||||
<p class="text-base md:text-lg lg:text-xl text-blue-600 mb-6 md:mb-8 text-center">Unlocking Exclusive Opportunities - Empowering Entrepreneurial Dreams</p>
|
<img src="assets/images/corpusChristiSkyline.jpg" alt="Business handshake" class="rounded-lg shadow-2xl" />
|
||||||
<!-- Restliche Anpassungen (Innenabstände, Button-Paddings etc.) bleiben wie im vorherigen Schritt -->
|
</div>
|
||||||
<div class="bg-white bg-opacity-80 pb-4 md:pb-6 pt-2 px-3 sm:px-4 md:px-6 rounded-lg shadow-lg w-full" [ngClass]="{ 'pt-6': aiSearch }">
|
</div>
|
||||||
@if(!aiSearch){
|
<div class="flex justify-center mt-10">
|
||||||
<div class="text-sm lg:text-base mb-1 text-center text-gray-500 border-gray-200 dark:text-gray-400 dark:border-gray-700 flex justify-between">
|
<a routerLink="/businessListings" class="bg-green-500 md:text-2xl text-lg text-white font-semibold px-8 py-4 rounded-full shadow-lg hover:bg-green-600 transition duration-300"> View Available Businesses </a>
|
||||||
<ul class="flex flex-wrap -mb-px w-full">
|
</div>
|
||||||
<li class="w-[33%]">
|
</div>
|
||||||
<a
|
</section>
|
||||||
(click)="changeTab('business')"
|
|
||||||
[ngClass]="
|
|
||||||
activeTabAction === 'business'
|
|
||||||
? ['text-blue-600', 'border-blue-600', 'active', 'dark:text-blue-500', 'dark:border-blue-500']
|
|
||||||
: ['border-transparent', 'hover:text-gray-600', 'hover:border-gray-300', 'dark:hover:text-gray-300']
|
|
||||||
"
|
|
||||||
class="hover:cursor-pointer inline-block px-1 py-2 md:p-4 border-b-2 rounded-t-lg"
|
|
||||||
>Businesses</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
@if ((numberOfCommercial$ | async) > 0) {
|
|
||||||
<li class="w-[33%]">
|
|
||||||
<a
|
|
||||||
(click)="changeTab('commercialProperty')"
|
|
||||||
[ngClass]="
|
|
||||||
activeTabAction === 'commercialProperty'
|
|
||||||
? ['text-blue-600', 'border-blue-600', 'active', 'dark:text-blue-500', 'dark:border-blue-500']
|
|
||||||
: ['border-transparent', 'hover:text-gray-600', 'hover:border-gray-300', 'dark:hover:text-gray-300']
|
|
||||||
"
|
|
||||||
class="hover:cursor-pointer inline-block px-1 py-2 md:p-4 border-b-2 rounded-t-lg"
|
|
||||||
>Properties</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
} @if ((numberOfBroker$ | async) > 0) {
|
|
||||||
<li class="w-[33%]">
|
|
||||||
<a
|
|
||||||
(click)="changeTab('broker')"
|
|
||||||
[ngClass]="
|
|
||||||
activeTabAction === 'broker'
|
|
||||||
? ['text-blue-600', 'border-blue-600', 'active', 'dark:text-blue-500', 'dark:border-blue-500']
|
|
||||||
: ['border-transparent', 'hover:text-gray-600', 'hover:border-gray-300', 'dark:hover:text-gray-300']
|
|
||||||
"
|
|
||||||
class="hover:cursor-pointer inline-block px-1 py-2 md:p-4 border-b-2 rounded-t-lg"
|
|
||||||
>Professionals</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
} @if(criteria && !aiSearch){
|
|
||||||
<div class="w-full max-w-3xl mx-auto bg-white rounded-lg flex flex-col md:flex-row md:border md:border-gray-300">
|
|
||||||
<div class="md:flex-none md:w-48 flex-1 md:border-r border-gray-300 overflow-hidden mb-2 md:mb-0">
|
|
||||||
<div class="relative max-sm:border border-gray-300 rounded-md">
|
|
||||||
<select
|
|
||||||
class="appearance-none bg-transparent w-full py-3 px-4 pr-8 focus:outline-none md:border-none rounded-md md:rounded-none"
|
|
||||||
[ngModel]="criteria.types"
|
|
||||||
(ngModelChange)="onTypesChange($event)"
|
|
||||||
[ngClass]="{ 'placeholder-selected': criteria.types.length === 0 }"
|
|
||||||
>
|
|
||||||
<option [value]="[]">{{ getPlaceholderLabel() }}</option>
|
|
||||||
@for(type of getTypes(); track type){
|
|
||||||
<option [value]="type.value">{{ type.name }}</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
|
||||||
<i class="fas fa-chevron-down text-xs"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="md:flex-auto md:w-36 flex-grow md:border-r border-gray-300 mb-2 md:mb-0">
|
<!-- Services Section -->
|
||||||
<div class="relative max-sm:border border-gray-300 rounded-md">
|
<section id="services" class="py-20 bg-gray-50">
|
||||||
<ng-select
|
<div class="container mx-auto px-6">
|
||||||
class="custom md:border-none rounded-md md:rounded-none"
|
<div class="text-center mb-16">
|
||||||
[multiple]="false"
|
<h2 class="text-3xl font-bold text-blue-800 mb-4">Our Services</h2>
|
||||||
[hideSelected]="true"
|
<p class="text-gray-600 max-w-2xl mx-auto">We offer comprehensive business brokerage services to help you navigate the complex process of buying or selling a business.</p>
|
||||||
[trackByFn]="trackByFn"
|
</div>
|
||||||
[minTermLength]="2"
|
|
||||||
[loading]="cityLoading"
|
<div class="flex flex-wrap -mx-4">
|
||||||
typeToSearchText="Please enter 2 or more characters"
|
<!-- Service 1 -->
|
||||||
[typeahead]="cityInput$"
|
<div class="w-full md:w-1/3 px-4 mb-8">
|
||||||
[ngModel]="cityOrState"
|
<div class="service-card bg-white rounded-lg filter md:drop-shadow-custom-bg drop-shadow-custom-bg-mobile p-8 h-full">
|
||||||
(ngModelChange)="setCityOrState($event)"
|
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
placeholder="Enter City or State ..."
|
<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
groupBy="type"
|
<path
|
||||||
>
|
d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"
|
||||||
@for (city of cities$ | async; track city.id) { @let state = city.type==='city'?city.content.state:''; @let separator = city.type==='city'?' - ':'';
|
></path>
|
||||||
<ng-option [value]="city">{{ city.content.name }}{{ separator }}{{ state }}</ng-option>
|
</svg>
|
||||||
}
|
|
||||||
</ng-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (criteria.radius && !aiSearch){
|
|
||||||
<div class="md:flex-none md:w-36 flex-1 md:border-r border-gray-300 mb-2 md:mb-0">
|
|
||||||
<div class="relative max-sm:border border-gray-300 rounded-md">
|
|
||||||
<select
|
|
||||||
class="appearance-none bg-transparent w-full py-3 px-4 pr-8 focus:outline-none md:border-none rounded-md md:rounded-none"
|
|
||||||
(ngModelChange)="onRadiusChange($event)"
|
|
||||||
[ngModel]="criteria.radius"
|
|
||||||
[ngClass]="{ 'placeholder-selected': !criteria.radius }"
|
|
||||||
>
|
|
||||||
<option [value]="null">City Radius</option>
|
|
||||||
@for(dist of selectOptions.distances; track dist){
|
|
||||||
<option [value]="dist.value">{{ dist.name }}</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
|
||||||
<i class="fas fa-chevron-down text-xs"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="bg-blue-600 hover:bg-blue-500 transition-colors duration-200 max-sm:rounded-md">
|
|
||||||
@if(getNumberOfFiltersSet()>0 && numberOfResults$){
|
|
||||||
<button class="w-full h-full text-white font-semibold py-2 px-4 md:py-3 md:px-6 focus:outline-none rounded-md md:rounded-none min-h-[48px]" (click)="search()">
|
|
||||||
Search ({{ numberOfResults$ | async }})
|
|
||||||
</button>
|
|
||||||
}@else {
|
|
||||||
<button class="w-full h-full text-white font-semibold py-2 px-4 md:py-3 md:px-6 focus:outline-none rounded-md md:rounded-none min-h-[48px]" (click)="search()">Search</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
<h3 class="text-xl font-semibold text-blue-800 mb-4 text-center">Business Sales</h3>
|
||||||
<!-- <div class="mt-4 flex items-center justify-center text-gray-700">
|
<p class="text-gray-600 text-center">We help business owners prepare and market their businesses to qualified buyers, ensuring confidentiality throughout the process.</p>
|
||||||
<span class="mr-2">AI-Search</span>
|
</div>
|
||||||
<span [attr.data-tooltip-target]="tooltipTargetBeta" class="bg-sky-300 text-teal-800 text-xs font-semibold px-2 py-1 rounded">BETA</span>
|
</div>
|
||||||
<app-tooltip [id]="tooltipTargetBeta" text="AI will convert your input into filter criteria. Please check them in the filter menu after search"></app-tooltip>
|
|
||||||
<span class="ml-2">- Try now</span>
|
<!-- Service 2 -->
|
||||||
<div class="ml-4 relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in">
|
<div class="w-full md:w-1/3 px-4 mb-8">
|
||||||
<input (click)="toggleAiSearch()" type="checkbox" name="toggle" id="toggle" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 border-gray-300 appearance-none cursor-pointer" />
|
<div class="service-card bg-white rounded-lg filter md:drop-shadow-custom-bg drop-shadow-custom-bg-mobile p-8 h-full">
|
||||||
<label for="toggle" class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"></label>
|
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
</div>
|
<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
</div> -->
|
<path fill-rule="evenodd" d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-4 text-center">Business Acquisitions</h3>
|
||||||
|
<p class="text-gray-600 text-center">We assist buyers in finding the right business opportunity, perform due diligence, and negotiate favorable terms for acquisition.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Service 3 -->
|
||||||
|
<div class="w-full md:w-1/3 px-4 mb-8">
|
||||||
|
<div class="service-card bg-white rounded-lg filter md:drop-shadow-custom-bg drop-shadow-custom-bg-mobile p-8 h-full">
|
||||||
|
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
|
<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zM12 2a1 1 0 01.967.744L14.146 7.2 17.5 9.134a1 1 0 010 1.732l-3.354 1.935-1.18 4.455a1 1 0 01-1.933 0L9.854 12.8 6.5 10.866a1 1 0 010-1.732l3.354-1.935 1.18-4.455A1 1 0 0112 2z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-4 text-center">Business Valuation</h3>
|
||||||
|
<p class="text-gray-600 text-center">Our expert team provides accurate business valuations based on industry standards, financial performance, and market conditions.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Video Section -->
|
||||||
|
<div class="mt-16 text-center">
|
||||||
|
<h3 class="text-2xl font-semibold text-blue-800 mb-8">See How We Work</h3>
|
||||||
|
<div class="max-w-4xl mx-auto">
|
||||||
|
<video controls class="w-full rounded-lg shadow-xl" poster="assets/images/video-poster.png">
|
||||||
|
<source src="assets/videos/Bizmatch30Spot.mp4" type="video/mp4" />
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Why Choose Us Section -->
|
||||||
|
<section class="py-20 bg-white">
|
||||||
|
<div class="container mx-auto px-6">
|
||||||
|
<div class="text-center mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-blue-800 mb-4">Why Choose BizMatch</h2>
|
||||||
|
<p class="text-gray-600 max-w-2xl mx-auto">With decades of experience in the business brokerage industry, we provide unparalleled service to our clients.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap -mx-4">
|
||||||
|
<!-- Feature 1 -->
|
||||||
|
<div class="w-full md:w-1/4 px-4 mb-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
|
<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-2">Experience</h3>
|
||||||
|
<p class="text-gray-600">Over 25 years of combined experience in business brokerage.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feature 2 -->
|
||||||
|
<div class="w-full md:w-1/4 px-4 mb-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
|
<svg class="w-8 h-8 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-2">Confidentiality</h3>
|
||||||
|
<p class="text-gray-600">We maintain strict confidentiality throughout the entire transaction process.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feature 3 -->
|
||||||
|
<div class="w-full md:w-1/4 px-4 mb-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
|
<svg class="w-8 h-8 text-purple-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-2">Network</h3>
|
||||||
|
<p class="text-gray-600">Extensive network of qualified buyers and business owners throughout Texas.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feature 4 -->
|
||||||
|
<div class="w-full md:w-1/4 px-4 mb-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-16 h-16 bg-yellow-100 rounded-full flex items-center justify-center mb-6 mx-auto">
|
||||||
|
<svg class="w-8 h-8 text-yellow-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-2">Personalized Approach</h3>
|
||||||
|
<p class="text-gray-600">Customized strategy for each client based on their unique business goals.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</section>
|
||||||
<!-- ==== ANPASSUNGEN ENDE ==== -->
|
|
||||||
|
<!-- Location Section with Map -->
|
||||||
|
<section id="location" class="py-20 bg-gray-50">
|
||||||
|
<div class="container mx-auto px-6">
|
||||||
|
<div class="flex flex-wrap items-center">
|
||||||
|
<div class="w-full lg:w-2/5 mb-12 lg:mb-0">
|
||||||
|
<!-- Changed from 1/2 to 2/5 to make it smaller -->
|
||||||
|
<h2 class="text-3xl font-bold text-blue-800 mb-6">Visit Our Office</h2>
|
||||||
|
<p class="text-gray-600 mb-8 text-lg">Our team of business brokers is ready to assist you at our Corpus Christi location.</p>
|
||||||
|
<div class="bg-white p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 class="text-xl font-semibold text-blue-800 mb-4">BizMatch Headquarters</h3>
|
||||||
|
<p class="text-gray-600 mb-2">1001 Blucher Street</p>
|
||||||
|
<p class="text-gray-600 mb-2">Corpus Christi, TX 78401</p>
|
||||||
|
<p class="text-gray-600 mb-6">United States</p>
|
||||||
|
<p class="text-gray-600 mb-2"><strong>Phone:</strong> (555) 123-4567</p>
|
||||||
|
<p class="text-gray-600"><strong>Email:</strong> info@bizmatch.net</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full lg:w-3/5">
|
||||||
|
<!-- Changed from 1/2 to 3/5 for the map -->
|
||||||
|
<div class="rounded-lg overflow-hidden shadow-xl h-96">
|
||||||
|
<!-- Added fixed height -->
|
||||||
|
<!-- Google Maps integration -->
|
||||||
|
<!-- <google-map [options]="mapOptions" height="100%" width="100%">
|
||||||
|
<map-marker [position]="markerPosition"></map-marker>
|
||||||
|
</google-map> -->
|
||||||
|
<iframe
|
||||||
|
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3331.7894679685755!2d-98.48527228476843!3d27.773756032788047!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x866c1e3b8a9d0c0b%3A0x8f2c1d4c1a5c5b2c!2s1001%20Blucher%20St%2C%20Corpus%20Christi%2C%20TX%2078401%2C%20USA!5e0!3m2!1sen!2sde!4v1672531192743!5m2!1sen!2sde"
|
||||||
|
width="100%"
|
||||||
|
height="417"
|
||||||
|
class="rounded-lg shadow-lg"
|
||||||
|
style="border: 0"
|
||||||
|
allowfullscreen=""
|
||||||
|
loading="lazy"
|
||||||
|
referrerpolicy="no-referrer-when-downgrade"
|
||||||
|
>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Contact Section -->
|
||||||
|
<section id="contact" class="py-20 bg-blue-700">
|
||||||
|
<div class="container mx-auto px-6 text-center">
|
||||||
|
<h2 class="text-3xl font-bold text-white mb-8">Ready to Get Started?</h2>
|
||||||
|
<p class="text-white text-xl mb-12 max-w-3xl mx-auto">Contact our team of experienced business brokers today for a confidential consultation about buying or selling a business.</p>
|
||||||
|
<a routerLink="/emailUs" class="bg-white text-blue-700 font-bold px-8 py-4 rounded-lg shadow-lg hover:bg-gray-100 transition duration-300 text-lg">Contact Us Now</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,45 @@
|
||||||
.bg-cover-custom {
|
// Hero section styles
|
||||||
background-image: url('/assets/images/index-bg.webp');
|
.hero-section {
|
||||||
background-size: cover;
|
background: linear-gradient(135deg, #0046b5 0%, #00a0e9 100%);
|
||||||
background-position: center;
|
// height: 70vh; // Made shorter as requested
|
||||||
border-radius: 20px;
|
// min-height: 500px; // Reduced from 600px
|
||||||
}
|
}
|
||||||
select:not([size]) {
|
|
||||||
background-image: unset;
|
// Button hover effects
|
||||||
}
|
.btn-primary {
|
||||||
[type='text'],
|
background-color: #0046b5;
|
||||||
[type='email'],
|
transition: all 0.3s ease;
|
||||||
[type='url'],
|
|
||||||
[type='password'],
|
&:hover {
|
||||||
[type='number'],
|
background-color: #003492;
|
||||||
[type='date'],
|
|
||||||
[type='datetime-local'],
|
|
||||||
[type='month'],
|
|
||||||
[type='search'],
|
|
||||||
[type='tel'],
|
|
||||||
[type='time'],
|
|
||||||
[type='week'],
|
|
||||||
[multiple],
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
border: unset;
|
|
||||||
}
|
|
||||||
.toggle-checkbox:checked {
|
|
||||||
right: 0;
|
|
||||||
border-color: rgb(125 211 252);
|
|
||||||
}
|
|
||||||
.toggle-checkbox:checked + .toggle-label {
|
|
||||||
background-color: rgb(125 211 252);
|
|
||||||
}
|
|
||||||
:host ::ng-deep .ng-select.ng-select-single .ng-select-container {
|
|
||||||
height: 48px;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
.ng-value-container .ng-input {
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
span.ng-arrow-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
|
||||||
color: #000; /* Standard-Textfarbe für das Dropdown */
|
// Service card animation
|
||||||
// background-color: #fff; /* Hintergrundfarbe für das Dropdown */
|
.service-card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select option {
|
// Responsive adjustments
|
||||||
color: #000; /* Textfarbe für Dropdown-Optionen */
|
@media (max-width: 768px) {
|
||||||
|
.hero-section {
|
||||||
|
height: auto;
|
||||||
|
padding: 4rem 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select.placeholder-selected {
|
// Make sure the Google Map is responsive
|
||||||
color: #999; /* Farbe für den Platzhalter */
|
google-map {
|
||||||
}
|
display: block;
|
||||||
input::placeholder {
|
width: 100%;
|
||||||
color: #555; /* Dunkleres Grau */
|
|
||||||
opacity: 1; /* Stellt sicher, dass die Deckkraft 100% ist */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stellt sicher, dass die Optionen im Dropdown immer schwarz sind */
|
// Override Tailwind default styling for video
|
||||||
select:focus option,
|
video {
|
||||||
select:hover option {
|
max-width: 100%;
|
||||||
color: #000 !important;
|
object-fit: cover;
|
||||||
}
|
|
||||||
input[type='text'][name='aiSearchText'] {
|
|
||||||
padding: 14px; /* Innerer Abstand */
|
|
||||||
font-size: 16px; /* Schriftgröße anpassen */
|
|
||||||
box-sizing: border-box; /* Padding und Border in die Höhe und Breite einrechnen */
|
|
||||||
height: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,313 +1,56 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ChangeDetectorRef, Component, ElementRef, ViewChild } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { RouterLink, RouterOutlet } from '@angular/router';
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
|
||||||
import { NgSelectModule } from '@ng-select/ng-select';
|
|
||||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
|
||||||
import { initFlowbite } from 'flowbite';
|
|
||||||
import { catchError, concat, debounceTime, distinctUntilChanged, lastValueFrom, Observable, of, Subject, Subscription, switchMap, tap } from 'rxjs';
|
|
||||||
import { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingCriteria, GeoResult, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
|
||||||
import { ModalService } from '../../components/search-modal/modal.service';
|
|
||||||
import { TooltipComponent } from '../../components/tooltip/tooltip.component';
|
|
||||||
import { AiService } from '../../services/ai.service';
|
|
||||||
import { AuthService } from '../../services/auth.service';
|
|
||||||
import { CriteriaChangeService } from '../../services/criteria-change.service';
|
|
||||||
import { GeoService } from '../../services/geo.service';
|
|
||||||
import { ListingsService } from '../../services/listings.service';
|
|
||||||
import { SearchService } from '../../services/search.service';
|
|
||||||
import { SelectOptionsService } from '../../services/select-options.service';
|
|
||||||
import { UserService } from '../../services/user.service';
|
|
||||||
import {
|
|
||||||
assignProperties,
|
|
||||||
compareObjects,
|
|
||||||
createEmptyBusinessListingCriteria,
|
|
||||||
createEmptyCommercialPropertyListingCriteria,
|
|
||||||
createEmptyUserListingCriteria,
|
|
||||||
createEnhancedProxy,
|
|
||||||
getCriteriaStateObject,
|
|
||||||
map2User,
|
|
||||||
} from '../../utils/utils';
|
|
||||||
@UntilDestroy()
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule, FormsModule, RouterModule, NgSelectModule, TooltipComponent],
|
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.scss',
|
styleUrls: ['./home.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, RouterOutlet, RouterLink],
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent implements OnInit {
|
||||||
placeholders: string[] = ['Property close to Houston less than 10M', 'Franchise business in Austin price less than 500K'];
|
showMobileMenu = false;
|
||||||
activeTabAction: 'business' | 'commercialProperty' | 'broker' = 'business';
|
|
||||||
type: string;
|
|
||||||
maxPrice: string;
|
|
||||||
minPrice: string;
|
|
||||||
criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
|
||||||
states = [];
|
|
||||||
isMenuOpen = false;
|
|
||||||
user: KeycloakUser;
|
|
||||||
prompt: string;
|
|
||||||
cities$: Observable<CityAndStateResult[]>;
|
|
||||||
cityLoading = false;
|
|
||||||
cityInput$ = new Subject<string>();
|
|
||||||
cityOrState = undefined;
|
|
||||||
private criteriaChangeSubscription: Subscription;
|
|
||||||
numberOfResults$: Observable<number>;
|
|
||||||
numberOfBroker$: Observable<number>;
|
|
||||||
numberOfCommercial$: Observable<number>;
|
|
||||||
aiSearch = false;
|
|
||||||
aiSearchText = '';
|
|
||||||
aiSearchFailed = false;
|
|
||||||
loadingAi = false;
|
|
||||||
@ViewChild('aiSearchInput', { static: false }) searchInput!: ElementRef;
|
|
||||||
typingSpeed: number = 100; // Geschwindigkeit des Tippens (ms)
|
|
||||||
pauseTime: number = 2000; // Pausezeit, bevor der Text verschwindet (ms)
|
|
||||||
index: number = 0;
|
|
||||||
charIndex: number = 0;
|
|
||||||
typingInterval: any;
|
|
||||||
showInput: boolean = true; // Steuerung der Anzeige des Eingabefelds
|
|
||||||
tooltipTargetBeta = 'tooltipTargetBeta';
|
|
||||||
public constructor(
|
|
||||||
private router: Router,
|
|
||||||
private modalService: ModalService,
|
|
||||||
private searchService: SearchService,
|
|
||||||
private activatedRoute: ActivatedRoute,
|
|
||||||
public selectOptions: SelectOptionsService,
|
|
||||||
|
|
||||||
private criteriaChangeService: CriteriaChangeService,
|
// Google Maps configuration
|
||||||
private geoService: GeoService,
|
// mapOptions: google.maps.MapOptions = {
|
||||||
public cdRef: ChangeDetectorRef,
|
// center: { lat: 27.777, lng: -97.396 }, // Corpus Christi coordinates
|
||||||
private listingService: ListingsService,
|
// zoom: 15,
|
||||||
private userService: UserService,
|
// mapTypeId: 'roadmap',
|
||||||
private aiService: AiService,
|
// scrollwheel: false,
|
||||||
private authService: AuthService,
|
// disableDefaultUI: true,
|
||||||
) {}
|
// zoomControl: true,
|
||||||
async ngOnInit() {
|
// streetViewControl: true,
|
||||||
setTimeout(() => {
|
// };
|
||||||
initFlowbite();
|
|
||||||
}, 0);
|
// Marker position for office location
|
||||||
this.numberOfBroker$ = this.userService.getNumberOfBroker(createEmptyUserListingCriteria());
|
// markerPosition: google.maps.LatLngLiteral = {
|
||||||
this.numberOfCommercial$ = this.listingService.getNumberOfListings(createEmptyCommercialPropertyListingCriteria(), 'commercialProperty');
|
// lat: 27.777,
|
||||||
const token = await this.authService.getToken();
|
// lng: -97.396,
|
||||||
sessionStorage.removeItem('businessListings');
|
// };
|
||||||
sessionStorage.removeItem('commercialPropertyListings');
|
|
||||||
sessionStorage.removeItem('brokerListings');
|
constructor() {}
|
||||||
this.criteria = createEnhancedProxy(getCriteriaStateObject('businessListings'), this);
|
|
||||||
this.user = map2User(token);
|
ngOnInit(): void {
|
||||||
this.loadCities();
|
// Add smooth scrolling for anchor links
|
||||||
this.setupCriteriaChangeListener();
|
this.setupSmoothScrolling();
|
||||||
}
|
|
||||||
async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') {
|
|
||||||
this.activeTabAction = tabname;
|
|
||||||
this.cityOrState = null;
|
|
||||||
if ('business' === tabname) {
|
|
||||||
this.criteria = createEnhancedProxy(getCriteriaStateObject('businessListings'), this);
|
|
||||||
} else if ('commercialProperty' === tabname) {
|
|
||||||
this.criteria = createEnhancedProxy(getCriteriaStateObject('commercialPropertyListings'), this);
|
|
||||||
} else if ('broker' === tabname) {
|
|
||||||
this.criteria = createEnhancedProxy(getCriteriaStateObject('brokerListings'), this);
|
|
||||||
} else {
|
|
||||||
this.criteria = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
toggleMobileMenu(): void {
|
||||||
this.router.navigate([`${this.activeTabAction}Listings`]);
|
this.showMobileMenu = !this.showMobileMenu;
|
||||||
}
|
|
||||||
private setupCriteriaChangeListener() {
|
|
||||||
this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(untilDestroyed(this), debounceTime(400)).subscribe(() => this.setTotalNumberOfResults());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMenu() {
|
private setupSmoothScrolling(): void {
|
||||||
this.isMenuOpen = !this.isMenuOpen;
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
}
|
anchor.addEventListener('click', function (e) {
|
||||||
onTypesChange(value) {
|
e.preventDefault();
|
||||||
if (value === '') {
|
const target = document.querySelector((this as HTMLAnchorElement).getAttribute('href') || '');
|
||||||
// Wenn keine Option ausgewählt ist, setzen Sie types zurück auf ein leeres Array
|
if (target) {
|
||||||
this.criteria.types = [];
|
target.scrollIntoView({
|
||||||
} else {
|
behavior: 'smooth',
|
||||||
this.criteria.types = [value];
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
onRadiusChange(value) {
|
|
||||||
if (value === 'null') {
|
|
||||||
// Wenn keine Option ausgewählt ist, setzen Sie types zurück auf ein leeres Array
|
|
||||||
this.criteria.radius = null;
|
|
||||||
} else {
|
|
||||||
this.criteria.radius = parseInt(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async openModal() {
|
|
||||||
const accepted = await this.modalService.showModal(this.criteria);
|
|
||||||
if (accepted) {
|
|
||||||
this.router.navigate([`${this.activeTabAction}Listings`]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private loadCities() {
|
|
||||||
this.cities$ = concat(
|
|
||||||
of([]), // default items
|
|
||||||
this.cityInput$.pipe(
|
|
||||||
distinctUntilChanged(),
|
|
||||||
tap(() => (this.cityLoading = true)),
|
|
||||||
switchMap(term =>
|
|
||||||
//this.geoService.findCitiesStartingWith(term).pipe(
|
|
||||||
this.geoService.findCitiesAndStatesStartingWith(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.cityLoading = false)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
trackByFn(item: GeoResult) {
|
|
||||||
return item.id;
|
|
||||||
}
|
|
||||||
setCityOrState(cityOrState: CityAndStateResult) {
|
|
||||||
if (cityOrState) {
|
|
||||||
if (cityOrState.type === 'state') {
|
|
||||||
this.criteria.state = cityOrState.content.state_code;
|
|
||||||
} else {
|
|
||||||
this.criteria.city = cityOrState.content as GeoResult;
|
|
||||||
this.criteria.state = cityOrState.content.state;
|
|
||||||
this.criteria.searchType = 'radius';
|
|
||||||
this.criteria.radius = 20;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.criteria.state = null;
|
|
||||||
this.criteria.city = null;
|
|
||||||
this.criteria.radius = null;
|
|
||||||
this.criteria.searchType = 'exact';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getTypes() {
|
|
||||||
if (this.criteria.criteriaType === 'businessListings') {
|
|
||||||
return this.selectOptions.typesOfBusiness;
|
|
||||||
} else if (this.criteria.criteriaType === 'commercialPropertyListings') {
|
|
||||||
return this.selectOptions.typesOfCommercialProperty;
|
|
||||||
} else {
|
|
||||||
return this.selectOptions.customerSubTypes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getPlaceholderLabel() {
|
|
||||||
if (this.criteria.criteriaType === 'businessListings') {
|
|
||||||
return 'Business Type';
|
|
||||||
} else if (this.criteria.criteriaType === 'commercialPropertyListings') {
|
|
||||||
return 'Property Type';
|
|
||||||
} else {
|
|
||||||
return 'Professional Type';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTotalNumberOfResults() {
|
|
||||||
if (this.criteria) {
|
|
||||||
console.log(`Getting total number of results for ${this.criteria.criteriaType}`);
|
|
||||||
if (this.criteria.criteriaType === 'businessListings' || this.criteria.criteriaType === 'commercialPropertyListings') {
|
|
||||||
this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty');
|
|
||||||
} else if (this.criteria.criteriaType === 'brokerListings') {
|
|
||||||
this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria);
|
|
||||||
} else {
|
|
||||||
this.numberOfResults$ = of();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getNumberOfFiltersSet() {
|
|
||||||
if (this.criteria?.criteriaType === 'brokerListings') {
|
|
||||||
return compareObjects(createEmptyUserListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
|
|
||||||
} else if (this.criteria?.criteriaType === 'businessListings') {
|
|
||||||
return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
|
|
||||||
} else if (this.criteria?.criteriaType === 'commercialPropertyListings') {
|
|
||||||
return compareObjects(createEmptyCommercialPropertyListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toggleAiSearch() {
|
|
||||||
this.aiSearch = !this.aiSearch;
|
|
||||||
this.aiSearchFailed = false;
|
|
||||||
if (!this.aiSearch) {
|
|
||||||
this.aiSearchText = '';
|
|
||||||
this.stopTypingEffect();
|
|
||||||
} else {
|
|
||||||
setTimeout(() => this.startTypingEffect(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
clearTimeout(this.typingInterval); // Stelle sicher, dass das Intervall gestoppt wird, wenn die Komponente zerstört wird
|
|
||||||
}
|
|
||||||
|
|
||||||
startTypingEffect(): void {
|
|
||||||
if (!this.aiSearchText) {
|
|
||||||
this.typePlaceholder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopTypingEffect(): void {
|
|
||||||
clearTimeout(this.typingInterval);
|
|
||||||
}
|
|
||||||
typePlaceholder(): void {
|
|
||||||
if (!this.searchInput || !this.searchInput.nativeElement) {
|
|
||||||
return; // Falls das Eingabefeld nicht verfügbar ist (z.B. durch ngIf)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.aiSearchText) {
|
|
||||||
return; // Stoppe, wenn der Benutzer Text eingegeben hat
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputField = this.searchInput.nativeElement as HTMLInputElement;
|
|
||||||
if (document.activeElement === inputField) {
|
|
||||||
this.stopTypingEffect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputField.placeholder = this.placeholders[this.index].substring(0, this.charIndex);
|
|
||||||
|
|
||||||
if (this.charIndex < this.placeholders[this.index].length) {
|
|
||||||
this.charIndex++;
|
|
||||||
this.typingInterval = setTimeout(() => this.typePlaceholder(), this.typingSpeed);
|
|
||||||
} else {
|
|
||||||
// Nach dem vollständigen Tippen eine Pause einlegen
|
|
||||||
this.typingInterval = setTimeout(() => {
|
|
||||||
inputField.placeholder = ''; // Schlagartiges Löschen des Platzhalters
|
|
||||||
this.charIndex = 0;
|
|
||||||
this.index = (this.index + 1) % this.placeholders.length;
|
|
||||||
this.typingInterval = setTimeout(() => this.typePlaceholder(), this.typingSpeed);
|
|
||||||
}, this.pauseTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async generateAiResponse() {
|
|
||||||
this.loadingAi = true;
|
|
||||||
this.aiSearchFailed = false;
|
|
||||||
try {
|
|
||||||
const result = await this.aiService.generateAiReponse(this.aiSearchText);
|
|
||||||
let criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria | any;
|
|
||||||
if (result.criteriaType === 'businessListings') {
|
|
||||||
this.changeTab('business');
|
|
||||||
criteria = result as BusinessListingCriteria;
|
|
||||||
} else if (result.criteriaType === 'commercialPropertyListings') {
|
|
||||||
this.changeTab('commercialProperty');
|
|
||||||
criteria = result as CommercialPropertyListingCriteria;
|
|
||||||
} else {
|
|
||||||
this.changeTab('broker');
|
|
||||||
criteria = result as UserListingCriteria;
|
|
||||||
}
|
|
||||||
const city = criteria.city as string;
|
|
||||||
if (city && city.length > 0) {
|
|
||||||
let results = await lastValueFrom(this.geoService.findCitiesStartingWith(city, criteria.state));
|
|
||||||
if (results.length > 0) {
|
|
||||||
criteria.city = results[0];
|
|
||||||
} else {
|
|
||||||
criteria.city = null;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
if (criteria.radius && criteria.radius.length > 0) {
|
});
|
||||||
criteria.radius = parseInt(criteria.radius);
|
|
||||||
}
|
|
||||||
this.loadingAi = false;
|
|
||||||
this.criteria = assignProperties(this.criteria, criteria);
|
|
||||||
this.search();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
this.aiSearchFailed = true;
|
|
||||||
this.loadingAi = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
Binary file not shown.
Loading…
Reference in New Issue