Compare commits
2 Commits
047c723364
...
timo
| Author | SHA1 | Date | |
|---|---|---|---|
| 2aeebb8d39 | |||
| 27aebcab38 |
4
.gitattributes
vendored
@@ -1,4 +0,0 @@
|
|||||||
* text=auto eol=lf
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.jpeg binary
|
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"groq-sdk": "^0.5.0",
|
"groq-sdk": "^0.5.0",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"helmet": "^8.1.0",
|
|
||||||
"nest-winston": "^1.9.4",
|
"nest-winston": "^1.9.4",
|
||||||
"nestjs-cls": "^5.4.0",
|
"nestjs-cls": "^5.4.0",
|
||||||
"nodemailer": "^7.0.12",
|
"nodemailer": "^7.0.12",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { LoggerService } from '@nestjs/common';
|
import { LoggerService } from '@nestjs/common';
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import helmet from 'helmet';
|
|
||||||
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ async function bootstrap() {
|
|||||||
app.useLogger(logger);
|
app.useLogger(logger);
|
||||||
//app.use('/bizmatch/payment/webhook', bodyParser.raw({ type: 'application/json' }));
|
//app.use('/bizmatch/payment/webhook', bodyParser.raw({ type: 'application/json' }));
|
||||||
// Serve static files from pictures directory
|
// Serve static files from pictures directory
|
||||||
// app.use('/pictures', express.static('pictures'));
|
app.use('/pictures', express.static('pictures'));
|
||||||
|
|
||||||
app.setGlobalPrefix('bizmatch');
|
app.setGlobalPrefix('bizmatch');
|
||||||
|
|
||||||
@@ -23,37 +22,6 @@ async function bootstrap() {
|
|||||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||||
allowedHeaders: 'Content-Type, Accept, Authorization, x-hide-loading',
|
allowedHeaders: 'Content-Type, Accept, Authorization, x-hide-loading',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Security Headers with helmet
|
|
||||||
app.use(
|
|
||||||
helmet({
|
|
||||||
contentSecurityPolicy: {
|
|
||||||
directives: {
|
|
||||||
defaultSrc: ["'self'"],
|
|
||||||
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://fonts.googleapis.com"],
|
|
||||||
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
|
||||||
imgSrc: ["'self'", "data:", "https:", "blob:"],
|
|
||||||
connectSrc: ["'self'", "https://api.bizmatch.net", "https://*.firebaseapp.com", "https://*.googleapis.com"],
|
|
||||||
fontSrc: ["'self'", "https://fonts.gstatic.com", "data:"],
|
|
||||||
objectSrc: ["'none'"],
|
|
||||||
mediaSrc: ["'self'"],
|
|
||||||
frameSrc: ["'self'"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
crossOriginEmbedderPolicy: false, // Disable for now to avoid breaking existing functionality
|
|
||||||
hsts: {
|
|
||||||
maxAge: 31536000,
|
|
||||||
includeSubDomains: true,
|
|
||||||
preload: true,
|
|
||||||
},
|
|
||||||
frameguard: {
|
|
||||||
action: 'sameorigin', // Allow same-origin framing
|
|
||||||
},
|
|
||||||
crossOriginOpenerPolicy: { policy: 'same-origin-allow-popups' }, // Allow popups for OAuth
|
|
||||||
crossOriginResourcePolicy: { policy: 'cross-origin' }, // Allow cross-origin resources
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await app.listen(process.env.PORT || 3001);
|
await app.listen(process.env.PORT || 3001);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
"outputPath": "dist/bizmatch",
|
"outputPath": "dist/bizmatch",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"browser": "src/main.ts",
|
"browser": "src/main.ts",
|
||||||
|
"server": "src/main.server.ts",
|
||||||
|
"prerender": false,
|
||||||
|
"ssr": {
|
||||||
|
"entry": "server.ts"
|
||||||
|
},
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"quill-delta",
|
"quill-delta",
|
||||||
"leaflet",
|
"leaflet",
|
||||||
@@ -48,7 +53,10 @@
|
|||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss",
|
"src/styles.scss",
|
||||||
"src/styles/lazy-load.css"
|
"src/styles/lazy-load.css",
|
||||||
|
"node_modules/quill/dist/quill.snow.css",
|
||||||
|
"node_modules/leaflet/dist/leaflet.css",
|
||||||
|
"node_modules/ngx-sharebuttons/themes/default.scss"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
@@ -61,8 +69,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
"maximumWarning": "10kb",
|
"maximumWarning": "2kb",
|
||||||
"maximumError": "30kb"
|
"maximumError": "4kb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputHashing": "all"
|
"outputHashing": "all"
|
||||||
@@ -70,7 +78,8 @@
|
|||||||
"development": {
|
"development": {
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
"sourceMap": true
|
"sourceMap": true,
|
||||||
|
"ssr": false
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
google-site-verification: googleccd5315437d68a49.html
|
|
||||||
@@ -26,74 +26,6 @@ export async function app(): Promise<express.Express> {
|
|||||||
server.set('view engine', 'html');
|
server.set('view engine', 'html');
|
||||||
server.set('views', browserDistFolder);
|
server.set('views', browserDistFolder);
|
||||||
|
|
||||||
// Sitemap XML endpoints - MUST be before static files middleware
|
|
||||||
server.get('/sitemap.xml', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const sitemapIndexXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<sitemap>
|
|
||||||
<loc>https://www.bizmatch.net/sitemap-static.xml</loc>
|
|
||||||
<lastmod>${new Date().toISOString().split('T')[0]}</lastmod>
|
|
||||||
</sitemap>
|
|
||||||
</sitemapindex>`;
|
|
||||||
|
|
||||||
res.header('Content-Type', 'application/xml; charset=utf-8');
|
|
||||||
res.send(sitemapIndexXml);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[SSR] Error generating sitemap index:', error);
|
|
||||||
res.status(500).send('Error generating sitemap');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.get('/sitemap-static.xml', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/</loc>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>1.0</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/home</loc>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>1.0</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/businessListings</loc>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/commercialPropertyListings</loc>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/brokerListings</loc>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/terms-of-use</loc>
|
|
||||||
<changefreq>monthly</changefreq>
|
|
||||||
<priority>0.5</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.bizmatch.net/privacy-statement</loc>
|
|
||||||
<changefreq>monthly</changefreq>
|
|
||||||
<priority>0.5</priority>
|
|
||||||
</url>
|
|
||||||
</urlset>`;
|
|
||||||
|
|
||||||
res.header('Content-Type', 'application/xml; charset=utf-8');
|
|
||||||
res.send(sitemapXml);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[SSR] Error generating static sitemap:', error);
|
|
||||||
res.status(500).send('Error generating sitemap');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Example Express Rest API endpoints
|
// Example Express Rest API endpoints
|
||||||
// server.get('/api/**', (req, res) => { });
|
// server.get('/api/**', (req, res) => { });
|
||||||
// Serve static files from /browser
|
// Serve static files from /browser
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@a
|
|||||||
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
|
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
|
||||||
import { getAuth, provideAuth } from '@angular/fire/auth';
|
import { getAuth, provideAuth } from '@angular/fire/auth';
|
||||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
|
import { GALLERY_CONFIG, GalleryConfig } from 'ng-gallery';
|
||||||
|
import { provideQuillConfig } from 'ngx-quill';
|
||||||
|
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
|
||||||
|
import { shareIcons } from 'ngx-sharebuttons/icons';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { AuthInterceptor } from './interceptors/auth.interceptor';
|
import { AuthInterceptor } from './interceptors/auth.interceptor';
|
||||||
@@ -44,6 +48,13 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provide: 'TIMEOUT_DURATION',
|
provide: 'TIMEOUT_DURATION',
|
||||||
useValue: 5000, // Standard-Timeout von 5 Sekunden
|
useValue: 5000, // Standard-Timeout von 5 Sekunden
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: GALLERY_CONFIG,
|
||||||
|
useValue: {
|
||||||
|
autoHeight: true,
|
||||||
|
imageSize: 'cover',
|
||||||
|
} as GalleryConfig,
|
||||||
|
},
|
||||||
{ provide: ErrorHandler, useClass: GlobalErrorHandler }, // Registriere den globalen ErrorHandler
|
{ provide: ErrorHandler, useClass: GlobalErrorHandler }, // Registriere den globalen ErrorHandler
|
||||||
{
|
{
|
||||||
provide: IMAGE_CONFIG,
|
provide: IMAGE_CONFIG,
|
||||||
@@ -51,6 +62,13 @@ export const appConfig: ApplicationConfig = {
|
|||||||
disableImageSizeWarning: true,
|
disableImageSizeWarning: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
provideShareButtonsOptions(
|
||||||
|
shareIcons(),
|
||||||
|
withConfig({
|
||||||
|
debug: true,
|
||||||
|
sharerMethod: SharerMethods.Anchor,
|
||||||
|
}),
|
||||||
|
),
|
||||||
provideRouter(
|
provideRouter(
|
||||||
routes,
|
routes,
|
||||||
withEnabledBlockingInitialNavigation(),
|
withEnabledBlockingInitialNavigation(),
|
||||||
@@ -61,6 +79,18 @@ export const appConfig: ApplicationConfig = {
|
|||||||
),
|
),
|
||||||
...(environment.production ? [POSTHOG_INIT_PROVIDER] : []),
|
...(environment.production ? [POSTHOG_INIT_PROVIDER] : []),
|
||||||
provideAnimations(),
|
provideAnimations(),
|
||||||
|
provideQuillConfig({
|
||||||
|
modules: {
|
||||||
|
syntax: true,
|
||||||
|
toolbar: [
|
||||||
|
['bold', 'italic', 'underline'], // Einige Standardoptionen
|
||||||
|
[{ header: [1, 2, 3, false] }], // Benutzerdefinierte Header
|
||||||
|
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||||
|
[{ color: [] }], // Dropdown mit Standardfarben
|
||||||
|
['clean'], // Entfernt Formatierungen
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
|
provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
|
||||||
provideAuth(() => getAuth()),
|
provideAuth(() => getAuth()),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,14 +11,19 @@ import { LoginRegisterComponent } from './components/login-register/login-regist
|
|||||||
import { AuthGuard } from './guards/auth.guard';
|
import { AuthGuard } from './guards/auth.guard';
|
||||||
import { ListingCategoryGuard } from './guards/listing-category.guard';
|
import { ListingCategoryGuard } from './guards/listing-category.guard';
|
||||||
|
|
||||||
// Public pages - HomeComponent stays eagerly loaded as landing page
|
// Public pages (eagerly loaded - high traffic)
|
||||||
|
import { DetailsBusinessListingComponent } from './pages/details/details-business-listing/details-business-listing.component';
|
||||||
|
import { DetailsCommercialPropertyListingComponent } from './pages/details/details-commercial-property-listing/details-commercial-property-listing.component';
|
||||||
|
import { DetailsUserComponent } from './pages/details/details-user/details-user.component';
|
||||||
import { HomeComponent } from './pages/home/home.component';
|
import { HomeComponent } from './pages/home/home.component';
|
||||||
|
import { BrokerListingsComponent } from './pages/listings/broker-listings/broker-listings.component';
|
||||||
|
import { BusinessListingsComponent } from './pages/listings/business-listings/business-listings.component';
|
||||||
|
import { CommercialPropertyListingsComponent } from './pages/listings/commercial-property-listings/commercial-property-listings.component';
|
||||||
import { SuccessComponent } from './pages/success/success.component';
|
import { SuccessComponent } from './pages/success/success.component';
|
||||||
import { TermsOfUseComponent } from './pages/legal/terms-of-use.component';
|
import { TermsOfUseComponent } from './pages/legal/terms-of-use.component';
|
||||||
import { PrivacyStatementComponent } from './pages/legal/privacy-statement.component';
|
import { PrivacyStatementComponent } from './pages/legal/privacy-statement.component';
|
||||||
|
|
||||||
// Note: All listing and details components are now lazy-loaded for better initial bundle size
|
// Note: Account, Edit, Admin, Favorites, MyListing, and EmailUs components are now lazy-loaded below
|
||||||
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -27,17 +32,17 @@ export const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'businessListings',
|
path: 'businessListings',
|
||||||
loadComponent: () => import('./pages/listings/business-listings/business-listings.component').then(m => m.BusinessListingsComponent),
|
component: BusinessListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'commercialPropertyListings',
|
path: 'commercialPropertyListings',
|
||||||
loadComponent: () => import('./pages/listings/commercial-property-listings/commercial-property-listings.component').then(m => m.CommercialPropertyListingsComponent),
|
component: CommercialPropertyListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'brokerListings',
|
path: 'brokerListings',
|
||||||
loadComponent: () => import('./pages/listings/broker-listings/broker-listings.component').then(m => m.BrokerListingsComponent),
|
component: BrokerListingsComponent,
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -48,11 +53,11 @@ export const routes: Routes = [
|
|||||||
// Listings Details - New SEO-friendly slug-based URLs
|
// Listings Details - New SEO-friendly slug-based URLs
|
||||||
{
|
{
|
||||||
path: 'business/:slug',
|
path: 'business/:slug',
|
||||||
loadComponent: () => import('./pages/details/details-business-listing/details-business-listing.component').then(m => m.DetailsBusinessListingComponent),
|
component: DetailsBusinessListingComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'commercial-property/:slug',
|
path: 'commercial-property/:slug',
|
||||||
loadComponent: () => import('./pages/details/details-commercial-property-listing/details-commercial-property-listing.component').then(m => m.DetailsCommercialPropertyListingComponent),
|
component: DetailsCommercialPropertyListingComponent,
|
||||||
},
|
},
|
||||||
// Backward compatibility redirects for old UUID-based URLs
|
// Backward compatibility redirects for old UUID-based URLs
|
||||||
{
|
{
|
||||||
@@ -90,7 +95,7 @@ export const routes: Routes = [
|
|||||||
// User Details
|
// User Details
|
||||||
{
|
{
|
||||||
path: 'details-user/:id',
|
path: 'details-user/:id',
|
||||||
loadComponent: () => import('./pages/details/details-user/details-user.component').then(m => m.DetailsUserComponent),
|
component: DetailsUserComponent,
|
||||||
},
|
},
|
||||||
// #########
|
// #########
|
||||||
// User edit (lazy-loaded)
|
// User edit (lazy-loaded)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { ConfirmationService } from './confirmation.service';
|
|||||||
<button
|
<button
|
||||||
(click)="confirmationService.reject()"
|
(click)="confirmationService.reject()"
|
||||||
type="button"
|
type="button"
|
||||||
class="absolute top-3 end-2.5 text-gray-600 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
class="absolute top-3 end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
>
|
>
|
||||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||||
@@ -21,11 +21,11 @@ import { ConfirmationService } from './confirmation.service';
|
|||||||
<span class="sr-only">Close modal</span>
|
<span class="sr-only">Close modal</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="p-4 md:p-5 text-center">
|
<div class="p-4 md:p-5 text-center">
|
||||||
<svg class="mx-auto mb-4 text-gray-600 w-12 h-12 dark:text-gray-200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
<svg class="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||||
</svg>
|
</svg>
|
||||||
@let confirmation = (confirmationService.confirmation$ | async);
|
@let confirmation = (confirmationService.confirmation$ | async);
|
||||||
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-600">{{ confirmation?.message }}</h3>
|
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">{{ confirmation?.message }}</h3>
|
||||||
@if(confirmation?.buttons==='both'){
|
@if(confirmation?.buttons==='both'){
|
||||||
<button
|
<button
|
||||||
(click)="confirmationService.accept()"
|
(click)="confirmationService.accept()"
|
||||||
@@ -37,7 +37,7 @@ import { ConfirmationService } from './confirmation.service';
|
|||||||
<button
|
<button
|
||||||
(click)="confirmationService.reject()"
|
(click)="confirmationService.reject()"
|
||||||
type="button"
|
type="button"
|
||||||
class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-600 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
||||||
>
|
>
|
||||||
No, cancel
|
No, cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<button
|
<button
|
||||||
(click)="eMailService.reject()"
|
(click)="eMailService.reject()"
|
||||||
type="button"
|
type="button"
|
||||||
class="end-2.5 text-gray-600 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
>
|
>
|
||||||
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
<div class="flex flex-col lg:flex-row items-center mb-4 lg:mb-0">
|
||||||
<!-- <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" alt="BizMatch Logo" 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 routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||||
<img src="/assets/images/header-logo.png" class="h-8 mb-2 lg:mb-0 lg:mr-4" alt="BizMatch Logo" width="120" height="32" />
|
<img src="/assets/images/header-logo.png" class="h-8 mb-2 lg:mb-0 lg:mr-4" alt="BizMatch Logo" />
|
||||||
</a>
|
</a>
|
||||||
<p class="text-sm text-neutral-600 text-center lg:text-left">© {{ currentYear }} Bizmatch All rights reserved.</p>
|
<p class="text-sm text-neutral-600 text-center lg:text-left">© {{ currentYear }} Bizmatch All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4 lg:mb-0 flex flex-col items-center lg:items-end">
|
<div class="mb-4 lg:mb-0 flex flex-col items-center lg:items-end">
|
||||||
<a href="tel:+1-800-840-6025" class="text-sm text-neutral-600 mb-1 lg:mb-2 hover:text-primary-600 w-full"> <i
|
<a class="text-sm text-neutral-600 mb-1 lg:mb-2 hover:text-primary-600 w-full"> <i
|
||||||
class="fas fa-phone-alt mr-2"></i>1-800-840-6025 </a>
|
class="fas fa-phone-alt mr-2"></i>1-800-840-6025 </a>
|
||||||
<a href="mailto:info@bizmatch.net" class="text-sm text-neutral-600 hover:text-primary-600"> <i
|
<a class="text-sm text-neutral-600 hover:text-primary-600"> <i
|
||||||
class="fas fa-envelope mr-2"></i>info@bizmatch.net </a>
|
class="fas fa-envelope mr-2"></i>info@bizmatch.net </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
<div class="max-w-screen-xl 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-10 w-auto"
|
<img src="/assets/images/header-logo.png" class="h-10 w-auto"
|
||||||
alt="BizMatch - Business Marketplace for Buying and Selling Businesses" width="150" height="40" />
|
alt="BizMatch - Business Marketplace for Buying and Selling Businesses" />
|
||||||
</a>
|
</a>
|
||||||
<div class="flex items-center md:order-2 space-x-3 rtl:space-x-reverse">
|
<div class="flex items-center md:order-2 space-x-3 rtl:space-x-reverse">
|
||||||
<!-- Filter button -->
|
<!-- Filter button -->
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
class="inline-flex items-center py-2 px-3 rounded hover:bg-neutral-100 md:hover:bg-transparent md:hover:text-primary-600 md:p-0 dark:text-white md:dark:hover:text-primary-500 dark:hover:bg-neutral-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-neutral-700"
|
class="inline-flex items-center py-2 px-3 rounded hover:bg-neutral-100 md:hover:bg-transparent md:hover:text-primary-600 md:p-0 dark:text-white md:dark:hover:text-primary-500 dark:hover:bg-neutral-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-neutral-700"
|
||||||
(click)="closeMenusAndSetCriteria('brokerListings')">
|
(click)="closeMenusAndSetCriteria('brokerListings')">
|
||||||
<img src="/assets/images/icon_professionals.png" alt="Professionals"
|
<img src="/assets/images/icon_professionals.png" alt="Professionals"
|
||||||
class="w-5 h-5 mr-2 object-contain bg-transparent" style="mix-blend-mode: darken;" width="20" height="20" />
|
class="w-5 h-5 mr-2 object-contain bg-transparent" style="mix-blend-mode: darken;" />
|
||||||
<span>Professionals</span>
|
<span>Professionals</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CommonModule, isPlatformBrowser } from '@angular/common';
|
|||||||
import { Component, HostListener, OnDestroy, OnInit, AfterViewInit, PLATFORM_ID, inject } from '@angular/core';
|
import { Component, HostListener, OnDestroy, OnInit, AfterViewInit, PLATFORM_ID, inject } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { NavigationEnd, Router, RouterModule } from '@angular/router';
|
import { NavigationEnd, Router, RouterModule } from '@angular/router';
|
||||||
import { APP_ICONS } from '../../utils/fontawesome-icons';
|
import { faUserGear } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||||
import { Collapse, Dropdown, initFlowbite } from 'flowbite';
|
import { Collapse, Dropdown, initFlowbite } from 'flowbite';
|
||||||
import { filter, Observable, Subject, takeUntil } from 'rxjs';
|
import { filter, Observable, Subject, takeUntil } from 'rxjs';
|
||||||
@@ -35,7 +35,7 @@ export class HeaderComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
keycloakUser: KeycloakUser;
|
keycloakUser: KeycloakUser;
|
||||||
user: User;
|
user: User;
|
||||||
activeItem;
|
activeItem;
|
||||||
faUserGear = APP_ICONS.faUserGear;
|
faUserGear = faUserGear;
|
||||||
profileUrl: string;
|
profileUrl: string;
|
||||||
env = environment;
|
env = environment;
|
||||||
private filterDropdown: Dropdown | null = null;
|
private filterDropdown: Dropdown | null = null;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
placeholder="Please enter E-Mail Address"
|
placeholder="Please enter E-Mail Address"
|
||||||
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
<fa-icon [icon]="envelope" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-600"></fa-icon>
|
<fa-icon [icon]="envelope" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
placeholder="Please enter Password"
|
placeholder="Please enter Password"
|
||||||
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-600"></fa-icon>
|
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
placeholder="Repeat Password"
|
placeholder="Repeat Password"
|
||||||
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-600"></fa-icon>
|
<fa-icon [icon]="lock" class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Component } from '@angular/core';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||||
import { APP_ICONS } from '../../utils/fontawesome-icons';
|
import { faArrowRight, faEnvelope, faLock, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { AuthService } from '../../services/auth.service';
|
||||||
import { LoadingService } from '../../services/loading.service';
|
import { LoadingService } from '../../services/loading.service';
|
||||||
@Component({
|
@Component({
|
||||||
@@ -18,10 +18,10 @@ export class LoginRegisterComponent {
|
|||||||
confirmPassword: string = '';
|
confirmPassword: string = '';
|
||||||
isLoginMode: boolean = true; // true: Login, false: Registration
|
isLoginMode: boolean = true; // true: Login, false: Registration
|
||||||
errorMessage: string = '';
|
errorMessage: string = '';
|
||||||
envelope = APP_ICONS.faEnvelope;
|
envelope = faEnvelope;
|
||||||
lock = APP_ICONS.faLock;
|
lock = faLock;
|
||||||
arrowRight = APP_ICONS.faArrowRight;
|
arrowRight = faArrowRight;
|
||||||
userplus = APP_ICONS.faUserPlus;
|
userplus = faUserPlus;
|
||||||
constructor(private authService: AuthService, private route: ActivatedRoute, private router: Router, private loadingService: LoadingService) {}
|
constructor(private authService: AuthService, private route: ActivatedRoute, private router: Router, private loadingService: LoadingService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" aria-label="Location - State" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" aria-label="Location - State" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" aria-label="Location - State" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" aria-label="Location - State" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
|
|||||||
@@ -61,8 +61,8 @@
|
|||||||
<div class="grid grid-cols-1 gap-6">
|
<div class="grid grid-cols-1 gap-6">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state-select" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state" aria-label="Location - State"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-neutral-900">Location - State</label>
|
||||||
<ng-select id="state-select" aria-label="Location - State" class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-neutral-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
|
|||||||
@@ -31,27 +31,13 @@ import dayjs from 'dayjs';
|
|||||||
import { AuthService } from '../../../services/auth.service';
|
import { AuthService } from '../../../services/auth.service';
|
||||||
import { BaseDetailsComponent } from '../base-details.component';
|
import { BaseDetailsComponent } from '../base-details.component';
|
||||||
import { ShareButton } from 'ngx-sharebuttons/button';
|
import { ShareButton } from 'ngx-sharebuttons/button';
|
||||||
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
|
|
||||||
import { shareIcons } from 'ngx-sharebuttons/icons';
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-details-business-listing',
|
selector: 'app-details-business-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, LeafletModule, BreadcrumbsComponent, ShareButton, NgOptimizedImage],
|
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, LeafletModule, BreadcrumbsComponent, ShareButton, NgOptimizedImage],
|
||||||
providers: [
|
providers: [],
|
||||||
provideShareButtonsOptions(
|
|
||||||
shareIcons(),
|
|
||||||
withConfig({
|
|
||||||
debug: false,
|
|
||||||
sharerMethod: SharerMethods.Anchor,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
templateUrl: './details-business-listing.component.html',
|
templateUrl: './details-business-listing.component.html',
|
||||||
styleUrls: [
|
styleUrl: '../details.scss',
|
||||||
'../details.scss',
|
|
||||||
'../../../../../node_modules/leaflet/dist/leaflet.css',
|
|
||||||
'../../../../../node_modules/ngx-sharebuttons/themes/default.scss'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class DetailsBusinessListingComponent extends BaseDetailsComponent {
|
export class DetailsBusinessListingComponent extends BaseDetailsComponent {
|
||||||
// listings: Array<BusinessListing>;
|
// listings: Array<BusinessListing>;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { NgOptimizedImage } from '@angular/common';
|
|||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { LeafletModule } from '@bluehalo/ngx-leaflet';
|
import { LeafletModule } from '@bluehalo/ngx-leaflet';
|
||||||
import { APP_ICONS } from '../../../utils/fontawesome-icons';
|
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { GALLERY_CONFIG, GalleryConfig, GalleryModule, ImageItem } from 'ng-gallery';
|
import { GalleryModule, ImageItem } from 'ng-gallery';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { CommercialPropertyListing, EventTypeEnum, ShareByEMail, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { CommercialPropertyListing, EventTypeEnum, ShareByEMail, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { CommercialPropertyListingCriteria, ErrorResponse, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { CommercialPropertyListingCriteria, ErrorResponse, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
@@ -30,35 +30,14 @@ import { createMailInfo, map2User } from '../../../utils/utils';
|
|||||||
import { BaseDetailsComponent } from '../base-details.component';
|
import { BaseDetailsComponent } from '../base-details.component';
|
||||||
import { BreadcrumbItem, BreadcrumbsComponent } from '../../../components/breadcrumbs/breadcrumbs.component';
|
import { BreadcrumbItem, BreadcrumbsComponent } from '../../../components/breadcrumbs/breadcrumbs.component';
|
||||||
import { ShareButton } from 'ngx-sharebuttons/button';
|
import { ShareButton } from 'ngx-sharebuttons/button';
|
||||||
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
|
|
||||||
import { shareIcons } from 'ngx-sharebuttons/icons';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-details-commercial-property-listing',
|
selector: 'app-details-commercial-property-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, GalleryModule, LeafletModule, BreadcrumbsComponent, ShareButton, NgOptimizedImage],
|
imports: [SharedModule, ValidatedInputComponent, ValidatedTextareaComponent, ValidatedNgSelectComponent, GalleryModule, LeafletModule, BreadcrumbsComponent, ShareButton, NgOptimizedImage],
|
||||||
providers: [
|
providers: [],
|
||||||
provideShareButtonsOptions(
|
|
||||||
shareIcons(),
|
|
||||||
withConfig({
|
|
||||||
debug: false,
|
|
||||||
sharerMethod: SharerMethods.Anchor,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
{
|
|
||||||
provide: GALLERY_CONFIG,
|
|
||||||
useValue: {
|
|
||||||
autoHeight: true,
|
|
||||||
imageSize: 'cover',
|
|
||||||
} as GalleryConfig,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
templateUrl: './details-commercial-property-listing.component.html',
|
templateUrl: './details-commercial-property-listing.component.html',
|
||||||
styleUrls: [
|
styleUrl: '../details.scss',
|
||||||
'../details.scss',
|
|
||||||
'../../../../../node_modules/leaflet/dist/leaflet.css',
|
|
||||||
'../../../../../node_modules/ngx-sharebuttons/themes/default.scss'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class DetailsCommercialPropertyListingComponent extends BaseDetailsComponent {
|
export class DetailsCommercialPropertyListingComponent extends BaseDetailsComponent {
|
||||||
responsiveOptions = [
|
responsiveOptions = [
|
||||||
@@ -90,7 +69,7 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon
|
|||||||
ts = new Date().getTime();
|
ts = new Date().getTime();
|
||||||
env = environment;
|
env = environment;
|
||||||
errorResponse: ErrorResponse;
|
errorResponse: ErrorResponse;
|
||||||
faTimes = APP_ICONS.faTimes;
|
faTimes = faTimes;
|
||||||
propertyDetails = [];
|
propertyDetails = [];
|
||||||
images: Array<ImageItem> = [];
|
images: Array<ImageItem> = [];
|
||||||
relatedListings: CommercialPropertyListing[] = [];
|
relatedListings: CommercialPropertyListing[] = [];
|
||||||
|
|||||||
@@ -19,27 +19,13 @@ import { UserService } from '../../../services/user.service';
|
|||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
import { formatPhoneNumber, map2User } from '../../../utils/utils';
|
import { formatPhoneNumber, map2User } from '../../../utils/utils';
|
||||||
import { ShareButton } from 'ngx-sharebuttons/button';
|
import { ShareButton } from 'ngx-sharebuttons/button';
|
||||||
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
|
|
||||||
import { shareIcons } from 'ngx-sharebuttons/icons';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-details-user',
|
selector: 'app-details-user',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, BreadcrumbsComponent, NgOptimizedImage, ShareButton],
|
imports: [SharedModule, BreadcrumbsComponent, NgOptimizedImage, ShareButton],
|
||||||
providers: [
|
|
||||||
provideShareButtonsOptions(
|
|
||||||
shareIcons(),
|
|
||||||
withConfig({
|
|
||||||
debug: false,
|
|
||||||
sharerMethod: SharerMethods.Anchor,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
templateUrl: './details-user.component.html',
|
templateUrl: './details-user.component.html',
|
||||||
styleUrls: [
|
styleUrl: '../details.scss',
|
||||||
'../details.scss',
|
|
||||||
'../../../../../node_modules/ngx-sharebuttons/themes/default.scss'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class DetailsUserComponent {
|
export class DetailsUserComponent {
|
||||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<header class="w-full flex justify-between items-center p-4 bg-white top-0 z-10 h-16 md:h-20">
|
<header class="w-full flex justify-between items-center p-4 bg-white top-0 z-10 h-16 md:h-20">
|
||||||
<img src="/assets/images/header-logo.png" alt="BizMatch - Business & Property Marketplace" class="h-8 md:h-10 w-auto" width="120" height="40" />
|
<img src="/assets/images/header-logo.png" alt="Logo" class="h-8 md:h-10 w-auto" />
|
||||||
<div class="hidden md:flex items-center space-x-4">
|
<div class="hidden md:flex items-center space-x-4">
|
||||||
@if(user){
|
@if(user){
|
||||||
<a routerLink="/account" class="text-primary-600 border border-primary-600 px-3 py-2 rounded">Account</a>
|
<a routerLink="/account" class="text-primary-600 border border-primary-600 px-3 py-2 rounded">Account</a>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
(click)="toggleMenu()"
|
(click)="toggleMenu()"
|
||||||
class="md:hidden text-neutral-700 p-2 min-w-[44px] min-h-[44px] flex items-center justify-center"
|
class="md:hidden text-neutral-600"
|
||||||
aria-label="Open navigation menu"
|
aria-label="Open navigation menu"
|
||||||
[attr.aria-expanded]="isMenuOpen"
|
[attr.aria-expanded]="isMenuOpen"
|
||||||
>
|
>
|
||||||
@@ -88,8 +88,8 @@
|
|||||||
<!-- Restliche Anpassungen (Innenabstände, Button-Paddings etc.) bleiben wie im vorherigen Schritt -->
|
<!-- Restliche Anpassungen (Innenabstände, Button-Paddings etc.) bleiben wie im vorherigen Schritt -->
|
||||||
<div class="search-form-container 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 class="search-form-container 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 }">
|
||||||
@if(!aiSearch){
|
@if(!aiSearch){
|
||||||
<div class="text-sm lg:text-base mb-1 text-center text-neutral-700 border-neutral-200 dark:text-neutral-300 dark:border-neutral-700 flex justify-between">
|
<div class="text-sm lg:text-base mb-1 text-center text-neutral-500 border-neutral-200 dark:text-neutral-400 dark:border-neutral-700 flex justify-between">
|
||||||
<ul class="flex flex-wrap -mb-px w-full" role="tablist" aria-label="Search categories">
|
<ul class="flex flex-wrap -mb-px w-full" role="tablist">
|
||||||
<li class="w-[33%]" role="presentation">
|
<li class="w-[33%]" role="presentation">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -99,11 +99,9 @@
|
|||||||
[ngClass]="
|
[ngClass]="
|
||||||
activeTabAction === 'business'
|
activeTabAction === 'business'
|
||||||
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
||||||
: ['border-transparent', 'text-neutral-700', 'hover:text-neutral-900', 'hover:border-neutral-400', 'dark:hover:text-neutral-300']
|
: ['border-transparent', 'hover:text-neutral-600', 'hover:border-neutral-300', 'dark:hover:text-neutral-300']
|
||||||
"
|
"
|
||||||
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent min-h-[44px]"
|
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent"
|
||||||
[attr.aria-controls]="'tabpanel-search'"
|
|
||||||
id="tab-business"
|
|
||||||
>
|
>
|
||||||
<img src="/assets/images/business_logo.png" alt="" aria-hidden="true" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" width="28" height="28" />
|
<img src="/assets/images/business_logo.png" alt="" aria-hidden="true" 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>
|
<span>Businesses</span>
|
||||||
@@ -119,11 +117,9 @@
|
|||||||
[ngClass]="
|
[ngClass]="
|
||||||
activeTabAction === 'commercialProperty'
|
activeTabAction === 'commercialProperty'
|
||||||
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
||||||
: ['border-transparent', 'text-neutral-700', 'hover:text-neutral-900', 'hover:border-neutral-400', 'dark:hover:text-neutral-300']
|
: ['border-transparent', 'hover:text-neutral-600', 'hover:border-neutral-300', 'dark:hover:text-neutral-300']
|
||||||
"
|
"
|
||||||
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent min-h-[44px]"
|
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent"
|
||||||
[attr.aria-controls]="'tabpanel-search'"
|
|
||||||
id="tab-properties"
|
|
||||||
>
|
>
|
||||||
<img src="/assets/images/properties_logo.png" alt="" aria-hidden="true" class="tab-icon w-6 h-6 md:w-7 md:h-7 mr-1 md:mr-2 object-contain" width="28" height="28" />
|
<img src="/assets/images/properties_logo.png" alt="" aria-hidden="true" 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>
|
<span>Properties</span>
|
||||||
@@ -139,11 +135,9 @@
|
|||||||
[ngClass]="
|
[ngClass]="
|
||||||
activeTabAction === 'broker'
|
activeTabAction === 'broker'
|
||||||
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
? ['text-primary-600', 'border-primary-600', 'active', 'dark:text-primary-500', 'dark:border-primary-500']
|
||||||
: ['border-transparent', 'text-neutral-700', 'hover:text-neutral-900', 'hover:border-neutral-400', 'dark:hover:text-neutral-300']
|
: ['border-transparent', 'hover:text-neutral-600', 'hover:border-neutral-300', 'dark:hover:text-neutral-300']
|
||||||
"
|
"
|
||||||
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent min-h-[44px]"
|
class="tab-link w-full hover:cursor-pointer inline-flex items-center justify-center px-3 py-3 md:p-4 border-b-2 rounded-t-lg bg-transparent"
|
||||||
[attr.aria-controls]="'tabpanel-search'"
|
|
||||||
id="tab-professionals"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/assets/images/icon_professionals.png"
|
src="/assets/images/icon_professionals.png"
|
||||||
@@ -159,7 +153,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
} @if(criteria && !aiSearch){
|
} @if(criteria && !aiSearch){
|
||||||
<div id="tabpanel-search" role="tabpanel" aria-labelledby="tab-business" class="w-full max-w-3xl mx-auto bg-white rounded-lg flex flex-col md:flex-row md:border md:border-neutral-300">
|
<div class="w-full max-w-3xl mx-auto bg-white rounded-lg flex flex-col md:flex-row md:border md:border-neutral-300">
|
||||||
<div class="md:flex-none md:w-48 flex-1 md:border-r border-neutral-300 overflow-hidden mb-2 md:mb-0">
|
<div class="md:flex-none md:w-48 flex-1 md:border-r border-neutral-300 overflow-hidden mb-2 md:mb-0">
|
||||||
<div class="relative max-sm:border border-neutral-300 rounded-md">
|
<div class="relative max-sm:border border-neutral-300 rounded-md">
|
||||||
<label for="type-filter" class="sr-only">Filter by type</label>
|
<label for="type-filter" class="sr-only">Filter by type</label>
|
||||||
@@ -201,11 +195,7 @@
|
|||||||
groupBy="type"
|
groupBy="type"
|
||||||
labelForId="location-search"
|
labelForId="location-search"
|
||||||
aria-label="Search by city or state"
|
aria-label="Search by city or state"
|
||||||
[inputAttrs]="{'aria-describedby': 'location-search-hint'}"
|
|
||||||
>
|
>
|
||||||
<ng-template ng-footer-tmp>
|
|
||||||
<span id="location-search-hint" class="sr-only">Enter at least 2 characters to search for a city or state</span>
|
|
||||||
</ng-template>
|
|
||||||
@for (city of cities$ | async; track city.id) { @let state = city.type==='city'?city.content.state:''; @let separator = city.type==='city'?' - ':'';
|
@for (city of cities$ | async; track city.id) { @let state = city.type==='city'?city.content.state:''; @let separator = city.type==='city'?' - ':'';
|
||||||
<ng-option [value]="city">{{ city.content.name }}{{ separator }}{{ state }}</ng-option>
|
<ng-option [value]="city">{{ city.content.name }}{{ separator }}{{ state }}</ng-option>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
select:not([size]) {
|
select:not([size]) {
|
||||||
background-image: unset;
|
background-image: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
[type='text'],
|
[type='text'],
|
||||||
[type='email'],
|
[type='email'],
|
||||||
[type='url'],
|
[type='url'],
|
||||||
@@ -19,51 +19,39 @@ textarea,
|
|||||||
select {
|
select {
|
||||||
border: unset;
|
border: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-checkbox:checked {
|
.toggle-checkbox:checked {
|
||||||
right: 0;
|
right: 0;
|
||||||
border-color: rgb(125 211 252);
|
border-color: rgb(125 211 252);
|
||||||
}
|
}
|
||||||
|
.toggle-checkbox:checked + .toggle-label {
|
||||||
.toggle-checkbox:checked+.toggle-label {
|
|
||||||
background-color: rgb(125 211 252);
|
background-color: rgb(125 211 252);
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep .ng-select.ng-select-single .ng-select-container {
|
:host ::ng-deep .ng-select.ng-select-single .ng-select-container {
|
||||||
min-height: 52px;
|
min-height: 52px;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
.ng-value-container .ng-input {
|
.ng-value-container .ng-input {
|
||||||
top: 12px;
|
top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.ng-arrow-wrapper {
|
span.ng-arrow-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
color: #000;
|
color: #000; /* Standard-Textfarbe für das Dropdown */
|
||||||
/* Standard-Textfarbe für das Dropdown */
|
|
||||||
// background-color: #fff; /* Hintergrundfarbe für das Dropdown */
|
// background-color: #fff; /* Hintergrundfarbe für das Dropdown */
|
||||||
}
|
}
|
||||||
|
|
||||||
select option {
|
select option {
|
||||||
color: #000;
|
color: #000; /* Textfarbe für Dropdown-Optionen */
|
||||||
/* Textfarbe für Dropdown-Optionen */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select.placeholder-selected {
|
select.placeholder-selected {
|
||||||
color: #6b7280;
|
color: #999; /* Farbe für den Platzhalter */
|
||||||
/* gray-500 - besserer Kontrast für WCAG AA */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder {
|
input::placeholder {
|
||||||
color: #555;
|
color: #555; /* Dunkleres Grau */
|
||||||
/* Dunkleres Grau */
|
opacity: 1; /* Stellt sicher, dass die Deckkraft 100% ist */
|
||||||
opacity: 1;
|
|
||||||
/* Stellt sicher, dass die Deckkraft 100% ist */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stellt sicher, dass die Optionen im Dropdown immer schwarz sind */
|
/* Stellt sicher, dass die Optionen im Dropdown immer schwarz sind */
|
||||||
@@ -71,14 +59,10 @@ select:focus option,
|
|||||||
select:hover option {
|
select:hover option {
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'][name='aiSearchText'] {
|
input[type='text'][name='aiSearchText'] {
|
||||||
padding: 14px;
|
padding: 14px; /* Innerer Abstand */
|
||||||
/* Innerer Abstand */
|
font-size: 16px; /* Schriftgröße anpassen */
|
||||||
font-size: 16px;
|
box-sizing: border-box; /* Padding und Border in die Höhe und Breite einrechnen */
|
||||||
/* Schriftgröße anpassen */
|
|
||||||
box-sizing: border-box;
|
|
||||||
/* Padding und Border in die Höhe und Breite einrechnen */
|
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +145,6 @@ select,
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@@ -229,7 +212,6 @@ header {
|
|||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
&.text-blue-600.border.border-blue-600 {
|
&.text-blue-600.border.border-blue-600 {
|
||||||
|
|
||||||
// Log In button
|
// Log In button
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(37, 99, 235, 0.05);
|
background-color: rgba(37, 99, 235, 0.05);
|
||||||
@@ -242,7 +224,6 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.bg-blue-600 {
|
&.bg-blue-600 {
|
||||||
|
|
||||||
// Register button
|
// Register button
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgb(29, 78, 216);
|
background-color: rgb(29, 78, 216);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { NgSelectModule } from '@ng-select/ng-select';
|
|||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { catchError, concat, distinctUntilChanged, Observable, of, Subject, switchMap, tap } from 'rxjs';
|
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 { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingCriteria, GeoResult, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { FAQItem } from '../../components/faq/faq.component';
|
import { FaqComponent, FAQItem } from '../../components/faq/faq.component';
|
||||||
import { ModalService } from '../../components/search-modal/modal.service';
|
import { ModalService } from '../../components/search-modal/modal.service';
|
||||||
import { TooltipComponent } from '../../components/tooltip/tooltip.component';
|
import { TooltipComponent } from '../../components/tooltip/tooltip.component';
|
||||||
import { AiService } from '../../services/ai.service';
|
import { AiService } from '../../services/ai.service';
|
||||||
@@ -24,7 +24,7 @@ import { map2User } from '../../utils/utils';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, RouterModule, NgSelectModule],
|
imports: [CommonModule, FormsModule, RouterModule, NgSelectModule, FaqComponent],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.scss',
|
styleUrl: './home.component.scss',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@@ -126,7 +126,7 @@ export class HomeComponent {
|
|||||||
// Set SEO meta tags for home page
|
// Set SEO meta tags for home page
|
||||||
this.seoService.updateMetaTags({
|
this.seoService.updateMetaTags({
|
||||||
title: 'BizMatch - Buy & Sell Businesses and Commercial Properties',
|
title: 'BizMatch - Buy & Sell Businesses and Commercial Properties',
|
||||||
description: 'Buy and sell businesses, commercial properties, and franchises. Browse thousands of verified listings across the United States.',
|
description: 'Find profitable businesses for sale, commercial real estate, and franchise opportunities. Browse thousands of verified listings across the US.',
|
||||||
keywords: 'business for sale, businesses for sale, buy business, sell business, commercial property, commercial real estate, franchise opportunities, business broker, business marketplace',
|
keywords: 'business for sale, businesses for sale, buy business, sell business, commercial property, commercial real estate, franchise opportunities, business broker, business marketplace',
|
||||||
type: 'website'
|
type: 'website'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<i class="fas fa-arrow-left text-lg"></i>
|
<i class="fas fa-arrow-left text-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
<h1 class="text-3xl font-bold text-neutral-900 mb-6 pr-14">BizMatch Privacy Policy and Data Protection</h1>
|
<h1 class="text-3xl font-bold text-neutral-900 mb-6 pr-14">Privacy Statement</h1>
|
||||||
|
|
||||||
<section id="content" role="main">
|
<section id="content" role="main">
|
||||||
<article class="post page">
|
<article class="post page">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<i class="fas fa-arrow-left text-lg"></i>
|
<i class="fas fa-arrow-left text-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
<h1 class="text-3xl font-bold text-neutral-900 mb-6 pr-14">BizMatch Terms of Use and User Agreement</h1>
|
<h1 class="text-3xl font-bold text-neutral-900 mb-6 pr-14">Terms of Use</h1>
|
||||||
|
|
||||||
<section id="content" role="main">
|
<section id="content" role="main">
|
||||||
<article class="post page">
|
<article class="post page">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<!-- SEO-optimized heading -->
|
<!-- SEO-optimized heading -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h1 class="text-3xl md:text-4xl font-bold text-neutral-900 mb-2">Businesses for Sale - Find Your Next Business Opportunity</h1>
|
<h1 class="text-3xl md:text-4xl font-bold text-neutral-900 mb-2">Businesses for Sale</h1>
|
||||||
<p class="text-lg text-neutral-600">Discover profitable business opportunities across the United States. Browse
|
<p class="text-lg text-neutral-600">Discover profitable business opportunities across the United States. Browse
|
||||||
verified listings from business owners and brokers.</p>
|
verified listings from business owners and brokers.</p>
|
||||||
<div class="mt-4 text-base text-neutral-700 max-w-4xl">
|
<div class="mt-4 text-base text-neutral-700 max-w-4xl">
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ export class BusinessListingsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Set SEO meta tags for business listings page
|
// Set SEO meta tags for business listings page
|
||||||
this.seoService.updateMetaTags({
|
this.seoService.updateMetaTags({
|
||||||
title: 'Businesses for Sale | BizMatch',
|
title: 'Businesses for Sale - Profitable Opportunities | BizMatch',
|
||||||
description: 'Browse thousands of businesses for sale including restaurants, franchises, and retail stores. Verified listings nationwide.',
|
description: 'Browse thousands of businesses for sale. Find restaurants, franchises, retail stores, and more. Verified listings from owners and brokers.',
|
||||||
keywords: 'businesses for sale, buy a business, business opportunities, franchise for sale, restaurant for sale, retail business for sale, business broker listings',
|
keywords: 'businesses for sale, buy a business, business opportunities, franchise for sale, restaurant for sale, retail business for sale, business broker listings',
|
||||||
type: 'website'
|
type: 'website'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ export class CommercialPropertyListingsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Set SEO meta tags for commercial property listings page
|
// Set SEO meta tags for commercial property listings page
|
||||||
this.seoService.updateMetaTags({
|
this.seoService.updateMetaTags({
|
||||||
title: 'Commercial Properties for Sale | BizMatch',
|
title: 'Commercial Properties for Sale - Office, Retail | BizMatch',
|
||||||
description: 'Browse commercial real estate including offices, retail, warehouses, and industrial properties. Verified investment opportunities.',
|
description: 'Browse commercial real estate: office buildings, retail spaces, warehouses, and industrial properties. Verified investment opportunities.',
|
||||||
keywords: 'commercial property for sale, commercial real estate, office building for sale, retail space for sale, warehouse for sale, industrial property, investment property, commercial property listings',
|
keywords: 'commercial property for sale, commercial real estate, office building for sale, retail space for sale, warehouse for sale, industrial property, investment property, commercial property listings',
|
||||||
type: 'website'
|
type: 'website'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { DatePipe, TitleCasePipe } from '@angular/common';
|
import { DatePipe, TitleCasePipe } from '@angular/common';
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { APP_ICONS } from '../../../utils/fontawesome-icons';
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { NgSelectModule } from '@ng-select/ng-select';
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
import { QuillModule, provideQuillConfig } from 'ngx-quill';
|
import { QuillModule } from 'ngx-quill';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { AutoCompleteCompleteEvent, Invoice, UploadParams, ValidationMessage, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { AutoCompleteCompleteEvent, Invoice, UploadParams, ValidationMessage, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
@@ -45,27 +45,9 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
|||||||
ValidatedCountyComponent,
|
ValidatedCountyComponent,
|
||||||
ValidatedLocationComponent,
|
ValidatedLocationComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [TitleCasePipe, DatePipe],
|
||||||
TitleCasePipe,
|
|
||||||
DatePipe,
|
|
||||||
provideQuillConfig({
|
|
||||||
modules: {
|
|
||||||
syntax: true,
|
|
||||||
toolbar: [
|
|
||||||
['bold', 'italic', 'underline'],
|
|
||||||
[{ header: [1, 2, 3, false] }],
|
|
||||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
||||||
[{ color: [] }],
|
|
||||||
['clean'],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}) as any,
|
|
||||||
],
|
|
||||||
templateUrl: './account.component.html',
|
templateUrl: './account.component.html',
|
||||||
styleUrls: [
|
styleUrl: './account.component.scss',
|
||||||
'./account.component.scss',
|
|
||||||
'../../../../../node_modules/quill/dist/quill.snow.css'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class AccountComponent {
|
export class AccountComponent {
|
||||||
id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||||
@@ -76,7 +58,7 @@ export class AccountComponent {
|
|||||||
environment = environment;
|
environment = environment;
|
||||||
editorModules = TOOLBAR_OPTIONS;
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
env = environment;
|
env = environment;
|
||||||
faTrash = APP_ICONS.faTrash;
|
faTrash = faTrash;
|
||||||
quillModules = {
|
quillModules = {
|
||||||
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import { SelectOptionsService } from '../../../services/select-options.service';
|
|||||||
import { map2User, routeListingWithState } from '../../../utils/utils';
|
import { map2User, routeListingWithState } from '../../../utils/utils';
|
||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { APP_ICONS } from '../../../utils/fontawesome-icons';
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { QuillModule } from 'ngx-quill';
|
import { QuillModule } from 'ngx-quill';
|
||||||
|
|
||||||
import { NgSelectModule } from '@ng-select/ng-select';
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
import { NgxCurrencyDirective } from 'ngx-currency';
|
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||||
import { provideQuillConfig } from 'ngx-quill';
|
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, createDefaultBusinessListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { AutoCompleteCompleteEvent, ImageProperty, createDefaultBusinessListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
||||||
@@ -48,25 +47,9 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
|||||||
ValidatedTextareaComponent,
|
ValidatedTextareaComponent,
|
||||||
ValidatedLocationComponent,
|
ValidatedLocationComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [],
|
||||||
provideQuillConfig({
|
|
||||||
modules: {
|
|
||||||
syntax: true,
|
|
||||||
toolbar: [
|
|
||||||
['bold', 'italic', 'underline'],
|
|
||||||
[{ header: [1, 2, 3, false] }],
|
|
||||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
||||||
[{ color: [] }],
|
|
||||||
['clean'],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}) as any,
|
|
||||||
],
|
|
||||||
templateUrl: './edit-business-listing.component.html',
|
templateUrl: './edit-business-listing.component.html',
|
||||||
styleUrls: [
|
styleUrl: './edit-business-listing.component.scss',
|
||||||
'./edit-business-listing.component.scss',
|
|
||||||
'../../../../../node_modules/quill/dist/quill.snow.css'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class EditBusinessListingComponent {
|
export class EditBusinessListingComponent {
|
||||||
listingsCategory = 'business';
|
listingsCategory = 'business';
|
||||||
@@ -81,7 +64,7 @@ export class EditBusinessListingComponent {
|
|||||||
config = { aspectRatio: 16 / 9 };
|
config = { aspectRatio: 16 / 9 };
|
||||||
editorModules = TOOLBAR_OPTIONS;
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
draggedImage: ImageProperty;
|
draggedImage: ImageProperty;
|
||||||
faTrash = APP_ICONS.faTrash;
|
faTrash = faTrash;
|
||||||
data: CommercialPropertyListing;
|
data: CommercialPropertyListing;
|
||||||
typesOfBusiness = [];
|
typesOfBusiness = [];
|
||||||
quillModules = {
|
quillModules = {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
(click)="uploadPropertyPicture()"
|
(click)="uploadPropertyPicture()"
|
||||||
class="flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
class="flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<svg class="mr-2 h-5 w-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="mr-2 h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Upload
|
Upload
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { map2User, routeListingWithState } from '../../../utils/utils';
|
|||||||
|
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { ViewportRuler } from '@angular/cdk/scrolling';
|
import { ViewportRuler } from '@angular/cdk/scrolling';
|
||||||
import { APP_ICONS } from '../../../utils/fontawesome-icons';
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { NgSelectModule } from '@ng-select/ng-select';
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
import { NgxCurrencyDirective } from 'ngx-currency';
|
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||||
import { ImageCropperComponent } from 'ngx-image-cropper';
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
||||||
import { QuillModule, provideQuillConfig } from 'ngx-quill';
|
import { QuillModule } from 'ngx-quill';
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, UploadParams, createDefaultCommercialPropertyListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { AutoCompleteCompleteEvent, ImageProperty, UploadParams, createDefaultCommercialPropertyListing, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
||||||
@@ -53,25 +53,9 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
|||||||
ValidatedLocationComponent,
|
ValidatedLocationComponent,
|
||||||
ImageCropAndUploadComponent,
|
ImageCropAndUploadComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [],
|
||||||
provideQuillConfig({
|
|
||||||
modules: {
|
|
||||||
syntax: true,
|
|
||||||
toolbar: [
|
|
||||||
['bold', 'italic', 'underline'],
|
|
||||||
[{ header: [1, 2, 3, false] }],
|
|
||||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
||||||
[{ color: [] }],
|
|
||||||
['clean'],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}) as any,
|
|
||||||
],
|
|
||||||
templateUrl: './edit-commercial-property-listing.component.html',
|
templateUrl: './edit-commercial-property-listing.component.html',
|
||||||
styleUrls: [
|
styleUrl: './edit-commercial-property-listing.component.scss',
|
||||||
'./edit-commercial-property-listing.component.scss',
|
|
||||||
'../../../../../node_modules/quill/dist/quill.snow.css'
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class EditCommercialPropertyListingComponent {
|
export class EditCommercialPropertyListingComponent {
|
||||||
@ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;
|
@ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;
|
||||||
@@ -107,7 +91,7 @@ export class EditCommercialPropertyListingComponent {
|
|||||||
editorModules = TOOLBAR_OPTIONS;
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
|
|
||||||
draggedImage: ImageProperty;
|
draggedImage: ImageProperty;
|
||||||
faTrash = APP_ICONS.faTrash;
|
faTrash = faTrash;
|
||||||
suggestions: string[] | undefined;
|
suggestions: string[] | undefined;
|
||||||
data: BusinessListing;
|
data: BusinessListing;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, inject, PLATFORM_ID, Renderer2, RendererFactory2 } from '@angular/core';
|
import { Injectable, inject, PLATFORM_ID } from '@angular/core';
|
||||||
import { isPlatformBrowser, DOCUMENT } from '@angular/common';
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
import { Meta, Title } from '@angular/platform-browser';
|
import { Meta, Title } from '@angular/platform-browser';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@@ -22,17 +22,11 @@ export class SeoService {
|
|||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private platformId = inject(PLATFORM_ID);
|
private platformId = inject(PLATFORM_ID);
|
||||||
private isBrowser = isPlatformBrowser(this.platformId);
|
private isBrowser = isPlatformBrowser(this.platformId);
|
||||||
private document = inject(DOCUMENT);
|
|
||||||
private renderer: Renderer2;
|
|
||||||
|
|
||||||
private readonly defaultImage = 'https://www.bizmatch.net/assets/images/bizmatch-og-image.jpg';
|
private readonly defaultImage = 'https://www.bizmatch.net/assets/images/bizmatch-og-image.jpg';
|
||||||
private readonly siteName = 'BizMatch';
|
private readonly siteName = 'BizMatch';
|
||||||
private readonly baseUrl = 'https://www.bizmatch.net';
|
private readonly baseUrl = 'https://www.bizmatch.net';
|
||||||
|
|
||||||
constructor(rendererFactory: RendererFactory2) {
|
|
||||||
this.renderer = rendererFactory.createRenderer(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base URL for SEO purposes
|
* Get the base URL for SEO purposes
|
||||||
*/
|
*/
|
||||||
@@ -115,18 +109,20 @@ export class SeoService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update canonical URL (SSR-compatible using Renderer2)
|
* Update canonical URL
|
||||||
*/
|
*/
|
||||||
private updateCanonicalUrl(url: string): void {
|
private updateCanonicalUrl(url: string): void {
|
||||||
let link: HTMLLinkElement | null = this.document.querySelector('link[rel="canonical"]');
|
if (!this.isBrowser) return;
|
||||||
|
|
||||||
|
let link: HTMLLinkElement | null = document.querySelector('link[rel="canonical"]');
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
this.renderer.setAttribute(link, 'href', url);
|
link.setAttribute('href', url);
|
||||||
} else {
|
} else {
|
||||||
link = this.renderer.createElement('link');
|
link = document.createElement('link');
|
||||||
this.renderer.setAttribute(link, 'rel', 'canonical');
|
link.setAttribute('rel', 'canonical');
|
||||||
this.renderer.setAttribute(link, 'href', url);
|
link.setAttribute('href', url);
|
||||||
this.renderer.appendChild(this.document.head, link);
|
document.head.appendChild(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,40 +269,32 @@ export class SeoService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject JSON-LD structured data into page (SSR-compatible using Renderer2)
|
* Inject JSON-LD structured data into page
|
||||||
*/
|
*/
|
||||||
injectStructuredData(schema: object): void {
|
injectStructuredData(schema: object): void {
|
||||||
// Clear existing schema scripts with the same type
|
if (!this.isBrowser) return;
|
||||||
this.removeAllSchemas();
|
|
||||||
|
|
||||||
// Create new script element using Renderer2 (works in both SSR and browser)
|
// Remove existing schema script
|
||||||
const script = this.renderer.createElement('script');
|
const existingScript = document.querySelector('script[type="application/ld+json"]');
|
||||||
this.renderer.setAttribute(script, 'type', 'application/ld+json');
|
if (existingScript) {
|
||||||
this.renderer.setAttribute(script, 'data-schema', 'true');
|
existingScript.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// Create text node with schema JSON
|
// Add new schema script
|
||||||
const schemaText = this.renderer.createText(JSON.stringify(schema));
|
const script = document.createElement('script');
|
||||||
this.renderer.appendChild(script, schemaText);
|
script.type = 'application/ld+json';
|
||||||
|
script.text = JSON.stringify(schema);
|
||||||
// Append to document head
|
document.head.appendChild(script);
|
||||||
this.renderer.appendChild(this.document.head, script);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all schema scripts (internal helper, SSR-compatible)
|
* Clear all structured data
|
||||||
*/
|
|
||||||
private removeAllSchemas(): void {
|
|
||||||
const existingScripts = this.document.querySelectorAll('script[data-schema="true"]');
|
|
||||||
existingScripts.forEach(script => {
|
|
||||||
this.renderer.removeChild(this.document.head, script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all structured data (SSR-compatible)
|
|
||||||
*/
|
*/
|
||||||
clearStructuredData(): void {
|
clearStructuredData(): void {
|
||||||
this.removeAllSchemas();
|
if (!this.isBrowser) return;
|
||||||
|
|
||||||
|
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
|
||||||
|
scripts.forEach(script => script.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -528,21 +516,20 @@ export class SeoService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject multiple structured data schemas (SSR-compatible using Renderer2)
|
* Inject multiple structured data schemas
|
||||||
*/
|
*/
|
||||||
injectMultipleSchemas(schemas: object[]): void {
|
injectMultipleSchemas(schemas: object[]): void {
|
||||||
// Clear existing schema scripts
|
if (!this.isBrowser) return;
|
||||||
this.removeAllSchemas();
|
|
||||||
|
|
||||||
// Add new schema scripts using Renderer2
|
// Remove existing schema scripts
|
||||||
|
this.clearStructuredData();
|
||||||
|
|
||||||
|
// Add new schema scripts
|
||||||
schemas.forEach(schema => {
|
schemas.forEach(schema => {
|
||||||
const script = this.renderer.createElement('script');
|
const script = document.createElement('script');
|
||||||
this.renderer.setAttribute(script, 'type', 'application/ld+json');
|
script.type = 'application/ld+json';
|
||||||
this.renderer.setAttribute(script, 'data-schema', 'true');
|
script.text = JSON.stringify(schema);
|
||||||
|
document.head.appendChild(script);
|
||||||
const schemaText = this.renderer.createText(JSON.stringify(schema));
|
|
||||||
this.renderer.appendChild(script, schemaText);
|
|
||||||
this.renderer.appendChild(this.document.head, script);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import {
|
|
||||||
faArrowRight,
|
|
||||||
faEnvelope,
|
|
||||||
faLock,
|
|
||||||
faTimes,
|
|
||||||
faTrash,
|
|
||||||
faUserGear,
|
|
||||||
faUserPlus
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
|
|
||||||
export const APP_ICONS = {
|
|
||||||
faArrowRight,
|
|
||||||
faEnvelope,
|
|
||||||
faLock,
|
|
||||||
faTimes,
|
|
||||||
faTrash,
|
|
||||||
faUserGear,
|
|
||||||
faUserPlus,
|
|
||||||
};
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
// Lightweight logger implementation for both dev and production
|
|
||||||
// Avoids dynamic require() which causes build issues
|
|
||||||
const createLoggerImpl = (name: string) => ({
|
|
||||||
info: (...args: any[]) => {
|
|
||||||
if (!environment.production) {
|
|
||||||
console.log(`[${name}]`, ...args);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
warn: (...args: any[]) => console.warn(`[${name}]`, ...args),
|
|
||||||
error: (...args: any[]) => console.error(`[${name}]`, ...args),
|
|
||||||
debug: (...args: any[]) => {
|
|
||||||
if (!environment.production) {
|
|
||||||
console.debug(`[${name}]`, ...args);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trace: (...args: any[]) => {
|
|
||||||
if (!environment.production) {
|
|
||||||
console.trace(`[${name}]`, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createLogger = createLoggerImpl;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { createLogger as _createLogger } from './logger';
|
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
||||||
import { jwtDecode } from 'jwt-decode';
|
import { jwtDecode } from 'jwt-decode';
|
||||||
import onChange from 'on-change';
|
import onChange from 'on-change';
|
||||||
import { SortByOptions, User } from '../../../../bizmatch-server/src/models/db.model';
|
import { SortByOptions, User } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
@@ -141,8 +141,14 @@ export function createMailInfo(user?: User): MailInfo {
|
|||||||
listing: null,
|
listing: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function createLogger(name: string, level?: number, options?: any) {
|
export function createLogger(name: string, level: number = INFO, options: any = {}) {
|
||||||
return _createLogger(name);
|
return _createLogger({
|
||||||
|
name,
|
||||||
|
streams: [{ level, stream: new ConsoleFormattedStream() }],
|
||||||
|
serializers: stdSerializers,
|
||||||
|
src: true,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export function formatPhoneNumber(phone: string): string {
|
export function formatPhoneNumber(phone: string): string {
|
||||||
const cleaned = ('' + phone).replace(/\D/g, '');
|
const cleaned = ('' + phone).replace(/\D/g, '');
|
||||||
|
|||||||
BIN
bizmatch/src/assets/images/1_Version.jpg
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
bizmatch/src/assets/images/2_1_Version.jpg
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
bizmatch/src/assets/images/2_Version.jpg
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
bizmatch/src/assets/images/avatar-f-3 copy.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
bizmatch/src/assets/images/business_logo_with_bg.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
bizmatch/src/assets/images/bw-sky copy.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
bizmatch/src/assets/images/corpusChristiSkyline copy.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
bizmatch/src/assets/images/corpusChristiSkyline.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
bizmatch/src/assets/images/flag_flour_bg.jpg
Normal file
|
After Width: | Height: | Size: 8.7 MiB |
BIN
bizmatch/src/assets/images/header-logo copy.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
bizmatch/src/assets/images/header-logo-hq.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
bizmatch/src/assets/images/header-logo-original.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
bizmatch/src/assets/images/index-bg copy.jpg
Normal file
|
After Width: | Height: | Size: 662 KiB |
BIN
bizmatch/src/assets/images/index-bg copy.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
bizmatch/src/assets/images/index-bg.jpg
Normal file
|
After Width: | Height: | Size: 662 KiB |
BIN
bizmatch/src/assets/images/person_placeholder copy.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
bizmatch/src/assets/images/placeholder copy.png
Normal file
|
After Width: | Height: | Size: 667 B |
BIN
bizmatch/src/assets/images/placeholder_properties copy.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
bizmatch/src/assets/images/properties_logo_backup.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
bizmatch/src/assets/images/properties_logo_with_bg.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
@@ -1,6 +1,6 @@
|
|||||||
// Build information, automatically generated by `the_build_script` :zwinkern:
|
// Build information, automatically generated by `the_build_script` :zwinkern:
|
||||||
const build = {
|
const build = {
|
||||||
timestamp: "GER: 05.02.2026 13:06 | TX: 02/05/2026 6:06 AM"
|
timestamp: "GER: 03.02.2026 12:44 | TX: 02/03/2026 5:44 AM"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default build;
|
export default build;
|
||||||
@@ -35,9 +35,6 @@
|
|||||||
|
|
||||||
<!-- Preload critical assets -->
|
<!-- Preload critical assets -->
|
||||||
<link rel="preload" as="image" href="/assets/images/header-logo.png" type="image/png" />
|
<link rel="preload" as="image" href="/assets/images/header-logo.png" type="image/png" />
|
||||||
<!-- Hero background is LCP element - preload with high priority -->
|
|
||||||
<link rel="preload" as="image" href="/assets/images/flags_bg.avif" type="image/avif" fetchpriority="high" />
|
|
||||||
<link rel="preload" as="image" href="/assets/images/flags_bg.jpg" imagesrcset="/assets/images/flags_bg.jpg" type="image/jpeg" />
|
|
||||||
|
|
||||||
<!-- Prefetch common assets -->
|
<!-- Prefetch common assets -->
|
||||||
<link rel="prefetch" as="image" href="/assets/images/business_logo.png" />
|
<link rel="prefetch" as="image" href="/assets/images/business_logo.png" />
|
||||||
@@ -61,53 +58,6 @@
|
|||||||
<link rel="icon" href="/assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
<link rel="icon" href="/assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
||||||
<link rel="icon" href="/assets/cropped-Favicon-192x192.png" sizes="192x192" />
|
<link rel="icon" href="/assets/cropped-Favicon-192x192.png" sizes="192x192" />
|
||||||
<link rel="apple-touch-icon" href="/assets/cropped-Favicon-180x180.png" />
|
<link rel="apple-touch-icon" href="/assets/cropped-Favicon-180x180.png" />
|
||||||
|
|
||||||
<!-- Schema.org Structured Data (Static) -->
|
|
||||||
<script type="application/ld+json">
|
|
||||||
{
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "Organization",
|
|
||||||
"name": "BizMatch",
|
|
||||||
"url": "https://www.bizmatch.net",
|
|
||||||
"logo": "https://www.bizmatch.net/assets/images/bizmatch-logo.png",
|
|
||||||
"description": "Buy and sell businesses, commercial properties, and franchises. Browse thousands of verified listings across the United States.",
|
|
||||||
"address": {
|
|
||||||
"@type": "PostalAddress",
|
|
||||||
"streetAddress": "1001 Blucher Street",
|
|
||||||
"addressLocality": "Corpus Christi",
|
|
||||||
"addressRegion": "TX",
|
|
||||||
"postalCode": "78401",
|
|
||||||
"addressCountry": "US"
|
|
||||||
},
|
|
||||||
"contactPoint": {
|
|
||||||
"@type": "ContactPoint",
|
|
||||||
"contactType": "Customer Service",
|
|
||||||
"email": "info@bizmatch.net"
|
|
||||||
},
|
|
||||||
"sameAs": [
|
|
||||||
"https://www.facebook.com/bizmatch",
|
|
||||||
"https://www.linkedin.com/company/bizmatch",
|
|
||||||
"https://twitter.com/bizmatch"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="application/ld+json">
|
|
||||||
{
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@type": "WebSite",
|
|
||||||
"name": "BizMatch",
|
|
||||||
"url": "https://www.bizmatch.net",
|
|
||||||
"potentialAction": {
|
|
||||||
"@type": "SearchAction",
|
|
||||||
"target": {
|
|
||||||
"@type": "EntryPoint",
|
|
||||||
"urlTemplate": "https://www.bizmatch.net/businessListings?search={search_term_string}"
|
|
||||||
},
|
|
||||||
"query-input": "required name=search_term_string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="flex flex-col min-h-screen">
|
<body class="flex flex-col min-h-screen">
|
||||||
|
|||||||
@@ -129,8 +129,13 @@ Disallow: /
|
|||||||
# ===========================================
|
# ===========================================
|
||||||
# Sitemap locations
|
# Sitemap locations
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Main sitemap index
|
# Main sitemap index (dynamically generated, contains all sub-sitemaps)
|
||||||
Sitemap: https://www.bizmatch.net/sitemap.xml
|
Sitemap: https://www.bizmatch.net/bizmatch/sitemap.xml
|
||||||
|
|
||||||
|
# Individual sitemaps (auto-listed in sitemap index)
|
||||||
|
# - https://www.bizmatch.net/bizmatch/sitemap/static.xml
|
||||||
|
# - https://www.bizmatch.net/bizmatch/sitemap/business-1.xml
|
||||||
|
# - https://www.bizmatch.net/bizmatch/sitemap/commercial-1.xml
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Host directive (for Yandex)
|
# Host directive (for Yandex)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
// External CSS imports - these URL imports don't trigger deprecation warnings
|
// External CSS imports - these URL imports don't trigger deprecation warnings
|
||||||
// Using css2 API with specific weights for better performance
|
@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap');
|
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css');
|
||||||
|
|
||||||
// Local CSS files loaded as CSS (not SCSS) to avoid @import deprecation
|
// Local CSS files loaded as CSS (not SCSS) to avoid @import deprecation
|
||||||
// Note: These are loaded via angular.json styles array is the preferred approach,
|
// Note: These are loaded via angular.json styles array is the preferred approach,
|
||||||
@@ -121,7 +121,7 @@ p-menubarsub ul {
|
|||||||
|
|
||||||
input::placeholder,
|
input::placeholder,
|
||||||
textarea::placeholder {
|
textarea::placeholder {
|
||||||
color: #757575 !important; /* 4.54:1 contrast - WCAG AA compliant */
|
color: #999 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix für Marker-Icons in Leaflet */
|
/* Fix für Marker-Icons in Leaflet */
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/main.ts"
|
"src/main.ts",
|
||||||
|
"src/main.server.ts",
|
||||||
|
"server.ts"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.d.ts"
|
"src/**/*.d.ts"
|
||||||
|
|||||||