bizmatch-project/CHANGES.md

21 KiB

Changelog - BizMatch Project

Dokumentation aller wichtigen Änderungen am BizMatch-Projekt. Diese Datei listet Feature-Implementierungen, Bugfixes, Datenbank-Migrationen und architektonische Verbesserungen auf.


Inhaltsverzeichnis

  1. Datenbank-Änderungen
  2. Backend-Änderungen
  3. Frontend-Änderungen
  4. SEO-Verbesserungen
  5. Code-Cleanup & Wartung
  6. Bekannte Issues & Workarounds

1) Datenbank-Änderungen

1.1 Schema-Migration: JSON-basierte Speicherung

Datum: November 2025 Status: Abgeschlossen

Zusammenfassung der Änderungen

Die Datenbank wurde von einem relationalen Schema zu einem JSON-basierten Schema migriert. Dies bedeutet:

  • Neue Tabellen wurden erstellt (users_json, businesses_json, commercials_json)
  • Alte Tabellen wurden NICHT gelöscht (users, businesses, commercials existieren noch)
  • Alle Daten wurden migriert (kopiert von alten zu neuen Tabellen)
  • Anwendung nutzt ausschließlich neue Tabellen (alte Tabellen dienen nur als Backup)

Detaillierte Tabellenstruktur

ALTE Tabellen (nicht mehr in Verwendung, aber noch vorhanden):

-- users (relational)
CREATE TABLE users (
  id UUID PRIMARY KEY,
  email VARCHAR(255),
  firstname VARCHAR(100),
  lastname VARCHAR(100),
  phone VARCHAR(50),
  location_name VARCHAR(255),
  location_state VARCHAR(2),
  location_latitude FLOAT,
  location_longitude FLOAT,
  customer_type VARCHAR(50),
  customer_sub_type VARCHAR(50),
  show_in_directory BOOLEAN,
  created TIMESTAMP,
  updated TIMESTAMP,
  -- ... weitere 20+ Spalten
);

-- businesses (relational)
CREATE TABLE businesses (
  id UUID PRIMARY KEY,
  email VARCHAR(255),
  title VARCHAR(500),
  asking_price DECIMAL,
  established INTEGER,
  revenue DECIMAL,
  cash_flow DECIMAL,
  -- ... weitere 30+ Spalten für alle Business-Eigenschaften
);

-- commercials (relational)
CREATE TABLE commercials (
  id UUID PRIMARY KEY,
  email VARCHAR(255),
  title VARCHAR(500),
  asking_price DECIMAL,
  property_type VARCHAR(100),
  -- ... weitere 25+ Spalten für alle Property-Eigenschaften
);

NEUE Tabellen (aktuell in Verwendung):

-- users_json (JSON-basiert)
CREATE TABLE users_json (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) UNIQUE NOT NULL,
  data JSONB NOT NULL
);

-- businesses_json (JSON-basiert)
CREATE TABLE businesses_json (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) NOT NULL,
  data JSONB NOT NULL
);

-- commercials_json (JSON-basiert)
CREATE TABLE commercials_json (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) NOT NULL,
  data JSONB NOT NULL
);

Was wurde NICHT geändert

Folgende Dinge wurden NICHT geändert:

  1. Alte Tabellen existieren weiterhin - Sie wurden nicht gelöscht, um als Backup zu dienen
  2. Datenbank-Name - Weiterhin bizmatch (keine Änderung)
  3. Datenbank-User - Weiterhin bizmatch (keine Änderung)
  4. Datenbank-Passwort - Keine Änderung
  5. Datenbank-Port - Weiterhin 5432 (keine Änderung)
  6. Docker-Container-Name - Weiterhin bizmatchdb (keine Änderung)
  7. Indices - PostgreSQL JSONB-Indices wurden automatisch erstellt

Was wurde geändert

Folgende Dinge wurden geändert:

  1. Anwendungs-Code verwendet nur noch neue Tabellen:

    • Backend liest/schreibt nur noch in users_json, businesses_json, commercials_json
    • Drizzle ORM Schema wurde aktualisiert (bizmatch-server/src/drizzle/schema.ts)
    • Alle Services wurden angepasst (user.service.ts, business-listing.service.ts, etc.)
  2. Datenstruktur in JSONB-Spalten:

    • Alle Felder, die vorher einzelne Spalten waren, sind jetzt in der data-Spalte als JSON
    • Nested Objects möglich (z.B. location mit name, state, latitude, longitude)
    • Arrays direkt im JSON speicherbar (z.B. imageOrder, areasServed)
  3. Query-Syntax:

    • Statt WHERE firstname = 'John'WHERE (data->>'firstname') = 'John'
    • Statt WHERE location_state = 'TX'WHERE (data->'location'->>'state') = 'TX'
    • Haversine-Formel für Radius-Suche nutzt jetzt JSON-Pfade

