import React, { useState, useEffect, useMemo } from ‘react’;
import {
Search,
MapPin,
Cloud,
Sun,
CloudSun,
CloudRain,
Wind,
Droplets,
Thermometer,
ArrowRight,
Copy,
Check,
Menu,
X,
ChevronDown,
ChevronUp,
Eye,
Sunrise,
Sunset,
Umbrella,
Navigation,
Share2,
Code,
Layout,
Palette,
Settings,
MoreHorizontal
} from ‘lucide-react’;
/**
* — MOCK DATA GENERATORS & MODELS —
*/
const CONDITIONS = [
{ text: “Sunny”, icon: Sun, color: “text-yellow-400” },
{ text: “Partly Cloudy”, icon: CloudSun, color: “text-yellow-200” },
{ text: “Cloudy”, icon: Cloud, color: “text-gray-400” },
{ text: “Rain Shower”, icon: CloudRain, color: “text-blue-400” },
{ text: “Heavy Rain”, icon: CloudRain, color: “text-blue-600” },
];
const generateWeather = (city) => {
const currentCond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];
// Current Data
const current = {
temp: 72,
feels_like: 75,
condition_text: currentCond.text,
icon: currentCond.icon,
iconColor: currentCond.color,
high: 78,
low: 65,
wind_speed: “8 mph”,
wind_dir: “NW”,
humidity: “45%”,
dew_point: “58°”,
pressure: “29.92 in”,
uv_index: “6 of 10”,
visibility: “10 mi”,
sunrise: “6:23 am”,
sunset: “8:14 pm”,
moon_phase: “Waxing Gibbous”,
outlook_text: `Expect ${currentCond.text.toLowerCase()} conditions to continue until late afternoon. Wind gusts may increase slightly.`
};
// Hourly Data (Next 24h)
const hourly = Array.from({ length: 24 }, (_, i) => {
const hour = (new Date().getHours() + i) % 24;
const timeLabel = hour === 0 ? “12 am” : hour > 12 ? `${hour – 12} pm` : `${hour} am`;
const cond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];
return {
timestamp: i,
time: timeLabel,
temp: 72 – Math.floor(Math.random() * 10) + (i > 6 && i < 18 ? 10 : 0),
condition_text: cond.text,
icon: cond.icon,
precip_chance: Math.floor(Math.random() * 30) + "%",
wind: `${5 + Math.floor(Math.random() * 10)} mph ${['N','NE','E','SE','S','SW','W','NW'][Math.floor(Math.random()*8)]}`,
humidity: `${40 + Math.floor(Math.random() * 40)}%`,
feels_like: `${70 + Math.floor(Math.random() * 10)}°`,
uv_index: i > 6 && i < 18 ? Math.floor(Math.random() * 8) : 0,
cloud_cover: `${Math.floor(Math.random() * 100)}%`
};
});
// Daily Data (10 Days)
const days = ["Today", "Fri 12", "Sat 13", "Sun 14", "Mon 15", "Tue 16", "Wed 17", "Thu 18", "Fri 19", "Sat 20"];
const daily = days.map((day, i) => {
const cond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];
return {
date: day,
high: 70 + Math.floor(Math.random() * 15),
low: 55 + Math.floor(Math.random() * 10),
condition_text: cond.text,
icon: cond.icon,
precip_chance: Math.floor(Math.random() * 60) + “%”,
wind: `${5 + Math.floor(Math.random() * 15)} mph`,
humidity: `${50 + Math.floor(Math.random() * 30)}%`,
sunrise: “6:25 am”,
sunset: “8:10 pm”
};
});
return { location: { name: city, id: city.toLowerCase() }, current, hourly, daily };
};
const SAMPLE_PROMPTS = [
{
id: ‘p1’,
title: ‘Morning Walk Planner’,
description: ‘Determine the best time for a walk based on conditions.’,
category: ‘Daily Planning’,
tags: [‘health’, ‘active’],
template: “Analyze the forecast for {{city}}. Given the current temp of {{temp}} and {{condition}}, suggest the best 1-hour window for a walk today.”,
},
{
id: ‘p2’,
title: ‘Travel Commute Risk’,
description: ‘Assess driving risks for the next 24 hours.’,
category: ‘Travel’,
tags: [‘safety’, ‘commute’],
template: “I am driving in {{city}}. Review the hourly forecast. Are there any visibility ({{visibility}}) or precipitation risks I should know about?”,
},
{
id: ‘p3’,
title: ‘Event Go/No-Go’,
description: ‘Decision helper for outdoor events.’,
category: ‘Events’,
tags: [‘planning’, ‘outdoor’],
template: “I have an outdoor event planned in {{city}}. With a high of {{high}} and precip chance of {{precip}}, should I move it indoors?”,
},
{
id: ‘p4’,
title: ‘Allergy & Air Quality’,
description: ‘Check air quality impact on health.’,
category: ‘Health’,
tags: [‘medical’, ‘wellness’],
template: “Check the current humidity ({{humidity}}) and wind ({{wind}}) in {{city}}. Advise on allergy risks for today.”,
},
];
/**
* — UTILITIES —
*/
const cn = (…classes) => classes.filter(Boolean).join(‘ ‘);
/**
* — COMPONENTS —
*/
// 1. HEADER
const Header = ({ activeTab, onTabChange, onSearch, searchQuery, setSearchQuery, onOpenWidget }) => (
);
// 2. TODAY VIEW
const TodayView = ({ data, onNavigate }) => {
const { current, location } = data;
const Icon = current.icon;
return (
);
};
// 4. 10-DAY VIEW
const TenDayView = ({ data }) => {
const [expandedDay, setExpandedDay] = useState(null);
return (
);
};
// 5. PROMPT LIBRARY
const PromptLibrary = ({ prompts, onUse }) => {
const [filter, setFilter] = useState(‘All’);
const filtered = filter === ‘All’ ? prompts : prompts.filter(p => p.category === filter);
return (
);
};
// 6. WIDGET BUILDER
const WidgetBuilder = ({ weather }) => {
const [config, setConfig] = useState({
theme: ‘dark’, // dark, light, transparent
layout: ‘sidebar’, // sidebar, compact
units: ‘F’
});
const embedCode = ``;
return (
{label}
{value}
);
const DetailBox = ({ label, value }) => (
{type === ‘travel’ ?
);
const FooterNote = () => (
;
const Info = ({ className, size }) => (
);
const PromptModal = ({ isOpen, onClose, prompt, weather }) => {
if (!isOpen) return null;
const interpolated = prompt.template
.replace(‘{{city}}’, weather.location.name)
.replace(‘{{temp}}’, weather.current.temp + ‘°’)
.replace(‘{{condition}}’, weather.current.condition_text)
.replace(‘{{humidity}}’, weather.current.humidity)
.replace(‘{{wind}}’, weather.current.wind_speed)
.replace(‘{{high}}’, weather.current.high + ‘°’)
.replace(‘{{precip}}’, weather.daily[0].precip_chance)
.replace(‘{{visibility}}’, weather.current.visibility);
return (
);
};
/**
* — MAIN APP —
*/
export default function LuxHarborApp() {
const [activeTab, setActiveTab] = useState(‘Today’);
const [weatherData, setWeatherData] = useState(null);
const [searchQuery, setSearchQuery] = useState(‘Baltimore’);
const [loading, setLoading] = useState(true);
// Modal State
const [modalOpen, setModalOpen] = useState(false);
const [selectedPrompt, setSelectedPrompt] = useState(null);
// Initial Load
useEffect(() => {
handleSearch(‘Baltimore’);
}, []);
const handleSearch = (city) => {
setLoading(true);
// Simulate API Fetch
setTimeout(() => {
setWeatherData(generateWeather(city));
setLoading(false);
}, 600);
};
const handleOpenPrompt = (prompt) => {
setSelectedPrompt(prompt);
setModalOpen(true);
};
const renderContent = () => {
if (loading) return (
);
switch(activeTab) {
case ‘Today’: return ;
case ‘Hourly’: return ;
case ’10-Day’: return ;
case ‘Radar’: return (
);
case ‘Prompt Library’: return ;
case ‘Widget’: return ; // Added dedicated view or modal logic
default: return null;
}
};
return (
setActiveTab(‘Widget’)}
/>
{renderContent()}
setModalOpen(false)}
prompt={selectedPrompt}
weather={weatherData}
/>
);
}
{/* Top Bar */}
{/* Logo */}
LuxHarbor Weather
{/* Search */}
{/* Actions removed for visitor view */}
{/* Navigation Tabs */}
{/* Location Header */}
As of {new Date().toLocaleTimeString([], {hour: ‘2-digit’, minute:’2-digit’})} EST
{/* Main Current Conditions Card */}
{/* Outlook Strip */}
{/* Content Blocks */}
Roads are dry with good visibility. No major weather-related delays expected on I-95 corridor this evening.
Air quality is Moderate. Pollen counts are low, but sensitive groups should limit prolonged outdoor exertion.
);
};
// 3. HOURLY VIEW
const HourlyView = ({ data }) => {
const [expandedRow, setExpandedRow] = useState(null);
return (
{location.name}, US
As of {new Date().toLocaleTimeString([], {hour: ‘2-digit’, minute:’2-digit’})} EST
{/* Left: Big Temp & Condition */}
{/* Right: Grid of Metrics */}
{current.temp}°
{current.condition_text}
Day {current.high}° • Night {current.low}°
Feels Like {current.feels_like}
Outlook
{current.outlook_text}
Hourly Forecast
Next 24 Hours
{data.hourly.map((hour, idx) => {
const isExpanded = expandedRow === idx;
const Icon = hour.icon;
return (
{/* Summary Row */}
)}
);
})}
setExpandedRow(isExpanded ? null : idx)}
className=”flex items-center justify-between p-4 cursor-pointer hover:bg-slate-700/30 transition-colors”
>
{hour.condition_text}
{hour.precip_chance}
{hour.wind}
{/* Detailed Expansion */}
{isExpanded && (
{hour.time}
{hour.temp}°
{isExpanded ? : }
10-Day Forecast
{data.daily.map((day, idx) => {
const isExpanded = expandedDay === idx;
const Icon = day.icon;
return (
)}
);
})}
setExpandedDay(isExpanded ? null : idx)}
className=”flex items-center justify-between p-4 cursor-pointer hover:bg-slate-700/30 transition-colors”
>
{day.condition_text}
{day.precip_chance}
{isExpanded && (
{day.date}
{day.high}°
{day.low}°
{isExpanded ? : }
LuxHarbor Tip: A great day for outdoor activities in the morning before the heat sets in.
Weather Prompt Library
AI-ready templates that automatically adapt to the current forecast. Select a prompt to inject live data.
{[‘All’, ‘Daily Planning’, ‘Travel’, ‘Events’, ‘Health’].map(cat => (
))}
{filtered.map(prompt => (
))}
{prompt.category}
{prompt.title}
{prompt.description}
{/* Controls */}
{/* Live Preview */}
);
};
// HELPER COMPONENTS
const MetricRow = ({ icon: Icon, label, value }) => (
Widget Builder
Customize and embed a free weather widget for your site.
{[‘dark’, ‘light’, ‘transparent’].map(t => (
))}
{[‘sidebar’, ‘compact’].map(l => (
))}
{embedCode}
{/* Mock Widget Iframe */}
{config.layout === ‘sidebar’ && (
)}
Baltimore, US
72°
Partly Cloudy
Tomorrow 76°/62°
Saturday 70°/55°
Sunday 68°/54°
Powered by LuxHarbor
{label}
{value}
);
const ContentCard = ({ title, children, type }) => (
{type === ‘travel’ ? : }
{title}
{children}
Demo data only; connect to your preferred weather API for production use.
© 2025 LuxHarbor Weather.
);
const InfoIcon = ({ className, size }) => © 2025 LuxHarbor Weather.
Use Prompt
Current weather data for {weather.location.name} has been injected.
Fetching forecast data…
Interactive Radar
Map integration coming soon.