Building a Smart Vacation Optimizer: React, TypeScript & International Holidays¶
After years of manually planning vacations around holidays and trying to maximize time off, I decided to build a comprehensive solution: the Vacation Time Optimizer. This React-based application transforms vacation planning from a tedious manual process into an intelligent, data-driven experience.
The Challenge: Vacation Planning is Complex¶
Most people approach vacation planning by looking at a calendar and guessing which dates might give them the most bang for their buck. But optimal vacation planning involves:
- Understanding your available vacation days
- Identifying upcoming holidays in your region
- Calculating efficiency ratios (total days off vs. vacation days used)
- Visualizing different time periods (months, quarters, years)
- Exporting plans to your calendar system
This complexity inspired me to build a tool that handles all these calculations automatically.
Technical Architecture¶
React 18 + TypeScript Foundation¶
The application is built on React 18 with TypeScript throughout, providing end-to-end type safety:
interface VacationPlan {
startDate: string;
endDate: string;
vacationDaysUsed: number;
totalDays: number;
weekendDays: number;
holidayDays: number;
efficiency: number;
}
interface Holiday {
date: string;
name: string;
type: 'government' | 'company';
}
Multi-View Calendar System¶
One of the most challenging aspects was building a flexible calendar system that automatically switches views based on vacation length:
// Auto-switch to appropriate view based on highlighted period
useEffect(() => {
if (highlightedPeriod) {
const start = highlightedPeriod.start;
const end = highlightedPeriod.end;
const daysDifference = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
// If vacation spans more than 2 months, show year view
if (start.getMonth() !== end.getMonth() &&
(end.getMonth() - start.getMonth() > 1 || end.getFullYear() !== start.getFullYear())) {
setCurrentView('year');
}
// If vacation spans 2 months or is longer than 2 weeks, show quarter view
else if (start.getMonth() !== end.getMonth() || daysDifference > 14) {
setCurrentView('quarter');
}
// Otherwise, month view is fine
else {
setCurrentView('month');
}
}
}, [highlightedPeriod]);
International Holiday Integration¶
Supporting multiple countries required building a comprehensive holiday database:
export const getCountryHolidays = (country: CountryCode): Holiday[] => {
const holidayData = {
'US': [
{ date: '2025-01-01', name: 'New Year\'s Day', type: 'government' as const },
{ date: '2025-01-20', name: 'Martin Luther King Jr. Day', type: 'government' as const },
{ date: '2025-02-17', name: 'Presidents\' Day', type: 'government' as const },
// ... more holidays
],
'CA': [
{ date: '2025-01-01', name: 'New Year\'s Day', type: 'government' as const },
{ date: '2025-02-17', name: 'Family Day', type: 'government' as const },
// ... provincial variations
],
// Support for UK, Germany, France, Japan, Australia
};
return holidayData[country] || [];
};
Smart Recommendation Engine¶
The heart of the application is the vacation optimization algorithm:
export const findOptimalVacationPeriods = (
availableDays: number,
holidays: Holiday[],
year: number,
targetDays: number = 7
): VacationPlan[] => {
const recommendations: VacationPlan[] = [];
// Look for periods around holidays
holidays.forEach(holiday => {
const holidayDate = parseDate(holiday.date);
// Check periods before and after holidays
for (let daysBefore = 0; daysBefore <= 4; daysBefore++) {
for (let daysAfter = 0; daysAfter <= 4; daysAfter++) {
const startDate = addDays(holidayDate, -daysBefore);
const endDate = addDays(holidayDate, daysAfter);
const plan = calculateVacationPlan(startDate, endDate, holidays);
if (plan.vacationDaysUsed <= availableDays &&
plan.totalDays >= targetDays) {
recommendations.push(plan);
}
}
}
});
// Sort by efficiency (total days / vacation days used)
return recommendations
.sort((a, b) => b.efficiency - a.efficiency)
.slice(0, 10); // Top 10 recommendations
};
Calendar Export Integration¶
Modern vacation planning requires seamless integration with existing calendar systems:
export const generateGoogleCalendarUrl = (plan: VacationPlan): string => {
const startDate = parseDate(plan.startDate);
const endDate = parseDate(plan.endDate);
const title = `Vacation - ${plan.totalDays} days off`;
const details = `Optimized vacation using ${plan.vacationDaysUsed} vacation days for ${plan.totalDays} total days off. Efficiency: ${plan.efficiency.toFixed(1)}x`;
const params = new URLSearchParams({
action: 'TEMPLATE',
text: title,
details: details,
dates: `${formatDateForCalendar(startDate)}/${formatDateForCalendar(endDate)}`,
ctz: Intl.DateTimeFormat().resolvedOptions().timeZone
});
return `https://calendar.google.com/calendar/render?${params.toString()}`;
};
Mobile-First Design Philosophy¶
The application follows a progressive enhancement approach:
// Touch navigation for mobile
const TouchSwipe: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [touchStart, setTouchStart] = useState<number | null>(null);
const [touchEnd, setTouchEnd] = useState<number | null>(null);
const handleTouchStart = (e: React.TouchEvent) => {
setTouchEnd(null);
setTouchStart(e.targetTouches[0].clientX);
};
const handleTouchMove = (e: React.TouchEvent) => {
setTouchEnd(e.targetTouches[0].clientX);
};
const handleTouchEnd = () => {
if (!touchStart || !touchEnd) return;
const distance = touchStart - touchEnd;
const isLeftSwipe = distance > 50;
const isRightSwipe = distance < -50;
if (isLeftSwipe) {
// Navigate to next month/quarter/year
}
if (isRightSwipe) {
// Navigate to previous month/quarter/year
}
};
return (
<div
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
{children}
</div>
);
};
Performance Optimizations¶
With complex calendar calculations and international holiday data, performance was crucial:
- Memoized Calculations: Heavy date calculations are memoized
- Efficient Rendering: Only visible calendar months are rendered
- Optimized Bundle: Tree-shaking and code splitting with Vite
- Strategic State Management: Minimal re-renders through careful state design
Accessibility & User Experience¶
The application prioritizes accessibility:
- ARIA Labels: Complete screen reader support
- Keyboard Navigation: Full functionality without mouse
- Focus Management: Logical tab order throughout the interface
- Color Contrast: High contrast mode for visual accessibility
- Mobile Touch Targets: Large, touch-friendly interactive elements
Results & Impact¶
The Vacation Time Optimizer transforms a complex manual process into an intuitive, data-driven experience. Users can:
- Save Hours of manual vacation planning
- Maximize Time Off through efficiency scoring
- Plan Internationally with built-in holiday support
- Export Seamlessly to any calendar system
- Access Anywhere through responsive design
Technical Lessons Learned¶
- Complex State Management: Managing multiple calendar views and vacation periods required careful state architecture
- International Considerations: Holiday systems vary significantly between countries
- Date Calculations: Working with dates across timezones and DST transitions is challenging
- Mobile Performance: Calendar rendering on mobile devices requires optimization
- User Experience: Auto-switching views based on vacation length greatly improves UX
Open Source & Future Plans¶
The project is open source under Apache License 2.0, encouraging community contributions. Future enhancements include:
- Team vacation coordination features
- Advanced analytics and reporting
- PWA capabilities with offline support
- Machine learning for personalized recommendations
- Integration with HR systems
The Vacation Time Optimizer demonstrates how thoughtful application of modern web technologies can solve real-world problems while providing exceptional user experiences.
Check out the live application and contribute to the project on GitHub. Your next perfect vacation is just a few clicks away!