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 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 }