diff --git a/app/Http/Controllers/WeatherController.php b/app/Http/Controllers/WeatherController.php
index 72f21bd..92035c4 100644
--- a/app/Http/Controllers/WeatherController.php
+++ b/app/Http/Controllers/WeatherController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Models\WeatherReport;
+use Illuminate\Support\Facades\File;
use Inertia\Inertia;
use Inertia\Response;
@@ -52,6 +53,11 @@ class WeatherController extends Controller
'detailedForecast' => $period->detailed_forecast,
]);
+ $background = $this->getBackgroundForForecast(
+ $currentPeriod?->short_forecast,
+ $currentPeriod?->is_daytime ?? true
+ );
+
return Inertia::render('Weather', [
'current' => $currentPeriod ? [
'temperature' => $currentPeriod->temperature,
@@ -74,9 +80,79 @@ class WeatherController extends Controller
'name' => 'Utah County',
],
'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
{
if (! $forecast) {
diff --git a/resources/js/pages/Weather.vue b/resources/js/pages/Weather.vue
index e7e3a5d..ffb0ef5 100644
--- a/resources/js/pages/Weather.vue
+++ b/resources/js/pages/Weather.vue
@@ -44,12 +44,18 @@ interface Location {
name: string;
}
+interface Background {
+ imageUrl: string | null;
+ licenseHtml: string | null;
+}
+
const props = defineProps<{
current: CurrentWeather | null;
hourlyForecast: HourlyForecast[];
weeklyForecast: WeeklyForecast[];
location: Location;
reportedAt: string | null;
+ background: Background;
}>();
const weatherIcons: Record