Skip to content

fix(api): case-insensitive contact matching on cruise import#105

Merged
Systemsaholic merged 5 commits intomainfrom
feature/issue-103-contact-dedup
Mar 26, 2026
Merged

fix(api): case-insensitive contact matching on cruise import#105
Systemsaholic merged 5 commits intomainfrom
feature/issue-103-contact-dedup

Conversation

@Systemsaholic
Copy link
Copy Markdown
Owner

@Systemsaholic Systemsaholic commented Mar 26, 2026

Summary

  • Fix duplicate contact creation during cruise booking import
  • Root cause: exact case-sensitive eq() match on firstName/lastName
  • Import normalizes names to title case but existing contacts may have different casing
  • Switch to LOWER() comparison for case-insensitive matching

Test plan

  • Verified TypeScript compilation passes
  • Traced full import flow to confirm fix location
  • No DB migration needed — query-only change

fixes #103

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Admin users can now toggle dashboard views between personal, agency, and combined perspectives to filter KPI and leaderboard data accordingly.
    • Contact matching during cruise booking imports is now case-insensitive for improved matching accuracy.
  • Documentation

    • Added OTA consumer portal design specification documenting architecture, AI concierge features, advisor profiles, and referral attribution model.

Systemsaholic and others added 5 commits March 26, 2026 09:29
Comprehensive design spec for the consumer-facing OTA covering:
- Application architecture (Next.js 15.x on Vercel, existing NestJS API)
- AI Concierge with real API tools (Amadeus, Traveltek, Globus)
- Advisor micro-sites synced from TravelLeaders Network profiles
- Attribution system with 30-day referral cookies and CRM priority chain
- Deals system fed by existing VPS scraper via new API endpoint
- Search pages for flights, hotels, cars, cruises, tours, all-inclusives
- Data model (advisor_profiles, deals, ota_referrals, itinerary_templates additions)
- Auth strategy (public endpoints, service-to-service keys, rate limiting)
- Cross-cutting concerns (SEO, a11y, mobile, error handling)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Key corrections based on codebase audit:
- cruise-repository (already public) replaces FusionAPI for Phase 1 search
- Hotels are composite (Google Places + Amadeus + Booking.com), not Amadeus-only
- Template publishing uses separate ota_published_trips table with snapshot
  (itinerary_templates stores JSON blobs, not relational itinerary data)
- Flights/hotels need public facade endpoints (existing are JWT-gated)
- Airport lookup endpoint needed for flight search UX
- tour-repository and Globus live proxy both already public
- Updated API endpoints to reflect actual vs new capabilities

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FusionAPI required for live cruise pricing (not just catalog)
- Remove Booking.com from hotel search — Amadeus only
- Defer car rentals to Phase 2 (no provider exists)
- Update all search, tool, and endpoint references accordingly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admins can now switch between Agent view (personal trips only), Agency
view (all agency trips), or Both (current behavior). The toggle scopes
KPI cards, charts, widgets, and leaderboard visibility. Selection
persists to localStorage. Non-admin users always see personal view only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The import flow normalized passenger names to title case but matched
against existing contacts using exact case-sensitive eq(). If an existing
contact was stored as "gaetan" but the import normalized to "Gaetan",
a duplicate was created. Switch to LOWER() comparison for firstName
and lastName matching.

fixes #103

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Systemsaholic Systemsaholic merged commit f9a952c into main Mar 26, 2026
1 of 4 checks passed
@Systemsaholic Systemsaholic deleted the feature/issue-103-contact-dedup branch March 26, 2026 16:07
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ea33cbf-8fa2-4d29-ac52-3f02a36d0cc1

📥 Commits

Reviewing files that changed from the base of the PR and between e6d7b01 and a17e681.

📒 Files selected for processing (7)
  • apps/admin/src/app/dashboard/page.tsx
  • apps/admin/src/hooks/use-dashboard.ts
  • apps/api/src/cruise-booking/services/import-booking.service.ts
  • apps/api/src/dashboard/dashboard.controller.ts
  • apps/api/src/dashboard/dashboard.service.ts
  • apps/api/src/dashboard/dto/dashboard-overview.dto.ts
  • docs/superpowers/specs/2026-03-26-ota-consumer-portal-design.md

📝 Walkthrough

Walkthrough

Introduced a "view" selection feature to the admin dashboard enabling filtering between personal, agency, and combined views. Frontend manages view state with localStorage persistence; backend API supports the view query parameter to conditionally scope KPI queries and widget data. Also fixed contact matching to use case-insensitive comparison and documented the OTA consumer portal architecture.

Changes

Cohort / File(s) Summary
Dashboard View Selection
apps/admin/src/app/dashboard/page.tsx, apps/admin/src/hooks/use-dashboard.ts
Added DashboardView type and view state management with localStorage persistence. Updated dashboard to render KPI sections conditionally based on selected view (personal/agency/all), with admin-only view toggle. Hook now accepts and passes view parameter through query keys.
API View Parameter Support
apps/api/src/dashboard/dto/dashboard-overview.dto.ts, apps/api/src/dashboard/dashboard.controller.ts, apps/api/src/dashboard/dashboard.service.ts
Extended dashboard overview endpoint with optional view query parameter. Service now makes KPI and widget queries conditional based on view type, returning empty KPI objects when skipped instead of undefined values. Widget data (getRecentTrips, getLeavingSoon) scoped by selected view.
Contact Import Enhancement
apps/api/src/cruise-booking/services/import-booking.service.ts
Changed contact matching from exact first/last name comparison to case-insensitive matching using SQL LOWER() function.
OTA Consumer Portal Design
docs/superpowers/specs/2026-03-26-ota-consumer-portal-design.md
Added comprehensive design specification for OTA consumer portal including Next.js frontend architecture, NestJS API integration, AI concierge chat, advisor profiles, referral attribution model, and Phase 2 roadmap.

Sequence Diagram(s)

sequenceDiagram
    actor Admin as Admin User
    participant UI as Dashboard Page
    participant Hook as useDashboardOverview
    participant QK as React Query
    participant API as Dashboard API
    participant Service as DashboardService
    participant DB as Database

    Admin->>UI: Select view (personal/agency/all)
    UI->>UI: setView(), persist to localStorage
    UI->>Hook: useDashboardOverview({view})
    Hook->>QK: queryKey includes {view}
    QK->>API: GET /dashboard/overview?view=X
    API->>Service: getOverview(auth, {view})
    
    alt view === 'personal' or 'all'
        Service->>DB: Query personal KPIs
        DB-->>Service: Personal KPI data
    end
    
    alt view === 'agency' or 'all'
        Service->>DB: Query agency KPIs & leaderboard
        DB-->>Service: Agency KPI data
    end
    
    Service->>Service: Compute trends if both prior+current
    Service-->>API: Dashboard overview (scoped by view)
    API-->>Hook: KPI response
    Hook-->>UI: Updated dashboard data
    UI->>UI: Render KPI sections conditionally
    UI-->>Admin: Display filtered dashboard
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A dashboard view, oh what a sight!
Personal, agency, or all in one flight,
With localStorage keeping your choice,
Admins now filter with confident voice,
And contacts match better, no matter the case!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-103-contact-dedup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tailfire-client Ready Ready Preview, Comment Mar 26, 2026 4:08pm
tailfire-ota Ready Ready Preview, Comment Mar 26, 2026 4:08pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Contact

1 participant