Add Rate Monitor app — Booking.com + direct booking engine competitor rates

Combines Booking.com Playwright scraper (from forecasting), direct booking
engine scraper (ported from laptop-archive/guestline-monitor), and Newbook
own-hotel rates into one focused tool. Four views: Bookability, Market View
(with price index badges + direct rate sub-rows), Direct Rates (per-competitor
room breakdown, min-stay flags, hotel config/discovery), Rate Analysis
(advance purchase curve, DOW chart, rate timeline, comparison table).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-05 12:06:30 +00:00
commit e05054172f
50 changed files with 11860 additions and 0 deletions

114
frontend/src/types.ts Normal file
View file

@ -0,0 +1,114 @@
export interface User {
email: string
name: string
is_admin: boolean
caps: string[]
}
export function can(user: User | null, cap: string): boolean {
if (!user) return false
return user.is_admin || user.caps.includes(cap)
}
export interface Hotel {
id: number
name: string
tier: 'own' | 'competitor' | 'market'
star_rating: number | null
review_score: number | null
booking_com_url: string | null
booking_com_id: string | null
display_order: number
notes: string | null
is_active: boolean
scraped_dates?: number
last_scraped?: string | null
}
export interface RateMatrixEntry {
hotel_id: number
hotel_name: string
tier: string
rate_date: string
rate_gross: number | null
availability_status: string
rooms_left: number | null
scraped_at: string | null
}
export interface ScraperStatus {
enabled: boolean
paused: boolean
pause_until: string | null
backend: string
daily_time: string
last_batch: {
batch_id: string
started_at: string
status: string
rates_scraped: number
dates_completed: number
} | null
}
export interface BookabilityCategory {
category_id: string
category_name: string
room_count: number
display_order: number
dates: BookabilityDate[]
}
export interface BookabilityDate {
date: string
gross_rate: number | null
tariffs: TariffSummary[]
occupancy_pct: number | null
available_rooms: number | null
is_bookable: boolean
}
export interface TariffSummary {
tariff_name: string
rate: number | null
min_stay: number | null
advance_max: number | null
is_available: boolean
}
export interface AdvancePurchaseCurvePoint {
lead_bucket: string
avg_rate: number
sample_count: number
}
export interface DowAnalysisPoint {
dow: number
dow_label: string
avg_rate: number
date_count: number
}
export interface StrategySummary {
advance_discount_pct: number | null
weekend_premium_pct: number | null
avg_sold_out_rate_pct: number | null
strategy_label: string
}
export interface HotelAnalysis {
hotel: Hotel
date_range: { from: string; to: string }
advance_purchase_curve: AdvancePurchaseCurvePoint[]
dow_analysis: DowAnalysisPoint[]
sold_out_pattern: Array<{ dow: number; dow_label: string; total_dates: number; sold_out_dates: number }>
strategy_summary: StrategySummary
}
export interface RateTimelinePoint {
scraped_at: string
rate_gross: number | null
availability_status: string
rooms_left: number | null
days_out: number
}