From 5a0e6e416573bed559caa099f9e7fbecd93056cd Mon Sep 17 00:00:00 2001 From: Doar Mihai Date: Sun, 15 Feb 2026 16:24:47 +0200 Subject: [PATCH] feature flags and a bit on the design --- app/Features/UpVotes.php | 16 +++ app/Features/WaitlistPage.php | 19 +++ app/Http/Controllers/WaitlistController.php | 37 +++++ app/Mail/WaitlistWelcome.php | 55 ++++++++ app/Models/Product.php | 8 ++ app/Models/WaitlistEntry.php | 17 +++ composer.json | 1 + composer.lock | 80 ++++++++++- config/pennant.php | 44 ++++++ ...026_02_15_113143_create_features_table.php | 32 +++++ ...5_121201_create_waitlist_entries_table.php | 29 ++++ resources/views/auth/login.blade.php | 20 +-- .../views/auth/passwords/reset.blade.php | 16 +-- resources/views/auth/register.blade.php | 22 +-- .../components/ui/theme-toggle.blade.php | 17 ++- .../views/emails/waitlist-welcome.blade.php | 75 ++++++++++ resources/views/products/index.blade.php | 6 +- resources/views/products/show.blade.php | 72 +++++----- resources/views/profile/show.blade.php | 56 ++++---- resources/views/waitlist/index.blade.php | 132 ++++++++++++++++++ resources/views/welcome.blade.php | 6 +- routes/web.php | 5 + 22 files changed, 663 insertions(+), 102 deletions(-) create mode 100644 app/Features/UpVotes.php create mode 100644 app/Features/WaitlistPage.php create mode 100644 app/Http/Controllers/WaitlistController.php create mode 100644 app/Mail/WaitlistWelcome.php create mode 100644 app/Models/WaitlistEntry.php create mode 100644 config/pennant.php create mode 100644 database/migrations/2026_02_15_113143_create_features_table.php create mode 100644 database/migrations/2026_02_15_121201_create_waitlist_entries_table.php create mode 100644 resources/views/emails/waitlist-welcome.blade.php create mode 100644 resources/views/waitlist/index.blade.php diff --git a/app/Features/UpVotes.php b/app/Features/UpVotes.php new file mode 100644 index 0000000..2480859 --- /dev/null +++ b/app/Features/UpVotes.php @@ -0,0 +1,16 @@ +validate([ + 'email' => 'required|email|unique:waitlist_entries,email', + ]); + + try { + $entry = WaitlistEntry::create([ + 'email' => $request->email, + ]); + + // Send welcome email + Mail::to($entry->email)->send(new WaitlistWelcome($entry)); + + return redirect()->back()->with('success', 'Thank you for joining our waitlist! Check your email for confirmation.'); + } catch (\Exception $e) { + return redirect()->back()->with('error', 'Something went wrong. Please try again.'); + } + } +} diff --git a/app/Mail/WaitlistWelcome.php b/app/Mail/WaitlistWelcome.php new file mode 100644 index 0000000..b6190b0 --- /dev/null +++ b/app/Mail/WaitlistWelcome.php @@ -0,0 +1,55 @@ + + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Models/Product.php b/app/Models/Product.php index 11250f8..f8a66b4 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -40,6 +40,14 @@ class Product extends Model ]; } + /** + * Get the route key for the model. + */ + public function getRouteKeyName(): string + { + return 'slug'; + } + /** * Boot the model. */ diff --git a/app/Models/WaitlistEntry.php b/app/Models/WaitlistEntry.php new file mode 100644 index 0000000..ee949c6 --- /dev/null +++ b/app/Models/WaitlistEntry.php @@ -0,0 +1,17 @@ + 'boolean', + ]; +} diff --git a/composer.json b/composer.json index 8b43790..ff08f96 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", + "laravel/pennant": "^1.19", "laravel/tinker": "^2.10.1", "laravel/ui": "^4.6" }, diff --git a/composer.lock b/composer.lock index e29af17..8490e3c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "776ad1a776065eb04993dfac52d589d9", + "content-hash": "0b95f28c522a9fba5c9596c0f5187864", "packages": [ { "name": "brick/math", @@ -1274,6 +1274,82 @@ }, "time": "2026-01-28T03:40:49+00:00" }, + { + "name": "laravel/pennant", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pennant.git", + "reference": "178c654f10db832161b28c872f4b10035000c054" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pennant/zipball/178c654f10db832161b28c872f4b10035000c054", + "reference": "178c654f10db832161b28c872f4b10035000c054", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.0|^11.0|^12.0", + "illuminate/container": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0", + "illuminate/queue": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1", + "symfony/console": "^6.0|^7.0", + "symfony/finder": "^6.0|^7.0" + }, + "require-dev": { + "laravel/octane": "^1.4|^2.0", + "orchestra/testbench": "^8.36|^9.15|^10.8", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Feature": "Laravel\\Pennant\\Feature" + }, + "providers": [ + "Laravel\\Pennant\\PennantServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Pennant\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "A simple, lightweight library for managing feature flags.", + "homepage": "https://github.com/laravel/pennant", + "keywords": [ + "feature", + "flags", + "laravel", + "pennant" + ], + "support": { + "issues": "https://github.com/laravel/pennant/issues", + "source": "https://github.com/laravel/pennant" + }, + "time": "2026-02-02T05:44:53+00:00" + }, { "name": "laravel/prompts", "version": "v0.3.11", @@ -9405,5 +9481,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/pennant.php b/config/pennant.php new file mode 100644 index 0000000..d03ebfb --- /dev/null +++ b/config/pennant.php @@ -0,0 +1,44 @@ + env('PENNANT_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Pennant Stores + |-------------------------------------------------------------------------- + | + | Here you may configure each of the stores that should be available to + | Pennant. These stores shall be used to store resolved feature flag + | values - you may configure as many as your application requires. + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => null, + 'table' => 'features', + ], + + ], +]; diff --git a/database/migrations/2026_02_15_113143_create_features_table.php b/database/migrations/2026_02_15_113143_create_features_table.php new file mode 100644 index 0000000..a64eea2 --- /dev/null +++ b/database/migrations/2026_02_15_113143_create_features_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('scope'); + $table->text('value'); + $table->timestamps(); + + $table->unique(['name', 'scope']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('features'); + } +}; diff --git a/database/migrations/2026_02_15_121201_create_waitlist_entries_table.php b/database/migrations/2026_02_15_121201_create_waitlist_entries_table.php new file mode 100644 index 0000000..804b9a0 --- /dev/null +++ b/database/migrations/2026_02_15_121201_create_waitlist_entries_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('email')->unique(); + $table->boolean('notified')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('waitlist_entries'); + } +}; diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 0c77ad5..f3fa719 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -4,21 +4,21 @@
-

+

{{ __('Login') }}

-
+
@csrf
-