Beispiel: User-Datensatz Vorher/Nachher

VORHER (relationale Tabelle users):

id email firstname lastname phone location_name location_state location_latitude location_longitude customer_type customer_sub_type show_in_directory created updated
abc-123 john@example.com John Doe +1-555-0123 Austin TX 30.2672 -97.7431 professional broker true 2025-11-01 2025-11-25

NACHHER (JSON-Tabelle users_json):

id email data (JSONB)
abc-123 john@example.com {"firstname": "John", "lastname": "Doe", "phone": "+1-555-0123", "location": {"name": "Austin", "state": "TX", "latitude": 30.2672, "longitude": -97.7431}, "customerType": "professional", "customerSubType": "broker", "showInDirectory": true, "created": "2025-11-01T10:00:00Z", "updated": "2025-11-25T15:30:00Z"}

Vorteile der neuen Struktur

  • Keine Schema-Migrationen mehr nötig - Neue Felder einfach im JSON hinzufügen
  • Flexiblere Datenstrukturen - Nested Objects und Arrays direkt speicherbar
  • Einfacheres ORM-Mapping - TypeScript-Interfaces direkt zu JSON serialisierbar
  • Bessere Performance - PostgreSQL JSONB ist indexierbar und schnell durchsuchbar
  • Reduzierte Code-Komplexität - Weniger Join-Operationen, weniger Spalten-Mapping

Migration durchführen (Referenz)

Die Migration wurde bereits durchgeführt. Falls nötig, Backup-Prozess:

# 1. Backup der alten relationalen Tabellen
docker exec -it bizmatchdb \
  pg_dump -U bizmatch -d bizmatch -t users -t businesses -t commercials \
  -F c -Z 9 -f /tmp/backup_relational_tables.dump

# 2. Neue Tabellen sind bereits vorhanden und in Verwendung
# 3. Alte Tabellen können bei Bedarf gelöscht werden (NICHT empfohlen vor finalem Produktions-Test)

1.2 Location-Datenstruktur bei Professionals

Datum: November 2025 Status: Abgeschlossen

Problem

Professionals-Suche funktionierte nicht, da die Datenstruktur für location falsch angenommen wurde.

Lösung

  • Professionals verwenden location-Objekt (nicht areasServed-Array)
  • Struktur: { name: 'Austin', state: 'TX', latitude: 30.2672, longitude: -97.7431 }

Betroffene Queries

  • Exact City Search: location.name ILIKE-Vergleich
  • Radius Search: Haversine-Formel mit location.latitude und location.longitude

2) Backend-Änderungen

2.1 Professionals Search Fix

Datei: bizmatch-server/src/user/user.service.ts Status: Abgeschlossen

Änderungen

Exact City Search (Zeile 28-30):

if (criteria.city && criteria.searchType === 'exact') {
  whereConditions.push(sql`(${schema.users_json.data}->'location'->>'name') ILIKE ${criteria.city.name}`);
}

Radius Search (Zeile 32-36):

if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
  const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
  const distanceQuery = getDistanceQuery(schema.users_json, cityGeo.latitude, cityGeo.longitude);
  whereConditions.push(sql`${distanceQuery} <= ${criteria.radius}`);
}

State Filter (Zeile 55-57):

if (criteria.state) {
  whereConditions.push(sql`(${schema.users_json.data}->'location'->>'state') = ${criteria.state}`);
}

County Filter (Zeile 51-53):

if (criteria.counties && criteria.counties.length > 0) {
  whereConditions.push(or(...criteria.counties.map(county =>
    sql`(${schema.users_json.data}->'location'->>'county') ILIKE ${`%${county}%`}`
  )));
}

2.2 TypeScript-Fehler behoben

Problem: Kompilierungsfehler wegen falscher Parameter-Übergabe an getDistanceQuery()

Lösung:

  • Alt: getDistanceQuery(schema.users_json.data, 'location', lat, lon)
  • Neu: getDistanceQuery(schema.users_json, lat, lon)

2.3 Slug-Unterstützung für SEO-freundliche URLs

Status: Implementiert

Business- und Commercial-Property-Listings können nun über SEO-freundliche Slugs aufgerufen werden:

  • /business/austin-coffee-shop-for-sale statt /business/uuid-123-456
  • /commercial-property/downtown-retail-space-dallas statt /commercial-property/uuid-789

