background now updates to reflect weather
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\WeatherReport;
|
use App\Models\WeatherReport;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
|
|
||||||
@@ -52,6 +53,11 @@ class WeatherController extends Controller
|
|||||||
'detailedForecast' => $period->detailed_forecast,
|
'detailedForecast' => $period->detailed_forecast,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$background = $this->getBackgroundForForecast(
|
||||||
|
$currentPeriod?->short_forecast,
|
||||||
|
$currentPeriod?->is_daytime ?? true
|
||||||
|
);
|
||||||
|
|
||||||
return Inertia::render('Weather', [
|
return Inertia::render('Weather', [
|
||||||
'current' => $currentPeriod ? [
|
'current' => $currentPeriod ? [
|
||||||
'temperature' => $currentPeriod->temperature,
|
'temperature' => $currentPeriod->temperature,
|
||||||
@@ -74,9 +80,79 @@ class WeatherController extends Controller
|
|||||||
'name' => 'Utah County',
|
'name' => 'Utah County',
|
||||||
],
|
],
|
||||||
'reportedAt' => $latestHourly?->reported_at?->format('M d, Y H:i'),
|
'reportedAt' => $latestHourly?->reported_at?->format('M d, Y H:i'),
|
||||||
|
'background' => $background,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{imageUrl: string|null, licenseHtml: string|null}
|
||||||
|
*/
|
||||||
|
private function getBackgroundForForecast(?string $forecast, bool $isDaytime): array
|
||||||
|
{
|
||||||
|
$folder = $this->mapForecastToFolder($forecast);
|
||||||
|
$timeOfDay = $isDaytime ? 'day' : 'night';
|
||||||
|
$basePath = storage_path('app/public/backgrounds/'.$folder);
|
||||||
|
|
||||||
|
if (! File::isDirectory($basePath)) {
|
||||||
|
return ['imageUrl' => null, 'licenseHtml' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = File::files($basePath);
|
||||||
|
$pattern = '/^'.$timeOfDay.'_[a-zA-Z0-9_-]+\.jpg$/';
|
||||||
|
|
||||||
|
$imageFiles = collect($files)
|
||||||
|
->filter(fn ($file) => preg_match($pattern, $file->getFilename()))
|
||||||
|
->values();
|
||||||
|
|
||||||
|
if ($imageFiles->isEmpty()) {
|
||||||
|
$fallbackPattern = '/^(day|night)_[a-zA-Z0-9_-]+\.jpg$/';
|
||||||
|
$imageFiles = collect($files)
|
||||||
|
->filter(fn ($file) => preg_match($fallbackPattern, $file->getFilename()))
|
||||||
|
->values();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($imageFiles->isEmpty()) {
|
||||||
|
return ['imageUrl' => null, 'licenseHtml' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedImage = $imageFiles->random();
|
||||||
|
$imageFilename = $selectedImage->getFilename();
|
||||||
|
$imageUrl = '/storage/backgrounds/'.$folder.'/'.$imageFilename;
|
||||||
|
|
||||||
|
$licenseFilename = preg_replace('/\.jpg$/', '_license.html', $imageFilename);
|
||||||
|
$licensePath = $basePath.'/'.$licenseFilename;
|
||||||
|
$licenseHtml = null;
|
||||||
|
|
||||||
|
if (File::exists($licensePath)) {
|
||||||
|
$licenseHtml = trim(File::get($licensePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'imageUrl' => $imageUrl,
|
||||||
|
'licenseHtml' => $licenseHtml,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapForecastToFolder(?string $forecast): string
|
||||||
|
{
|
||||||
|
if (! $forecast) {
|
||||||
|
return 'cloudy';
|
||||||
|
}
|
||||||
|
|
||||||
|
$forecast = strtolower($forecast);
|
||||||
|
|
||||||
|
return match (true) {
|
||||||
|
str_contains($forecast, 'thunder') || str_contains($forecast, 'storm') => 'stormy',
|
||||||
|
str_contains($forecast, 'snow') => 'snowy',
|
||||||
|
str_contains($forecast, 'rain') || str_contains($forecast, 'shower') || str_contains($forecast, 'drizzle') => 'rainy',
|
||||||
|
str_contains($forecast, 'wind') => 'windy',
|
||||||
|
str_contains($forecast, 'sunny') || str_contains($forecast, 'clear') => 'clear',
|
||||||
|
str_contains($forecast, 'cloud') || str_contains($forecast, 'overcast') => 'cloudy',
|
||||||
|
str_contains($forecast, 'fog') || str_contains($forecast, 'mist') => 'cloudy',
|
||||||
|
default => 'cloudy',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private function mapIconToEmoji(?string $forecast): string
|
private function mapIconToEmoji(?string $forecast): string
|
||||||
{
|
{
|
||||||
if (! $forecast) {
|
if (! $forecast) {
|
||||||
|
|||||||
@@ -44,12 +44,18 @@ interface Location {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Background {
|
||||||
|
imageUrl: string | null;
|
||||||
|
licenseHtml: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
current: CurrentWeather | null;
|
current: CurrentWeather | null;
|
||||||
hourlyForecast: HourlyForecast[];
|
hourlyForecast: HourlyForecast[];
|
||||||
weeklyForecast: WeeklyForecast[];
|
weeklyForecast: WeeklyForecast[];
|
||||||
location: Location;
|
location: Location;
|
||||||
reportedAt: string | null;
|
reportedAt: string | null;
|
||||||
|
background: Background;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const weatherIcons: Record<string, string> = {
|
const weatherIcons: Record<string, string> = {
|
||||||
@@ -88,9 +94,16 @@ function parseWindSpeed(windSpeed: string | null): number {
|
|||||||
<Head title="Weather" />
|
<Head title="Weather" />
|
||||||
|
|
||||||
<div class="relative min-h-screen overflow-hidden bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
|
<div class="relative min-h-screen overflow-hidden bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
|
||||||
<!-- Background atmosphere effect -->
|
<!-- Dynamic background image based on weather -->
|
||||||
<div class="absolute inset-0 bg-[url('/images/clouds.jpg')] bg-cover bg-center opacity-20"></div>
|
<div
|
||||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent via-slate-900/50 to-slate-900"></div>
|
v-if="background.imageUrl"
|
||||||
|
class="absolute inset-0 bg-cover bg-center transition-opacity duration-1000"
|
||||||
|
:style="{ backgroundImage: `url(${background.imageUrl})` }"
|
||||||
|
></div>
|
||||||
|
<!-- Fallback gradient background -->
|
||||||
|
<div v-else class="absolute inset-0 bg-gradient-to-br from-slate-800 via-slate-700 to-slate-900"></div>
|
||||||
|
<!-- Overlay for better text readability -->
|
||||||
|
<div class="absolute inset-0 bg-gradient-to-b from-black/40 via-black/50 to-black/70"></div>
|
||||||
|
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<div class="relative z-10 mx-auto min-h-screen max-w-6xl p-6 lg:p-8">
|
<div class="relative z-10 mx-auto min-h-screen max-w-6xl p-6 lg:p-8">
|
||||||
@@ -277,5 +290,13 @@ function parseWindSpeed(windSpeed: string | null): number {
|
|||||||
Last updated: {{ reportedAt }}
|
Last updated: {{ reportedAt }}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Image attribution (bottom right corner) -->
|
||||||
|
<div
|
||||||
|
v-if="background.licenseHtml"
|
||||||
|
class="absolute bottom-4 right-4 z-20 max-w-xs rounded-lg bg-black/40 px-3 py-2 text-xs text-white/60 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
<span v-html="background.licenseHtml"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user