diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index 2fbbd1e5..638871a8 100644 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -6,6 +6,7 @@ namespace App\Actions\Fortify; use App\Enums\Role; use App\Enums\Weekday; +use App\Events\NewsletterRegistered; use App\Models\Organization; use App\Models\User; use App\Service\TimezoneService; @@ -49,6 +50,9 @@ class CreateNewUser implements CreatesNewUsers ], 'password' => $this->passwordRules(), 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '', + 'newsletter_consent' => [ + 'boolean', + ], ])->validate(); $timezone = 'UTC'; @@ -56,7 +60,7 @@ class CreateNewUser implements CreatesNewUsers $timezone = $input['timezone']; } - return DB::transaction(function () use ($input, $timezone) { + $user = DB::transaction(function () use ($input, $timezone) { return tap(User::create([ 'name' => $input['name'], 'email' => $input['email'], @@ -67,6 +71,13 @@ class CreateNewUser implements CreatesNewUsers $this->createTeam($user); }); }); + + $newsletterConsent = isset($input['newsletter_consent']) && (bool) $input['newsletter_consent']; + if ($newsletterConsent) { + NewsletterRegistered::dispatch($input['name'], $input['email'], $user->getKey()); + } + + return $user; } /** diff --git a/app/Events/NewsletterRegistered.php b/app/Events/NewsletterRegistered.php new file mode 100644 index 00000000..17a4a787 --- /dev/null +++ b/app/Events/NewsletterRegistered.php @@ -0,0 +1,28 @@ +name = $name; + $this->email = $email; + $this->id = $id; + } +} diff --git a/app/Providers/JetstreamServiceProvider.php b/app/Providers/JetstreamServiceProvider.php index 6f192680..91489cda 100644 --- a/app/Providers/JetstreamServiceProvider.php +++ b/app/Providers/JetstreamServiceProvider.php @@ -22,6 +22,8 @@ use Brick\Money\Currency; use Brick\Money\ISOCurrencyProvider; use Illuminate\Http\Request; use Illuminate\Support\ServiceProvider; +use Inertia\Inertia; +use Laravel\Fortify\Fortify; use Laravel\Jetstream\Actions\UpdateTeamMemberRole; use Laravel\Jetstream\Jetstream; @@ -52,6 +54,13 @@ class JetstreamServiceProvider extends ServiceProvider Jetstream::useTeamModel(Organization::class); Jetstream::useTeamInvitationModel(OrganizationInvitation::class); app()->singleton(UpdateTeamMemberRole::class, UpdateMemberRole::class); + Fortify::registerView(function () { + return Inertia::render('Auth/Register', [ + 'terms_url' => config('auth.terms_url'), + 'privacy_policy_url' => config('auth.privacy_policy_url'), + 'newsletter_consent' => config('auth.newsletter_consent'), + ]); + }); } /** diff --git a/config/auth.php b/config/auth.php index 9f3aa67a..6ac82835 100644 --- a/config/auth.php +++ b/config/auth.php @@ -117,4 +117,10 @@ return [ 'super_admins' => ! is_string(env('SUPER_ADMINS', null)) ? [] : explode(',', env('SUPER_ADMINS')), + 'terms_url' => env('TERMS_URL'), + + 'privacy_policy_url' => env('PRIVACY_POLICY_URL'), + + 'newsletter_consent' => env('NEWSLETTER_CONSENT', false), + ]; diff --git a/config/jetstream.php b/config/jetstream.php index fc04c5bd..11bd6f76 100644 --- a/config/jetstream.php +++ b/config/jetstream.php @@ -60,9 +60,8 @@ return [ */ 'features' => [ - // Features::termsAndPrivacyPolicy(), + Features::termsAndPrivacyPolicy(), Features::profilePhotos(), - // Features::api(), Features::teams(['invitations' => true]), Features::accountDeletion(), ], diff --git a/config/services.php b/config/services.php deleted file mode 100644 index 104826f0..00000000 --- a/config/services.php +++ /dev/null @@ -1,36 +0,0 @@ - [ - 'domain' => env('MAILGUN_DOMAIN'), - 'secret' => env('MAILGUN_SECRET'), - 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), - 'scheme' => 'https', - ], - - 'postmark' => [ - 'token' => env('POSTMARK_TOKEN'), - ], - - 'ses' => [ - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - ], - -]; diff --git a/resources/js/Pages/Auth/Register.vue b/resources/js/Pages/Auth/Register.vue index b18e6fed..68882ed3 100644 --- a/resources/js/Pages/Auth/Register.vue +++ b/resources/js/Pages/Auth/Register.vue @@ -15,15 +15,21 @@ const form = useForm({ password_confirmation: '', terms: false, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone ?? null, + newsletter_consent: false, }); const submit = () => { form.post(route('register'), { - onFinish: () => form.reset('password', 'password_confirmation'), + onSuccess: () => { + form.reset('password', 'password_confirmation'); + }, }); }; const page = usePage<{ + terms_url: string | null; + privacy_policy_url: string | null; + newsletter_consent: boolean; jetstream: { hasTermsAndPrivacyPolicyFeature: boolean; }; @@ -111,29 +117,32 @@ const page = usePage<{
+ name="terms" />
I agree to the Terms of Service and Privacy Policy
@@ -142,6 +151,25 @@ const page = usePage<{
+
+ +
+ + +
+ I agree to receive emails about product related + updates +
+
+ +
+
+
+ class="underline text-sm text-muted hover:text-white rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> Edit Profile diff --git a/tests/Feature/RegistrationTest.php b/tests/Feature/RegistrationTest.php index 5294a8c4..e99ef1b2 100644 --- a/tests/Feature/RegistrationTest.php +++ b/tests/Feature/RegistrationTest.php @@ -5,10 +5,12 @@ declare(strict_types=1); namespace Tests\Feature; use App\Enums\Role; +use App\Events\NewsletterRegistered; use App\Models\Membership; use App\Models\User; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Event; use Laravel\Fortify\Features; use Laravel\Jetstream\Jetstream; use Tests\TestCase; @@ -41,6 +43,11 @@ class RegistrationTest extends TestCase public function test_new_users_can_register(): void { + // Arrange + Event::fake([ + NewsletterRegistered::class, + ]); + // Act $response = $this->post('/register', [ 'name' => 'Test User', @@ -51,6 +58,7 @@ class RegistrationTest extends TestCase ]); // Assert + $response->assertValid(); $this->assertAuthenticated(); $response->assertRedirect(RouteServiceProvider::HOME); $user = User::where('email', 'test@example.com')->firstOrFail(); @@ -60,6 +68,34 @@ class RegistrationTest extends TestCase $this->assertSame(true, $organization->personal_team); $member = Membership::query()->whereBelongsTo($user, 'user')->whereBelongsTo($organization, 'organization')->firstOrFail(); $this->assertSame(Role::Owner->value, $member->role); + Event::assertNotDispatched(NewsletterRegistered::class); + } + + public function test_new_users_can_consent_to_newsletter_during_registration(): void + { + // Arrange + Event::fake([ + NewsletterRegistered::class, + ]); + + // Act + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature(), + 'newsletter_consent' => true, + ]); + + // Assert + $response->assertValid(); + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); + $user = User::where('email', 'test@example.com')->firstOrFail(); + $this->assertSame('Test User', $user->name); + $this->assertSame('UTC', $user->timezone); + Event::assertDispatched(NewsletterRegistered::class); } public function test_new_users_can_register_and_frontend_can_send_timezone_for_user(): void