Implementierung:

  • Automatische Slug-Generierung aus Listing-Titeln
  • Slug-basierte Routen in allen Controllern
  • Fallback auf ID, falls kein Slug vorhanden

3) Frontend-Änderungen

3.1 Breadcrumbs-Navigation

Datum: November 2025 Status: Abgeschlossen

Implementierte Komponenten

Betroffene Seiten:

  • Business Detail Pages (details-business-listing.component.ts)
  • Commercial Property Detail Pages (details-commercial-property-listing.component.ts)
  • User Detail Pages (details-user.component.ts)
  • 404 Not Found Page (not-found.component.ts)
  • Business Listings Overview (business-listings.component.ts)
  • Commercial Property Listings Overview (commercial-property-listings.component.ts)

Beispiel-Struktur:

Home > Commercial Properties > Austin Office Space for Sale
Home > Business Listings > Restaurant for Sale in Dallas
Home > 404 - Page Not Found

Komponente: bizmatch/src/app/components/breadcrumbs/breadcrumbs.component.ts

3.2 Automatische 50-Meilen-Radius-Auswahl

Datum: November 2025 Status: Abgeschlossen

Änderungen

Bei Auswahl einer Stadt in den Suchfiltern wird automatisch:

  • Search Type auf "radius" gesetzt
  • Radius auf 50 Meilen gesetzt
  • Filter-UI aktualisiert (Radius-Feld aktiv und ausgefüllt)

Betroffene Dateien:

  • bizmatch/src/app/components/search-modal/search-modal.component.ts (Business)
  • bizmatch/src/app/components/search-modal/search-modal-commercial.component.ts (Properties)
  • bizmatch/src/app/components/search-modal/search-modal-broker.component.ts (Professionals)

Implementierung (Zeilen 255-269 in search-modal.component.ts):

setCity(city: any): void {
  const updates: any = {};
  if (city) {
    updates.city = city;
    updates.state = city.state;
    // Automatically set radius to 50 miles and enable radius search
    updates.searchType = 'radius';
    updates.radius = 50;
  } else {
    updates.city = null;
    updates.radius = null;
    updates.searchType = 'exact';
  }
  this.updateCriteria(updates);
}

3.3 Error Handling Verbesserungen

Betroffene Komponenten:

  • details-business-listing.component.ts
  • details-commercial-property-listing.component.ts

Änderungen:

  • Safe Navigation für listing.imageOrder (Null-Check vor forEach)
  • Verbesserte Error-Message-Extraktion mit Optional Chaining
  • Default-Breadcrumbs auch bei Fehlerfall
  • Korrekte Navigation zu 404-Seite bei fehlenden Listings

Beispiel (Zeile 139 in details-commercial-property-listing.component.ts):

if (this.listing.imageOrder && Array.isArray(this.listing.imageOrder)) {
  this.listing.imageOrder.forEach(image => {
    const imageURL = `${this.env.imageBaseUrl}/pictures/property/${this.listing.imagePath}/${this.listing.serialId}/${image}`;
    this.images.push(new ImageItem({ src: imageURL, thumb: imageURL }));
  });
}

3.4 Business Location Privacy - Stadt-Grenze statt exakter Adresse

Datum: November 2025 Status: Abgeschlossen

Problem & Motivation

Bei Business-Listings für verkaufende Unternehmen sollte die exakte Adresse nicht öffentlich angezeigt werden, um:

  • Konkurrierende Unternehmen nicht zu informieren
  • Kunden nicht zu verunsichern
  • Mitarbeiter vor Verunsicherung zu schützen

Nur die ungefähre Stadt-Region soll angezeigt werden. Die genaue Adresse wird erst nach Kontaktaufnahme mitgeteilt.

Implementierung

Betroffene Dateien:

  • bizmatch/src/app/services/geo.service.ts - Neue Methode getCityBoundary()
  • bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts - Override von Map-Methoden

Unterschied: Business vs. Commercial Property

Listing-Typ Map-Anzeige Adresse-Anzeige Begründung
Business Listings Stadt-Grenze (rotes Polygon) Nur Stadt, County, State Privacy: Verkäufer-Schutz
Commercial Properties Exakter Pin-Marker Vollständige Straßenadresse Immobilie muss sichtbar sein

Technische Umsetzung:

  1. Nominatim API Integration (geo.service.ts:33-37):
