import type { HealthAssessmentAnswers } from './health' export function finiteNumberInRange(value: unknown, minimum: number, maximum: number, integer = false): value is number { return typeof value === 'number' && Number.isFinite(value) && value >= minimum && value <= maximum && (!integer || Number.isInteger(value)) } // Birthdays are calendar dates, not instants. Keep the displayed day unchanged // even when the API serializes a DateTime or the browser has a UTC offset. // Admin.NET returns yyyy-MM-dd HH:mm:ss; also accept date-only and ISO timestamps. export function ageFromBirthday(value: unknown, today = new Date()): number | undefined { if (typeof value !== 'string') return undefined const match = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/.exec(value) if (!match) return undefined const [, yearText, monthText, dayText] = match const year = Number(yearText), month = Number(monthText), day = Number(dayText) const birthday = new Date(year, month - 1, day) if (birthday.getFullYear() !== year || birthday.getMonth() !== month - 1 || birthday.getDate() !== day) return undefined const currentDay = new Date(today.getFullYear(), today.getMonth(), today.getDate()) if (birthday > currentDay) return undefined const beforeBirthday = today.getMonth() < month - 1 || (today.getMonth() === month - 1 && today.getDate() < day) return today.getFullYear() - year - (beforeBirthday ? 1 : 0) } const healthAnswerOptions: Record = { sleepDuration: ['<6', '6-7', '7-8', '>8'], fallingAsleepSpeed: ['hard', 'normal', 'fast'], temperaturePreference: ['heat_fear', 'cold_fear', 'normal'], nightAwakening: ['frequent', 'light', 'none'], mattressPreference: ['firm', 'medium', 'soft'], exerciseFrequency: ['none', '1-2', '3-5', 'daily'], muscleFatigueArea: ['back_neck', 'waist_back', 'legs', 'none'], activeTimeOfDay: ['morning', 'evening', 'night', 'irregular'], mealRegularity: ['regular', 'skip_breakfast', 'irregular'], flavorPreference: ['light', 'moderate', 'heavy'], lateNightEating: ['often', 'sometimes', 'rarely'], dailyWaterIntake: ['abundant', 'moderate', 'scarce'] } export function healthAnswersComplete(answers: HealthAssessmentAnswers): boolean { return (Object.keys(healthAnswerOptions) as (keyof HealthAssessmentAnswers)[]) .every(key => healthAnswerOptions[key].includes(answers[key])) }