Recommendation
-Fill up now
-- Local prices are at a 30-day low. Regional wholesale trends indicate a 3p/litre increase starting Monday. Securing fuel today is highly advised. -
-diff --git a/.gitignore b/.gitignore index c7cf1fa..b184fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ yarn-error.log /.nova /.vscode /.zed +/.tmp/ diff --git a/app/Livewire/Public/Fuel/Map.php b/app/Livewire/Public/Fuel/Map.php deleted file mode 100644 index 768a7ee..0000000 --- a/app/Livewire/Public/Fuel/Map.php +++ /dev/null @@ -1,21 +0,0 @@ -dispatch('map-update', results: $results, meta: $meta, radius: $radius); - } - - public function render(): View - { - return view('livewire.public.fuel.map'); - } -} diff --git a/app/Livewire/Public/Fuel/Recommendation.php b/app/Livewire/Public/Fuel/Recommendation.php deleted file mode 100644 index 7140e95..0000000 --- a/app/Livewire/Public/Fuel/Recommendation.php +++ /dev/null @@ -1,23 +0,0 @@ -prediction = $prediction; - } - - public function render(): View - { - return view('livewire.public.fuel.recommendation'); - } -} diff --git a/app/Livewire/Public/Fuel/Search.php b/app/Livewire/Public/Fuel/Search.php deleted file mode 100644 index 372e6f2..0000000 --- a/app/Livewire/Public/Fuel/Search.php +++ /dev/null @@ -1,118 +0,0 @@ -hasSearched) { - $this->findStations(); - } - } - - public function updatedRadius(): void - { - if ($this->hasSearched) { - $this->findStations(); - } - } - - public function updatedSort(): void - { - if ($this->hasSearched) { - $this->findStations(); - } - } - - public function findStations(): void - { - $this->validate(); - - $this->apiError = null; - $this->hasSearched = false; - - $radiusKm = round($this->radius * 1.60934, 2); - - try { - $response = Http::timeout(10) - ->withHeaders(['X-Api-Key' => config('app.api_secret_key')]) - ->get(url('/api/stations'), [ - 'postcode' => $this->search, - 'fuel_type' => $this->fuelType, - 'radius' => $radiusKm, - 'sort' => $this->sort, - ]); - } catch (ConnectionException) { - $this->apiError = 'Unable to fetch stations. Please try again.'; - - return; - } - - if ($response->status() === 422) { - $errors = $response->json('errors', []); - $this->apiError = collect($errors)->flatten()->first() - ?? $response->json('message', 'Validation error.'); - - return; - } - - if (! $response->successful()) { - $this->apiError = 'Unable to fetch stations. Please try again.'; - - return; - } - - $results = $response->json('data', []); - $meta = $response->json('meta', []); - $this->hasSearched = true; - - $prediction = null; - - try { - $predictionResponse = Http::timeout(10) - ->withHeaders(['X-Api-Key' => config('app.api_secret_key')]) - ->get(url('/api/prediction')); - - if ($predictionResponse->successful()) { - $prediction = $predictionResponse->json(); - } - } catch (ConnectionException) { - // Prediction failure is silent — stations are more important - } - - $this->dispatch('stations-found', - results: $results, - meta: $meta, - prediction: $prediction, - radius: $this->radius, - ); - } - - public function render(): View - { - return view('livewire.public.fuel.search'); - } -} diff --git a/app/Livewire/Public/Fuel/StationList.php b/app/Livewire/Public/Fuel/StationList.php deleted file mode 100644 index d8166d1..0000000 --- a/app/Livewire/Public/Fuel/StationList.php +++ /dev/null @@ -1,33 +0,0 @@ -results = $results; - $this->meta = $meta; - $this->radius = $radius; - $this->hasSearched = true; - } - - public function render(): View - { - return view('livewire.public.fuel.station-list'); - } -} diff --git a/app/Livewire/Public/FuelFinder.php b/app/Livewire/Public/FuelFinder.php deleted file mode 100644 index 94cb9e0..0000000 --- a/app/Livewire/Public/FuelFinder.php +++ /dev/null @@ -1,16 +0,0 @@ -validate([ + 'password' => $this->currentPasswordRules(), + ]); + + tap(Auth::user(), $logout(...))->delete(); + + $this->redirect('/', navigate: true); + } +} diff --git a/app/Livewire/Settings/Profile.php b/app/Livewire/Settings/Profile.php new file mode 100644 index 0000000..db7bf31 --- /dev/null +++ b/app/Livewire/Settings/Profile.php @@ -0,0 +1,81 @@ +name = Auth::user()->name; + $this->email = Auth::user()->email; + } + + /** + * Update the profile information for the currently authenticated user. + */ + public function updateProfileInformation(): void + { + $user = Auth::user(); + + $validated = $this->validate($this->profileRules($user->id)); + + $user->fill($validated); + + if ($user->isDirty('email')) { + $user->email_verified_at = null; + } + + $user->save(); + + Flux::toast(variant: 'success', text: __('Profile updated.')); + } + + /** + * Send an email verification notification to the current user. + */ + public function resendVerificationNotification(): void + { + $user = Auth::user(); + + if ($user->hasVerifiedEmail()) { + $this->redirectIntended(default: route('dashboard', absolute: false)); + + return; + } + + $user->sendEmailVerificationNotification(); + + Flux::toast(text: __('A new verification link has been sent to your email address.')); + } + + #[Computed] + public function hasUnverifiedEmail(): bool + { + return Auth::user() instanceof MustVerifyEmail && ! Auth::user()->hasVerifiedEmail(); + } + + #[Computed] + public function showDeleteUser(): bool + { + return ! Auth::user() instanceof MustVerifyEmail + || (Auth::user() instanceof MustVerifyEmail && Auth::user()->hasVerifiedEmail()); + } +} diff --git a/app/Livewire/Settings/Security.php b/app/Livewire/Settings/Security.php new file mode 100644 index 0000000..3edf160 --- /dev/null +++ b/app/Livewire/Settings/Security.php @@ -0,0 +1,225 @@ +canManageTwoFactor = Features::canManageTwoFactorAuthentication(); + + if ($this->canManageTwoFactor) { + if (Fortify::confirmsTwoFactorAuthentication() && is_null(auth()->user()->two_factor_confirmed_at)) { + $disableTwoFactorAuthentication(auth()->user()); + } + + $this->twoFactorEnabled = auth()->user()->hasEnabledTwoFactorAuthentication(); + $this->requiresConfirmation = Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm'); + } + } + + /** + * Update the password for the currently authenticated user. + */ + public function updatePassword(): void + { + try { + $validated = $this->validate([ + 'current_password' => $this->currentPasswordRules(), + 'password' => $this->passwordRules(), + ]); + } catch (ValidationException $e) { + $this->reset('current_password', 'password', 'password_confirmation'); + + throw $e; + } + + Auth::user()->update([ + 'password' => $validated['password'], + ]); + + $this->reset('current_password', 'password', 'password_confirmation'); + + Flux::toast(variant: 'success', text: __('Password updated.')); + } + + /** + * Enable two-factor authentication for the user. + */ + public function enable(EnableTwoFactorAuthentication $enableTwoFactorAuthentication): void + { + $enableTwoFactorAuthentication(auth()->user()); + + if (! $this->requiresConfirmation) { + $this->twoFactorEnabled = auth()->user()->hasEnabledTwoFactorAuthentication(); + } + + $this->loadSetupData(); + + $this->showModal = true; + } + + /** + * Load the two-factor authentication setup data for the user. + */ + private function loadSetupData(): void + { + $user = auth()->user(); + + try { + $this->qrCodeSvg = $user?->twoFactorQrCodeSvg(); + $this->manualSetupKey = decrypt($user->two_factor_secret); + } catch (Exception) { + $this->addError('setupData', 'Failed to fetch setup data.'); + + $this->reset('qrCodeSvg', 'manualSetupKey'); + } + } + + /** + * Show the two-factor verification step if necessary. + */ + public function showVerificationIfNecessary(): void + { + if ($this->requiresConfirmation) { + $this->showVerificationStep = true; + + $this->resetErrorBag(); + + return; + } + + $this->closeModal(); + } + + /** + * Close the two-factor authentication modal. + */ + public function closeModal(): void + { + $this->reset( + 'code', + 'manualSetupKey', + 'qrCodeSvg', + 'showModal', + 'showVerificationStep', + ); + + $this->resetErrorBag(); + + if (! $this->requiresConfirmation) { + $this->twoFactorEnabled = auth()->user()->hasEnabledTwoFactorAuthentication(); + } + } + + /** + * Confirm two-factor authentication for the user. + */ + public function confirmTwoFactor(ConfirmTwoFactorAuthentication $confirmTwoFactorAuthentication): void + { + $this->validate(); + + $confirmTwoFactorAuthentication(auth()->user(), $this->code); + + $this->closeModal(); + + $this->twoFactorEnabled = true; + } + + /** + * Reset two-factor verification state. + */ + public function resetVerification(): void + { + $this->reset('code', 'showVerificationStep'); + + $this->resetErrorBag(); + } + + /** + * Disable two-factor authentication for the user. + */ + public function disable(DisableTwoFactorAuthentication $disableTwoFactorAuthentication): void + { + $disableTwoFactorAuthentication(auth()->user()); + + $this->twoFactorEnabled = false; + } + + /** + * Get the current modal configuration state. + */ + public function getModalConfigProperty(): array + { + if ($this->twoFactorEnabled) { + return [ + 'title' => __('Two-factor authentication enabled'), + 'description' => __('Two-factor authentication is now enabled. Scan the QR code or enter the setup key in your authenticator app.'), + 'buttonText' => __('Close'), + ]; + } + + if ($this->showVerificationStep) { + return [ + 'title' => __('Verify authentication code'), + 'description' => __('Enter the 6-digit code from your authenticator app.'), + 'buttonText' => __('Continue'), + ]; + } + + return [ + 'title' => __('Enable two-factor authentication'), + 'description' => __('To finish enabling two-factor authentication, scan the QR code or enter the setup key in your authenticator app.'), + 'buttonText' => __('Continue'), + ]; + } +} diff --git a/app/Livewire/Settings/TwoFactor/RecoveryCodes.php b/app/Livewire/Settings/TwoFactor/RecoveryCodes.php new file mode 100644 index 0000000..ae0acba --- /dev/null +++ b/app/Livewire/Settings/TwoFactor/RecoveryCodes.php @@ -0,0 +1,50 @@ +loadRecoveryCodes(); + } + + /** + * Load the recovery codes for the user. + */ + private function loadRecoveryCodes(): void + { + $user = auth()->user(); + + if ($user->hasEnabledTwoFactorAuthentication() && $user->two_factor_recovery_codes) { + try { + $this->recoveryCodes = json_decode(decrypt($user->two_factor_recovery_codes), true); + } catch (Exception) { + $this->addError('recoveryCodes', 'Failed to load recovery codes'); + + $this->recoveryCodes = []; + } + } + } + + /** + * Generate new recovery codes for the user. + */ + public function regenerateRecoveryCodes(GenerateNewRecoveryCodes $generateNewRecoveryCodes): void + { + $generateNewRecoveryCodes(auth()->user()); + + $this->loadRecoveryCodes(); + } +} diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index 6ef37bd..44e57aa 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -45,13 +45,13 @@ class FortifyServiceProvider extends ServiceProvider */ private function configureViews(): void { - Fortify::loginView(fn () => view('pages::auth.login')); - Fortify::verifyEmailView(fn () => view('pages::auth.verify-email')); - Fortify::twoFactorChallengeView(fn () => view('pages::auth.two-factor-challenge')); - Fortify::confirmPasswordView(fn () => view('pages::auth.confirm-password')); - Fortify::registerView(fn () => view('pages::auth.register')); - Fortify::resetPasswordView(fn () => view('pages::auth.reset-password')); - Fortify::requestPasswordResetLinkView(fn () => view('pages::auth.forgot-password')); + Fortify::loginView(fn () => view('livewire.auth.login')); + Fortify::verifyEmailView(fn () => view('livewire.auth.verify-email')); + Fortify::twoFactorChallengeView(fn () => view('livewire.auth.two-factor-challenge')); + Fortify::confirmPasswordView(fn () => view('livewire.auth.confirm-password')); + Fortify::registerView(fn () => view('livewire.auth.register')); + Fortify::resetPasswordView(fn () => view('livewire.auth.reset-password')); + Fortify::requestPasswordResetLinkView(fn () => view('livewire.auth.forgot-password')); } /** diff --git a/package-lock.json b/package-lock.json index a5a4987..a45d6ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,9 +103,9 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", + "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", "license": "MIT", "optional": true, "dependencies": { @@ -121,18 +121,18 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", "cpu": [ "arm64" ], @@ -146,9 +146,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", "cpu": [ "arm64" ], @@ -162,9 +162,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", "cpu": [ "x64" ], @@ -178,9 +178,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", "cpu": [ "x64" ], @@ -194,9 +194,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", "cpu": [ "arm" ], @@ -210,9 +210,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", "cpu": [ "arm64" ], @@ -229,9 +229,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", "cpu": [ "arm64" ], @@ -248,9 +248,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", "cpu": [ "ppc64" ], @@ -267,9 +267,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", "cpu": [ "s390x" ], @@ -286,9 +286,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", "cpu": [ "x64" ], @@ -305,9 +305,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", "cpu": [ "x64" ], @@ -324,9 +324,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", "cpu": [ "arm64" ], @@ -340,25 +340,27 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", "cpu": [ "arm64" ], @@ -372,9 +374,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", "cpu": [ "x64" ], @@ -388,9 +390,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", "license": "MIT" }, "node_modules/@rollup/rollup-linux-x64-gnu": { @@ -749,9 +751,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.14", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.14.tgz", - "integrity": "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==", + "version": "2.10.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -794,9 +796,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001784", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", - "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", + "version": "1.0.30001787", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", + "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", "funding": [ { "type": "opencollective", @@ -907,9 +909,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.331", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", - "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "version": "1.5.334", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", + "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -1377,9 +1379,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "funding": [ { "type": "opencollective", @@ -1420,13 +1422,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" @@ -1435,21 +1437,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" } }, "node_modules/rxjs": { @@ -1543,13 +1545,13 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -1604,15 +1606,15 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "bin": { @@ -1630,7 +1632,7 @@ "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", diff --git a/resources/css/app.css b/resources/css/app.css index 8b8bbdc..ff130f6 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,4 +1,3 @@ -@import 'leaflet/dist/leaflet.css'; @import 'tailwindcss'; @import '../../vendor/livewire/flux/dist/flux.css'; @@ -9,7 +8,6 @@ @custom-variant dark (&:where(.dark, .dark *)); -/* Remap Flux's zinc scale to FuelAlert's warm brown neutrals */ @theme { --font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; @@ -55,18 +53,19 @@ --font-display: 'Manrope', ui-sans-serif, system-ui, sans-serif; } -@layer base { - h1, h2, h3, h4 { - font-family: var(--font-display); - letter-spacing: -0.02em; +@layer theme { + .dark { + --color-accent: var(--color-white); + --color-accent-content: var(--color-white); + --color-accent-foreground: var(--color-neutral-800); } } @layer utilities { .hero-gradient { background: - radial-gradient(circle at top right, color-mix(in oklch, var(--color-primary) 8%, transparent), transparent 50%), - radial-gradient(circle at bottom left, color-mix(in oklch, var(--color-primary) 5%, transparent), transparent 40%); + radial-gradient(circle at top right, color-mix(in oklch, var(--color-primary) 8%, transparent), transparent 50%), + radial-gradient(circle at bottom left, color-mix(in oklch, var(--color-primary) 5%, transparent), transparent 40%); } .glass-card { diff --git a/resources/views/components/action-message.blade.php b/resources/views/components/action-message.blade.php deleted file mode 100644 index d313ee6..0000000 --- a/resources/views/components/action-message.blade.php +++ /dev/null @@ -1,14 +0,0 @@ -@props([ - 'on', -]) - -
diff --git a/resources/views/pages/settings/layout.blade.php b/resources/views/components/settings/layout.blade.php similarity index 100% rename from resources/views/pages/settings/layout.blade.php rename to resources/views/components/settings/layout.blade.php diff --git a/resources/views/homepage.blade.php b/resources/views/homepage.blade.php deleted file mode 100644 index 7b8f24b..0000000 --- a/resources/views/homepage.blade.php +++ /dev/null @@ -1,620 +0,0 @@ - - - - - -Recommendation
-- Local prices are at a 30-day low. Regional wholesale trends indicate a 3p/litre increase starting Monday. Securing fuel today is highly advised. -
-- Join 50,000+ UK drivers using real-time insights to find the cheapest petrol and time their fill-ups perfectly. -
- - - -Stop guessing when to fill up. Our engine analyses thousands of data points daily to save you money.
-Enter your postcode to find every forecourt within a 5–20 mile radius instantly.
-Our engine compares local prices against national wholesale trends to give you a Fill Up / Wait recommendation.
-Navigate to the cheapest station and fill up with confidence knowing you've secured the best price.
-Verified daily prices from thousands of UK forecourts updated every 15 minutes.
-Proprietary 14-day forecasts for petrol and diesel trends near you.
-Track local supermarkets — they set the floor price that independents must follow.
-Get notified by email, WhatsApp, or SMS when local prices hit your target.
-Whether you're a daily commuter, a delivery professional, or just planning a weekend road trip, FuelAlert gives you the edge at the pump.
-Save hundreds for less than the cost of a coffee.
-Join thousands of UK drivers saving every single month.
-"I used to just go to the nearest station. Now I check FuelAlert and there's always a station 2 miles away that's 5p cheaper. Over a month it adds up to a free tank per year."
-James R.
-Daily Commuter
-"The predictions are eerily accurate. I was going to fill up Friday, FuelAlert said wait until Monday. Sure enough, my local Tesco dropped 3p. Brilliant."
-Sarah M.
-Delivery Driver
-Sign up free today and never pay over the odds for fuel again.
- -