getCityBoundary(cityName: string, state: string): Observable<any> {
  const query = `${cityName}, ${state}, USA`;
  let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
  return this.http.get(`${this.baseUrl}?q=${encodeURIComponent(query)}&format=json&polygon_geojson=1&limit=1`, { headers });
}
  1. City Boundary Polygon (details-business-listing.component.ts:322-430):
  • Lädt Stadt-Grenz-Polygon von OpenStreetMap Nominatim API
  • Zeigt rote Umrandung der Stadt (wie Google Maps)
  • Unterstützt Polygon und MultiPolygon Geometrien
  • Fallback: 8km-Radius-Kreis bei API-Fehler oder fehlenden Daten
  1. Karten-Konfiguration:
// Rotes Polygon (wie Google Maps)
const cityPolygon = polygon(latlngs, {
  color: '#ef4444',       // Rot
  fillColor: '#ef4444',   // Rot
  fillOpacity: 0.1,       // Sehr transparent (90% durchsichtig)
  weight: 2               // Linienstärke
});

// Popup zeigt nur allgemeine Information
cityPolygon.bindPopup(`
  <div style="padding: 8px;">
    <strong>General Area:</strong><br/>
    ${cityName}, ${county ? county + ', ' : ''}${state}<br/>
    <small style="color: #666;">City boundary shown for privacy.<br/>Exact location provided after contact.</small>
  </div>
`);
  1. Address Control Override:
  • Zeigt nur: "Austin, Travis County, TX"
  • NICHT angezeigt: Straßenname, Hausnummer, PLZ
  1. OpenStreetMap Link Override:
  • Zoom-Level 11 (Stadt-Ansicht) statt Zoom-Level 15 (Straßen-Ansicht)
  • Keine Marker auf exakter Position

Entwicklungs-Verlauf (Entscheidungen):

Ansatz Grund für Ablehnung Status
2km Fuzzy-Radius Zu klein - bei wenigen Businesses in Stadt identifizierbar Abgelehnt
County-Level Zu groß - schlechte UX, schlechtes SEO Abgelehnt
Stadt-Center + 8km-Kreis Funktioniert, aber nicht professionell aussehend ⚠️ Fallback
Stadt-Grenz-Polygon (wie Google Maps) Professionell, präzise, gute Privacy Implementiert

Vorteile der Lösung:

  • Privacy by Design - Exakte Location nicht sichtbar
  • Professionelles Erscheinungsbild - Wie Google Maps Stadt-Grenzen
  • Genaue Stadt-Darstellung - Nutzt offizielle OSM-Daten
  • Robust - Fallback auf Kreis bei API-Problemen
  • SEO-freundlich - Stadt-Namen bleiben in Metadaten erhalten
  • Multi-Polygon Support - Städte mit mehreren Bereichen (Inseln, etc.)

Was NICHT geändert wurde:

  • Commercial Property Listings zeigen weiterhin exakte Adressen
  • User/Professional Locations zeigen weiterhin Stadt-Pins
  • Datenbank - Location-Daten bleiben unverändert gespeichert
  • Backend - Keine API-Änderungen nötig

4) SEO-Verbesserungen

4.1 Meta-Tags & Structured Data

Status: Implementiert

Neue SEO-Features:

  • Dynamische Title & Description für alle Listing-Seiten
  • Open Graph Tags für Social Media Sharing
  • JSON-LD Structured Data (Schema.org)
  • Canonical URLs
  • Noindex für 404-Seiten

Implementierung:

  • bizmatch/src/app/services/seo.service.ts
  • Automatische Meta-Tag-Generierung basierend auf Listing-Daten

4.2 Sitemap-Generierung

Status: Implementiert

Endpunkte:

  • /bizmatch/sitemap.xml - Haupt-Sitemap (Index)
  • /bizmatch/sitemap/static.xml - Statische Seiten
  • /bizmatch/sitemap/business-1.xml - Business-Listings (paginiert)
  • /bizmatch/sitemap/commercial-1.xml - Commercial-Properties (paginiert)

Controller: bizmatch-server/src/sitemap/sitemap.controller.ts

4.3 SEO-freundliche 404-Seite

Datei: bizmatch/src/app/components/not-found/not-found.component.ts

Features:

  • Breadcrumbs für bessere Navigation
  • noindex Meta-Tag (verhindert Indexierung)
  • Aussagekräftige Title & Description
  • Link zurück zur Homepage

5) Code-Cleanup & Wartung

5.1 Gelöschte temporäre Dateien

Datum: November 2025 Status: Abgeschlossen

