Spaces:
Running
Running
<html lang="es"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>WeatherWardrobe - Asesor de Vestimenta Meteorol贸gico</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.weather-card { | |
backdrop-filter: blur(10px); | |
background: rgba(255, 255, 255, 0.2); | |
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); | |
border-radius: 10px; | |
border: 1px solid rgba(255, 255, 255, 0.18); | |
} | |
.sunny-gradient { | |
background: linear-gradient(135deg, #f6d365 0%, #fda085 100%); | |
} | |
.rainy-gradient { | |
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); | |
} | |
.cloudy-gradient { | |
background: linear-gradient(135deg, #bdc3c7 0%, #2c3e50 100%); | |
} | |
.night-gradient { | |
background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%); | |
} | |
.transition-all { | |
transition: all 0.3s ease; | |
} | |
.clothing-icon { | |
font-size: 2rem; | |
margin-bottom: 0.5rem; | |
} | |
.hourly-forecast { | |
scrollbar-width: thin; | |
scrollbar-color: rgba(255,255,255,0.5) transparent; | |
} | |
.hourly-forecast::-webkit-scrollbar { | |
height: 6px; | |
} | |
.hourly-forecast::-webkit-scrollbar-track { | |
background: transparent; | |
} | |
.hourly-forecast::-webkit-scrollbar-thumb { | |
background-color: rgba(255,255,255,0.5); | |
border-radius: 20px; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(20px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.animate-fadeIn { | |
animation: fadeIn 0.5s ease-out forwards; | |
} | |
.delay-100 { animation-delay: 0.1s; } | |
.delay-200 { animation-delay: 0.2s; } | |
.delay-300 { animation-delay: 0.3s; } | |
.delay-400 { animation-delay: 0.4s; } | |
</style> | |
</head> | |
<body class="min-h-screen font-sans bg-gray-100"> | |
<div class="min-h-screen flex flex-col"> | |
<!-- Header --> | |
<header class="bg-blue-600 text-white shadow-lg"> | |
<div class="container mx-auto px-4 py-6"> | |
<div class="flex flex-col md:flex-row justify-between items-center"> | |
<div class="flex items-center mb-4 md:mb-0"> | |
<i class="fas fa-cloud-sun text-3xl mr-3"></i> | |
<h1 class="text-2xl font-bold">WeatherWardrobe</h1> | |
</div> | |
<div class="relative w-full md:w-1/3"> | |
<input | |
type="text" | |
id="cityInput" | |
placeholder="Ingresa una ciudad..." | |
class="w-full px-4 py-2 rounded-full text-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-300" | |
> | |
<button | |
id="searchBtn" | |
class="absolute right-0 top-0 h-full px-4 text-blue-600 hover:text-blue-800 focus:outline-none" | |
> | |
<i class="fas fa-search"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</header> | |
<!-- Main Content --> | |
<main class="flex-grow container mx-auto px-4 py-8"> | |
<!-- Loading State --> | |
<div id="loading" class="hidden text-center py-12"> | |
<div class="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div> | |
<p class="text-gray-600">Buscando informaci贸n meteorol贸gica...</p> | |
</div> | |
<!-- Error State --> | |
<div id="error" class="hidden text-center py-12"> | |
<i class="fas fa-exclamation-triangle text-4xl text-red-500 mb-4"></i> | |
<p class="text-gray-600">No se pudo obtener la informaci贸n. Intenta con otra ciudad.</p> | |
</div> | |
<!-- Weather Display --> | |
<div id="weatherDisplay" class="hidden"> | |
<!-- Current Weather --> | |
<div class="weather-card p-6 mb-8 text-white sunny-gradient rounded-xl animate-fadeIn"> | |
<div class="flex flex-col md:flex-row justify-between items-center"> | |
<div class="mb-4 md:mb-0"> | |
<h2 class="text-2xl font-bold" id="currentCity">Ciudad</h2> | |
<p class="text-lg" id="currentDate">Fecha</p> | |
<p class="text-5xl font-bold my-2" id="currentTemp">0掳C</p> | |
<p class="text-xl" id="currentCondition">Condici贸n</p> | |
</div> | |
<div class="text-center"> | |
<i class="fas fa-sun text-6xl mb-2"></i> | |
<div class="grid grid-cols-3 gap-4 mt-4"> | |
<div> | |
<p class="text-sm">HUMEDAD</p> | |
<p class="text-xl font-semibold" id="currentHumidity">0%</p> | |
</div> | |
<div> | |
<p class="text-sm">VIENTO</p> | |
<p class="text-xl font-semibold" id="currentWind">0 km/h</p> | |
</div> | |
<div> | |
<p class="text-sm">UV</p> | |
<p class="text-xl font-semibold" id="currentUV">0</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Hourly Forecast --> | |
<div class="mb-8 animate-fadeIn delay-100"> | |
<h3 class="text-xl font-semibold mb-4 text-gray-700">Pron贸stico por horas</h3> | |
<div class="hourly-forecast flex overflow-x-auto pb-4 gap-4"> | |
<!-- Hourly items will be inserted here by JS --> | |
</div> | |
</div> | |
<!-- Clothing Recommendations --> | |
<div class="animate-fadeIn delay-200"> | |
<h3 class="text-xl font-semibold mb-4 text-gray-700">Recomendaciones de vestimenta</h3> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
<!-- Morning --> | |
<div class="weather-card bg-white p-6 rounded-lg shadow-md"> | |
<div class="flex items-center mb-4"> | |
<i class="fas fa-sun text-yellow-500 text-2xl mr-3"></i> | |
<h4 class="text-lg font-semibold text-gray-800">Ma帽ana</h4> | |
</div> | |
<div class="flex flex-wrap gap-4 justify-center" id="morningClothing"> | |
<!-- Clothing items will be inserted here by JS --> | |
</div> | |
<p class="mt-4 text-gray-600 text-sm italic" id="morningAdvice">Cargando recomendaci贸n...</p> | |
</div> | |
<!-- Afternoon --> | |
<div class="weather-card bg-white p-6 rounded-lg shadow-md"> | |
<div class="flex items-center mb-4"> | |
<i class="fas fa-sun text-orange-500 text-2xl mr-3"></i> | |
<h4 class="text-lg font-semibold text-gray-800">Tarde</h4> | |
</div> | |
<div class="flex flex-wrap gap-4 justify-center" id="afternoonClothing"> | |
<!-- Clothing items will be inserted here by JS --> | |
</div> | |
<p class="mt-4 text-gray-600 text-sm italic" id="afternoonAdvice">Cargando recomendaci贸n...</p> | |
</div> | |
<!-- Night --> | |
<div class="weather-card bg-white p-6 rounded-lg shadow-md"> | |
<div class="flex items-center mb-4"> | |
<i class="fas fa-moon text-indigo-500 text-2xl mr-3"></i> | |
<h4 class="text-lg font-semibold text-gray-800">Noche</h4> | |
</div> | |
<div class="flex flex-wrap gap-4 justify-center" id="nightClothing"> | |
<!-- Clothing items will be inserted here by JS --> | |
</div> | |
<p class="mt-4 text-gray-600 text-sm italic" id="nightAdvice">Cargando recomendaci贸n...</p> | |
</div> | |
</div> | |
</div> | |
<!-- Additional Tips --> | |
<div class="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6 animate-fadeIn delay-300"> | |
<div class="weather-card bg-white p-6 rounded-lg shadow-md"> | |
<h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center"> | |
<i class="fas fa-umbrella text-blue-500 mr-3"></i> Consejos para la lluvia | |
</h4> | |
<ul class="list-disc pl-5 text-gray-600 space-y-2" id="rainTips"> | |
<!-- Tips will be inserted here by JS --> | |
</ul> | |
</div> | |
<div class="weather-card bg-white p-6 rounded-lg shadow-md"> | |
<h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center"> | |
<i class="fas fa-temperature-high text-red-500 mr-3"></i> Consejos para el calor | |
</h4> | |
<ul class="list-disc pl-5 text-gray-600 space-y-2" id="heatTips"> | |
<!-- Tips will be inserted here by JS --> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<!-- Initial State --> | |
<div id="initialState" class="text-center py-12"> | |
<i class="fas fa-cloud-sun text-5xl text-blue-400 mb-6"></i> | |
<h2 class="text-2xl font-semibold text-gray-700 mb-2">Consulta el clima y recibe recomendaciones de vestimenta</h2> | |
<p class="text-gray-500 mb-6">Ingresa una ciudad en el buscador para conocer el pron贸stico y qu茅 ropa usar seg煤n las condiciones clim谩ticas.</p> | |
<div class="max-w-md mx-auto"> | |
<div class="relative"> | |
<input | |
type="text" | |
placeholder="Ej: Madrid, Buenos Aires, Nueva York" | |
class="w-full px-4 py-3 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-300" | |
id="initialCityInput" | |
> | |
<button | |
class="absolute right-0 top-0 h-full px-6 bg-blue-500 text-white rounded-r-full hover:bg-blue-600 transition-all" | |
id="initialSearchBtn" | |
> | |
Buscar | |
</button> | |
</div> | |
</div> | |
</div> | |
</main> | |
<!-- Footer --> | |
<footer class="bg-gray-800 text-white py-6"> | |
<div class="container mx-auto px-4 text-center"> | |
<p>漏 2023 WeatherWardrobe - Asesor de vestimenta seg煤n el clima</p> | |
<p class="text-gray-400 text-sm mt-2">Datos meteorol贸gicos simulados para prop贸sitos demostrativos</p> | |
</div> | |
</footer> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// Elements | |
const cityInput = document.getElementById('cityInput'); | |
const searchBtn = document.getElementById('searchBtn'); | |
const initialCityInput = document.getElementById('initialCityInput'); | |
const initialSearchBtn = document.getElementById('initialSearchBtn'); | |
const loading = document.getElementById('loading'); | |
const error = document.getElementById('error'); | |
const weatherDisplay = document.getElementById('weatherDisplay'); | |
const initialState = document.getElementById('initialState'); | |
// Event Listeners | |
searchBtn.addEventListener('click', fetchWeather); | |
initialSearchBtn.addEventListener('click', () => { | |
if(initialCityInput.value) { | |
cityInput.value = initialCityInput.value; | |
fetchWeather(); | |
} | |
}); | |
cityInput.addEventListener('keypress', (e) => { | |
if(e.key === 'Enter') fetchWeather(); | |
}); | |
initialCityInput.addEventListener('keypress', (e) => { | |
if(e.key === 'Enter' && initialCityInput.value) { | |
cityInput.value = initialCityInput.value; | |
fetchWeather(); | |
} | |
}); | |
// Mock weather data (since we're not using a real API) | |
function getMockWeatherData(city) { | |
// Base data | |
const baseTemp = Math.floor(Math.random() * 30) + 10; // 10-40掳C | |
const isRaining = Math.random() > 0.7; | |
const isSunny = !isRaining && Math.random() > 0.5; | |
const isCloudy = !isRaining && !isSunny; | |
// Current weather | |
const currentWeather = { | |
city: city, | |
date: new Date().toLocaleDateString('es-ES', { weekday: 'long', day: 'numeric', month: 'long' }), | |
temp: baseTemp, | |
condition: isRaining ? 'Lluvioso' : (isSunny ? 'Soleado' : 'Nublado'), | |
humidity: Math.floor(Math.random() * 50) + 30, // 30-80% | |
wind: Math.floor(Math.random() * 20) + 5, // 5-25 km/h | |
uv: Math.floor(Math.random() * 8) + 1, // 1-8 | |
icon: isRaining ? 'cloud-rain' : (isSunny ? 'sun' : 'cloud') | |
}; | |
// Hourly forecast (next 12 hours) | |
const hourlyForecast = []; | |
const now = new Date(); | |
for(let i = 0; i < 12; i++) { | |
const hour = new Date(now.getTime() + i * 3600000); | |
const hourTemp = baseTemp + (Math.random() * 6 - 3); // 卤3掳 variation | |
const hourRain = isRaining ? Math.random() * 30 : Math.random() * 10; | |
const hourIcon = hourRain > 20 ? 'cloud-rain' : (isSunny ? 'sun' : 'cloud'); | |
hourlyForecast.push({ | |
time: hour.getHours() + ':00', | |
temp: Math.round(hourTemp), | |
rain: Math.round(hourRain), | |
icon: hourIcon | |
}); | |
} | |
// Clothing recommendations | |
const recommendations = { | |
morning: generateClothingRecommendation(baseTemp - 5, isRaining, Math.min(currentWeather.uv, 5)), | |
afternoon: generateClothingRecommendation(baseTemp + 2, isRaining, currentWeather.uv), | |
night: generateClothingRecommendation(baseTemp - 8, isRaining, 1) | |
}; | |
return { | |
current: currentWeather, | |
hourly: hourlyForecast, | |
recommendations: recommendations | |
}; | |
} | |
// Generate clothing recommendations based on weather | |
function generateClothingRecommendation(temp, isRaining, uvIndex) { | |
const clothing = []; | |
const advice = []; | |
// Base clothing | |
if(temp < 10) { | |
clothing.push('coat', 'sweater', 'gloves', 'scarf'); | |
advice.push("Hace mucho fr铆o, abr铆gate bien."); | |
} else if(temp < 18) { | |
clothing.push('jacket', 'long-sleeve'); | |
advice.push("Temperaturas frescas, lleva algo de abrigo."); | |
} else if(temp < 25) { | |
clothing.push('t-shirt', 'light-jacket'); | |
advice.push("Temperaturas agradables, ropa ligera."); | |
} else { | |
clothing.push('tank-top', 'shorts', 'hat'); | |
advice.push("Hace calor, usa ropa fresca y ligera."); | |
} | |
// Rain considerations | |
if(isRaining) { | |
clothing.push('umbrella', 'waterproof-shoes'); | |
advice.push("Lleva paraguas o impermeable, hay probabilidad de lluvia."); | |
} | |
// UV considerations | |
if(uvIndex > 6) { | |
clothing.push('sunglasses', 'hat', 'sunscreen'); | |
advice.push("Alto 铆ndice UV, protege tu piel y ojos."); | |
} else if(uvIndex > 3) { | |
clothing.push('sunglasses', 'sunscreen'); | |
advice.push("Moderado 铆ndice UV, protecci贸n solar recomendada."); | |
} | |
// Footwear | |
if(isRaining) { | |
clothing.push('boots'); | |
} else if(temp > 22) { | |
clothing.push('sandals'); | |
} else { | |
clothing.push('sneakers'); | |
} | |
return { | |
clothing: clothing, | |
advice: advice.join(' ') | |
}; | |
} | |
// Get icon for clothing item | |
function getClothingIcon(item) { | |
const icons = { | |
'coat': 'fa-coat', | |
'sweater': 'fa-shirt', | |
'gloves': 'fa-mitten', | |
'scarf': 'fa-scarf', | |
'jacket': 'fa-jacket', | |
'long-sleeve': 'fa-tshirt', | |
't-shirt': 'fa-tshirt', | |
'light-jacket': 'fa-vest', | |
'tank-top': 'fa-vest', | |
'shorts': 'fa-shorts', | |
'hat': 'fa-hat-cowboy', | |
'umbrella': 'fa-umbrella', | |
'waterproof-shoes': 'fa-shoe-prints', | |
'sunglasses': 'fa-sunglasses', | |
'sunscreen': 'fa-spray-can', | |
'boots': 'fa-boot', | |
'sandals': 'fa-shoe-prints', | |
'sneakers': 'fa-sneaker' | |
}; | |
return icons[item] || 'fa-tshirt'; | |
} | |
// Get weather icon | |
function getWeatherIcon(iconName) { | |
const icons = { | |
'sun': 'fa-sun', | |
'cloud': 'fa-cloud', | |
'cloud-rain': 'fa-cloud-rain' | |
}; | |
return icons[iconName] || 'fa-cloud'; | |
} | |
// Get gradient class based on weather | |
function getWeatherGradient(condition, hour) { | |
const isNight = hour < 6 || hour > 20; | |
if(isNight) return 'night-gradient'; | |
if(condition.includes('Lluvia')) return 'rainy-gradient'; | |
if(condition.includes('Nublado')) return 'cloudy-gradient'; | |
return 'sunny-gradient'; | |
} | |
// Fetch weather data (mock in this case) | |
function fetchWeather() { | |
const city = cityInput.value.trim(); | |
if(!city) return; | |
// Show loading state | |
loading.classList.remove('hidden'); | |
error.classList.add('hidden'); | |
weatherDisplay.classList.add('hidden'); | |
initialState.classList.add('hidden'); | |
// Simulate API delay | |
setTimeout(() => { | |
try { | |
const weatherData = getMockWeatherData(city); | |
displayWeather(weatherData); | |
loading.classList.add('hidden'); | |
weatherDisplay.classList.remove('hidden'); | |
} catch(e) { | |
console.error(e); | |
loading.classList.add('hidden'); | |
error.classList.remove('hidden'); | |
} | |
}, 1000); | |
} | |
// Display weather data | |
function displayWeather(data) { | |
// Current weather | |
const current = data.current; | |
const currentHour = new Date().getHours(); | |
document.getElementById('currentCity').textContent = current.city; | |
document.getElementById('currentDate').textContent = current.date; | |
document.getElementById('currentTemp').textContent = `${current.temp}掳C`; | |
document.getElementById('currentCondition').textContent = current.condition; | |
document.getElementById('currentHumidity').textContent = `${current.humidity}%`; | |
document.getElementById('currentWind').textContent = `${current.wind} km/h`; | |
document.getElementById('currentUV').textContent = current.uv; | |
// Update background based on current weather | |
const weatherCard = document.querySelector('#weatherDisplay .weather-card'); | |
weatherCard.className = weatherCard.className.split(' ').filter(c => !c.includes('gradient')).join(' '); | |
weatherCard.classList.add(getWeatherGradient(current.condition, currentHour)); | |
// Weather icon | |
const weatherIcon = document.querySelector('#weatherDisplay .weather-card i.fas'); | |
weatherIcon.className = `fas ${getWeatherIcon(current.icon)} text-6xl mb-2`; | |
// Hourly forecast | |
const hourlyContainer = document.querySelector('.hourly-forecast'); | |
hourlyContainer.innerHTML = ''; | |
data.hourly.forEach(hour => { | |
const hourEl = document.createElement('div'); | |
hourEl.className = 'flex-shrink-0 weather-card bg-white bg-opacity-20 p-4 rounded-lg text-center text-white'; | |
const hourTime = document.createElement('p'); | |
hourTime.className = 'font-semibold'; | |
hourTime.textContent = hour.time; | |
const hourIcon = document.createElement('i'); | |
hourIcon.className = `fas ${getWeatherIcon(hour.icon)} text-3xl my-2`; | |
const hourTemp = document.createElement('p'); | |
hourTemp.className = 'text-xl font-bold'; | |
hourTemp.textContent = `${hour.temp}掳`; | |
const hourRain = document.createElement('p'); | |
hourRain.className = 'text-sm opacity-80'; | |
hourRain.textContent = `${hour.rain}%`; | |
hourEl.appendChild(hourTime); | |
hourEl.appendChild(hourIcon); | |
hourEl.appendChild(hourTemp); | |
hourEl.appendChild(hourRain); | |
hourlyContainer.appendChild(hourEl); | |
}); | |
// Clothing recommendations | |
const rec = data.recommendations; | |
// Morning | |
const morningContainer = document.getElementById('morningClothing'); | |
morningContainer.innerHTML = ''; | |
rec.morning.clothing.forEach(item => { | |
const itemEl = document.createElement('div'); | |
itemEl.className = 'text-center'; | |
const icon = document.createElement('i'); | |
icon.className = `clothing-icon fas ${getClothingIcon(item)}`; | |
const label = document.createElement('p'); | |
label.className = 'text-xs capitalize'; | |
label.textContent = item.replace('-', ' '); | |
itemEl.appendChild(icon); | |
itemEl.appendChild(label); | |
morningContainer.appendChild(itemEl); | |
}); | |
document.getElementById('morningAdvice').textContent = rec.morning.advice; | |
// Afternoon | |
const afternoonContainer = document.getElementById('afternoonClothing'); | |
afternoonContainer.innerHTML = ''; | |
rec.afternoon.clothing.forEach(item => { | |
const itemEl = document.createElement('div'); | |
itemEl.className = 'text-center'; | |
const icon = document.createElement('i'); | |
icon.className = `clothing-icon fas ${getClothingIcon(item)}`; | |
const label = document.createElement('p'); | |
label.className = 'text-xs capitalize'; | |
label.textContent = item.replace('-', ' '); | |
itemEl.appendChild(icon); | |
itemEl.appendChild(label); | |
afternoonContainer.appendChild(itemEl); | |
}); | |
document.getElementById('afternoonAdvice').textContent = rec.afternoon.advice; | |
// Night | |
const nightContainer = document.getElementById('nightClothing'); | |
nightContainer.innerHTML = ''; | |
rec.night.clothing.forEach(item => { | |
const itemEl = document.createElement('div'); | |
itemEl.className = 'text-center'; | |
const icon = document.createElement('i'); | |
icon.className = `clothing-icon fas ${getClothingIcon(item)}`; | |
const label = document.createElement('p'); | |
label.className = 'text-xs capitalize'; | |
label.textContent = item.replace('-', ' '); | |
itemEl.appendChild(icon); | |
itemEl.appendChild(label); | |
nightContainer.appendChild(itemEl); | |
}); | |
document.getElementById('nightAdvice').textContent = rec.night.advice; | |
// Additional tips | |
const rainTips = document.getElementById('rainTips'); | |
rainTips.innerHTML = ` | |
<li>Usa calzado impermeable para mantener tus pies secos</li> | |
<li>Lleva siempre un paraguas plegable en tu bolso/mochila</li> | |
<li>Considera un impermeable ligero si llover谩 todo el d铆a</li> | |
<li>Protege tus dispositivos electr贸nicos con fundas resistentes al agua</li> | |
`; | |
const heatTips = document.getElementById('heatTips'); | |
heatTips.innerHTML = ` | |
<li>Mantente hidratado, lleva siempre una botella de agua</li> | |
<li>Usa ropa clara y de tejidos transpirables como el lino o algod贸n</li> | |
<li>No olvides aplicar protector solar cada 2 horas</li> | |
<li>Busca la sombra durante las horas de mayor calor (12-16h)</li> | |
`; | |
} | |
}); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 馃К <a href="https://enzostvs-deepsite.hf.space?remix=RedSparkie/qu-ropa-llevar" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |