From 2b41c3ed440fe1f937676a96ec75b6bc4c653aef Mon Sep 17 00:00:00 2001 From: josh Date: Tue, 11 Aug 2026 20:52:21 +1000 Subject: [PATCH] Add London Airport and Million Mile Challenges --- .../Controllers/UserProfileController.php | 7 + app/Models/Airport.php | 4 + .../Achievements/AchievementService.php | 2 + .../Achievements/Checkers/DistanceChecker.php | 26 ++++ .../Checkers/FunChallengesChecker.php | 15 ++ .../Checkers/GeneralFlyingChecker.php | 8 - ...26_08_11_100455_add_london_achievement.php | 87 +++++++++++ .../Components/FlightsGoneBy/MainHeader.vue | 3 +- .../fun_challenges.all_london_airports.vue | 139 ++++++++++++++++++ .../js/Pages/Profile/UserAchievement.vue | 2 + 10 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 app/Services/Achievements/Checkers/DistanceChecker.php create mode 100644 database/migrations/2026_08_11_100455_add_london_achievement.php create mode 100644 resources/js/Pages/Profile/Achievements/fun_challenges.all_london_airports.vue diff --git a/app/Http/Controllers/UserProfileController.php b/app/Http/Controllers/UserProfileController.php index 33c31c2..d08cdd2 100644 --- a/app/Http/Controllers/UserProfileController.php +++ b/app/Http/Controllers/UserProfileController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; use App\Models\Achievement; use App\Models\Aircraft; +use App\Models\Airport; use App\Models\Alliance; use App\Models\Continent; use App\Models\Country; @@ -199,6 +200,11 @@ class UserProfileController extends Controller default => [], }; + $londonAirports = match($achievement->internal_name){ + 'fun_challenges.all_london_airports' => Airport::LONDON_AIRPORTS, + default => [], + }; + $canView = Gate::allows('viewProfileData', $user); return Inertia::render('Profile/UserAchievement', [ @@ -212,6 +218,7 @@ class UserProfileController extends Controller 'airlines' => $airlines, 'continents' => $continents, 'aircraft_families' => $aircraftFamilies, + 'london_airports' => $londonAirports, 'achievementCount' => $user->unlockedAchievements()->count(), 'majorAlliances' => Alliance::whereIn('internal_name', $majorAlliances)->get(), 'canView' => $canView, diff --git a/app/Models/Airport.php b/app/Models/Airport.php index faf43b8..2327e03 100644 --- a/app/Models/Airport.php +++ b/app/Models/Airport.php @@ -33,6 +33,10 @@ class Airport extends Model 'display_name', ]; + const array LONDON_AIRPORTS = [ + 'LHR', 'LGW', 'STN', 'LTN', 'LCY', 'SEN', + ]; + protected function displayName() : Attribute{ return Attribute::make( get: function () { diff --git a/app/Services/Achievements/AchievementService.php b/app/Services/Achievements/AchievementService.php index 6a60f03..b2905ce 100644 --- a/app/Services/Achievements/AchievementService.php +++ b/app/Services/Achievements/AchievementService.php @@ -12,6 +12,7 @@ use App\Services\Achievements\Checkers\AchievementCheckerInterface; use App\Services\Achievements\Checkers\AircraftChecker; use App\Services\Achievements\Checkers\CountriesAndContinentsChecker; use App\Services\Achievements\Checkers\AirlinesAndAlliancesChecker; +use App\Services\Achievements\Checkers\DistanceChecker; use App\Services\Achievements\Checkers\FunChallengesChecker; use App\Services\Achievements\Checkers\GeneralFlyingChecker; use Illuminate\Support\Collection; @@ -30,6 +31,7 @@ class AchievementService AircraftChecker::class, AirlinesAndAlliancesChecker::class, FunChallengesChecker::class, + DistanceChecker::class, ]; public function __construct() diff --git a/app/Services/Achievements/Checkers/DistanceChecker.php b/app/Services/Achievements/Checkers/DistanceChecker.php new file mode 100644 index 0000000..f18458b --- /dev/null +++ b/app/Services/Achievements/Checkers/DistanceChecker.php @@ -0,0 +1,26 @@ + + */ + $flights = $this->flights(); + + $totalDistance = $flights->sum('distance'); + + $this->awardProgress((int) $totalDistance, 'distance.circumference_of_the_earth'); + $this->awardProgress((int) $totalDistance, 'distance.to_the_moon'); + $this->awardProgress((int) $totalDistance, 'distance.gigametre'); + $this->awardProgress((int) $totalDistance, 'distance.million_miles'); + + } +} diff --git a/app/Services/Achievements/Checkers/FunChallengesChecker.php b/app/Services/Achievements/Checkers/FunChallengesChecker.php index ae6f777..df57cca 100644 --- a/app/Services/Achievements/Checkers/FunChallengesChecker.php +++ b/app/Services/Achievements/Checkers/FunChallengesChecker.php @@ -2,6 +2,7 @@ namespace App\Services\Achievements\Checkers; +use App\Models\Airport; use App\Models\User; use App\Models\UserFlight; @@ -131,5 +132,19 @@ class FunChallengesChecker extends BaseChecker ->count(); $this->awardProgress($visitedCanadianProvinces, 'fun_challenges.canadian_provinces'); + + // --- London Airports --- + $visitedLondonAirports = $flights + ->flatMap(fn(UserFlight $f) => [ + $f->departureAirport?->iata_code, + $f->arrivalAirport?->iata_code, + ]) + ->filter() + ->map(fn($code) => strtoupper($code)) + ->filter(fn($code) => in_array($code, Airport::LONDON_AIRPORTS)) + ->unique() + ->count(); + + $this->awardProgress($visitedLondonAirports, 'fun_challenges.all_london_airports'); } } diff --git a/app/Services/Achievements/Checkers/GeneralFlyingChecker.php b/app/Services/Achievements/Checkers/GeneralFlyingChecker.php index e17fa0f..d20b6a2 100644 --- a/app/Services/Achievements/Checkers/GeneralFlyingChecker.php +++ b/app/Services/Achievements/Checkers/GeneralFlyingChecker.php @@ -68,14 +68,6 @@ class GeneralFlyingChecker extends BaseChecker 'general_flying.domestic_two_countries' ); - // --- Progressive achievements --- - - $totalDistance = $flights->sum('distance'); - - $this->awardProgress((int) $totalDistance, 'general_flying.circumference_of_the_earth'); - $this->awardProgress((int) $totalDistance, 'general_flying.to_the_moon'); - $this->awardProgress((int) $totalDistance, 'general_flying.gigametre'); - $this->awardProgress($count,'general_flying.10_flights'); $this->awardProgress($count,'general_flying.50_flights'); $this->awardProgress($count,'general_flying.100_flights'); diff --git a/database/migrations/2026_08_11_100455_add_london_achievement.php b/database/migrations/2026_08_11_100455_add_london_achievement.php new file mode 100644 index 0000000..cb7e25d --- /dev/null +++ b/database/migrations/2026_08_11_100455_add_london_achievement.php @@ -0,0 +1,87 @@ +first(); + $near_impossible = AchievementDifficulty::where('internal_name', 'near_impossible')->first(); + $fun_challenges = AchievementCategory::where('internal_name', 'fun_challenges')->first(); + + Achievement::create([ + 'name' => 'London Calling', + 'internal_name' => 'fun_challenges.all_london_airports', + 'short_description' => 'Fly to all official London airports', + 'progressive' => true, + 'difficulty_description' => 'Much more achievable if you live in London!', + 'threshold' => 6, + 'achievement_category_id' => $fun_challenges->id, + 'achievement_difficulty_id' => $hard->id, + 'has_page' => true, + 'sort_order' => 10, + 'icon' => 'standard_achievement.png', + 'long_description' => '', + ]); + + $distance = AchievementCategory::create([ + 'name' => 'Distance', + 'internal_name' => 'distance', + 'description' => 'Achievements that are based on distance' + ]); + + Achievement::create([ + 'name' => 'Million Miler', + 'internal_name' => 'distance.million_miles', + 'short_description' => 'Fly one million miles', + 'progressive' => true, + 'threshold' => 1609344, + 'achievement_category_id' => $distance->id, + 'achievement_difficulty_id' => $near_impossible->id, + 'has_page' => false, + 'sort_order' => 4, + 'icon' => 'standard_achievement.png', + 'long_description' => '', + ]); + + Achievement::whereInternalName('general_flying.gigametre')->update([ + 'internal_name' => 'distance.gigametre', + 'achievement_category_id' => $distance->id, + 'sort_order' => 3 + ]); + + Achievement::whereInternalName('general_flying.circumference_of_the_earth')->update([ + 'internal_name' => 'distance.circumference_of_the_earth', + 'achievement_category_id' => $distance->id, + 'sort_order' => 1 + ]); + + Achievement::whereInternalName('general_flying.to_the_moon')->update([ + 'internal_name' => 'distance.to_the_moon', + 'achievement_category_id' => $distance->id, + 'sort_order' => 2 + ]); + + foreach(User::all() as $user){ + $user->calculateAchievements(); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/resources/js/Components/FlightsGoneBy/MainHeader.vue b/resources/js/Components/FlightsGoneBy/MainHeader.vue index 44f1479..24dd7e6 100644 --- a/resources/js/Components/FlightsGoneBy/MainHeader.vue +++ b/resources/js/Components/FlightsGoneBy/MainHeader.vue @@ -31,7 +31,7 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside)) - + @@ -371,5 +371,6 @@ header { @media (max-width: 600px) { .nav-desktop { display: none; } .hamburger { display: flex; } + .user-search-box { display: none; } } diff --git a/resources/js/Pages/Profile/Achievements/fun_challenges.all_london_airports.vue b/resources/js/Pages/Profile/Achievements/fun_challenges.all_london_airports.vue new file mode 100644 index 0000000..b97a7c4 --- /dev/null +++ b/resources/js/Pages/Profile/Achievements/fun_challenges.all_london_airports.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/resources/js/Pages/Profile/UserAchievement.vue b/resources/js/Pages/Profile/UserAchievement.vue index 321a3ba..1870a7d 100644 --- a/resources/js/Pages/Profile/UserAchievement.vue +++ b/resources/js/Pages/Profile/UserAchievement.vue @@ -25,6 +25,7 @@ const props = defineProps<{ regions: Region[] alliance: Alliance | null airlines: Airline[] + london_airports: string[] continents: Continent[] aircraft_families: Record majorAlliances: Alliance[] @@ -119,6 +120,7 @@ const unlocked = computed(() => { :continents="continents" :families="aircraft_families" :major-alliances="majorAlliances" + :london-airports="london_airports" />