Markdown-Dokumentation (7 Dateien):

  • DATABASE-FIX-INSTRUCTIONS.md
  • DEPLOYMENT-GUIDE.md
  • PROFESSIONALS-TAB-IMPLEMENTATION.md
  • RESTART-BACKEND.md
  • SEO-IMPROVEMENTS-SUMMARY.md
  • bizmatch-server/SEO-DEPLOYMENT-SUCCESS.md
  • bizmatch-server/TYPESCRIPT-FIX-SUMMARY.md

Shell-Scripts (33 Dateien):

  • Alle .sh-Dateien in bizmatch-server/ (check-, fix-, test-, run-, setup-*, etc.)

SQL-Test-Dateien (5 Dateien):

  • create-schema.sql
  • insert-professionals-json.sql
  • insert-professionals-simple.sql
  • insert-test-professionals.sql
  • insert-test-professionals-fixed.sql

Debug-JavaScript (2 Dateien):

  • check-db.js
  • verify.js

Komplette Verzeichnisse:

  • bizmatch-server/scripts/ (~13 Dateien)
  • bizmatch-server/src/scripts/ (~13 Dateien)
  • .claude/ (Verzeichnis)

Gesamt: ~75 temporäre Dateien und 3 Verzeichnisse entfernt

5.2 Beibehaltene Konfigurationsdateien

Wichtige Dateien (nicht gelöscht):

  • README.md (Projekt-Dokumentation)
  • bizmatch-server/README.md (Server-Dokumentation)
  • .eslintrc.js (Code-Style-Konfiguration)
  • docker-compose.yml (Container-Konfiguration)
  • .gitignore (Git-Konfiguration)

6) Bekannte Issues & Workarounds

6.1 Docker-Container-Neustart nach Code-Änderungen

Problem: TypeScript-Kompilierungsfehler können dazu führen, dass der Backend-Container nicht startet.

Lösung:

# Container-Logs prüfen
docker logs bizmatch-app --tail 50

# Bei TypeScript-Fehlern: Container neu starten
docker restart bizmatch-app

# Prüfen, ob App erfolgreich gestartet ist
docker logs bizmatch-app | grep "Nest application successfully started"

6.2 Database Connection Issues

Problem: password authentication failed for user "bizmatch"

Lösung: Siehe README.md - Abschnitt 4.1

6.3 Frontend Proxy-Fehler

Problem: http proxy error: /bizmatch/select-options während Backend-Neustart

Lösung:

  • Warten, bis Backend vollständig gestartet ist (~30 Sekunden)
  • Frontend-Dev-Server bei Bedarf neu starten: npm start

Migration-Guide: JSON-Schema

Von relationaler DB zu JSON-Schema

Beispiel: User-Daten

Alt (relationale Tabelle):

CREATE TABLE users (
  id UUID PRIMARY KEY,
  email VARCHAR(255),
  firstname VARCHAR(100),
  lastname VARCHAR(100),
  phone VARCHAR(50),
  ...
);

Neu (JSON-Schema):

CREATE TABLE users_json (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255),
  data JSONB
);

JSON-Struktur in data-Spalte:

{
  "firstname": "John",
  "lastname": "Doe",
  "email": "john.doe@example.com",
  "phone": "+1-555-0123",
  "customerType": "professional",
  "customerSubType": "broker",
  "location": {
    "name": "Austin",
    "state": "TX",
    "latitude": 30.2672,
    "longitude": -97.7431
  },
  "showInDirectory": true,
  "created": "2025-11-25T10:30:00Z",
  "updated": "2025-11-25T10:30:00Z"
}

Query-Beispiele:

-- Alle Professionals in Texas finden
SELECT * FROM users_json
WHERE (data->>'customerType') = 'professional'
  AND (data->'location'->>'state') = 'TX';

-- Nach Name suchen
SELECT * FROM users_json
WHERE (data->>'firstname') ILIKE '%John%';

-- Radius-Suche (50 Meilen um Austin)
SELECT * FROM users_json
WHERE (
  3959 * 2 * ASIN(SQRT(
    POWER(SIN((30.2672 - (data->'location'->>'latitude')::float) * PI() / 180 / 2), 2) +
    COS(30.2672 * PI() / 180) * COS((data->'location'->>'latitude')::float * PI() / 180) *
    POWER(SIN((-97.7431 - (data->'location'->>'longitude')::float) * PI() / 180 / 2), 2)
  ))
) <= 50;

Support & Fragen

Bei Fragen zu diesen Änderungen:

  1. README.md für Setup-Informationen konsultieren
  2. Docker-Logs prüfen: docker logs bizmatch-app und docker logs bizmatchdb
  3. Git-History für Details zu Änderungen durchsuchen

Letzte Aktualisierung: November 2025