From 47673f135d2fe0e92806236914675598757763e3 Mon Sep 17 00:00:00 2001 From: korridor <26689068+korridor@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:14:26 +0100 Subject: [PATCH] Added timezone to registration --- app/Actions/Fortify/CreateNewUser.php | 9 ++++- .../Importers/ClockifyTimeEntriesImporter.php | 1 + .../Import/Importers/DefaultImporter.php | 8 +++++ .../Import/Importers/TogglDataImporter.php | 1 + .../Importers/TogglTimeEntriesImporter.php | 1 + app/Service/TimezoneService.php | 5 +++ .../2014_10_12_000000_create_users_table.php | 2 +- playwright.config.ts | 2 +- resources/js/Pages/Auth/Register.vue | 1 + .../Partials/UpdateProfileInformationForm.vue | 13 +++++-- tests/Feature/RegistrationTest.php | 36 +++++++++++++++++++ .../Import/ImportDatabaseHelperTest.php | 1 + 12 files changed, 75 insertions(+), 5 deletions(-) diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index 3702cf5a..9fc33f6a 100644 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -6,6 +6,7 @@ use App\Models\Organization; use App\Models\User; +use App\Service\TimezoneService; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -48,11 +49,17 @@ public function create(array $input): User 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '', ])->validate(); - return DB::transaction(function () use ($input) { + $timezone = 'UTC'; + if (array_key_exists('timezone', $input) && is_string($input['timezone']) && app(TimezoneService::class)->isValid($input['timezone'])) { + $timezone = $input['timezone']; + } + + return DB::transaction(function () use ($input, $timezone) { return tap(User::create([ 'name' => $input['name'], 'email' => $input['email'], 'password' => Hash::make($input['password']), + 'timezone' => $timezone, ]), function (User $user) { $this->createTeam($user); }); diff --git a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php index 228b54df..aff39efd 100644 --- a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php +++ b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php @@ -53,6 +53,7 @@ public function importData(string $data): void 'email' => $record['Email'], ], [ 'name' => $record['User'], + 'timezone' => 'UTC', 'is_placeholder' => true, ]); $clientId = null; diff --git a/app/Service/Import/Importers/DefaultImporter.php b/app/Service/Import/Importers/DefaultImporter.php index e60cef51..8645ceda 100644 --- a/app/Service/Import/Importers/DefaultImporter.php +++ b/app/Service/Import/Importers/DefaultImporter.php @@ -12,6 +12,7 @@ use App\Models\User; use App\Service\ColorService; use App\Service\Import\ImportDatabaseHelper; +use App\Service\TimezoneService; use Illuminate\Database\Eloquent\Builder; abstract class DefaultImporter implements ImporterContract @@ -47,6 +48,8 @@ abstract class DefaultImporter implements ImporterContract protected ColorService $colorService; + protected TimezoneService $timezoneService; + public function init(Organization $organization): void { $this->organization = $organization; @@ -62,6 +65,10 @@ public function init(Organization $organization): void 'required', 'max:255', ], + 'timezone' => [ + 'required', + 'timezone:all', + ], ]); $this->projectImportHelper = new ImportDatabaseHelper(Project::class, ['name', 'organization_id'], true, function (Builder $builder) { return $builder->where('organization_id', $this->organization->id); @@ -97,6 +104,7 @@ public function init(Organization $organization): void ]); $this->timeEntriesCreated = 0; $this->colorService = app(ColorService::class); + $this->timezoneService = app(TimezoneService::class); } #[\Override] diff --git a/app/Service/Import/Importers/TogglDataImporter.php b/app/Service/Import/Importers/TogglDataImporter.php index d6c8415e..1adf5e1f 100644 --- a/app/Service/Import/Importers/TogglDataImporter.php +++ b/app/Service/Import/Importers/TogglDataImporter.php @@ -83,6 +83,7 @@ public function importData(string $data): void 'email' => $workspaceUser->email, ], [ 'name' => $workspaceUser->name, + 'timezone' => $workspaceUser->timezone ?? 'UTC', 'is_placeholder' => true, ], (string) $workspaceUser->id); } diff --git a/app/Service/Import/Importers/TogglTimeEntriesImporter.php b/app/Service/Import/Importers/TogglTimeEntriesImporter.php index 99f301f5..90009a40 100644 --- a/app/Service/Import/Importers/TogglTimeEntriesImporter.php +++ b/app/Service/Import/Importers/TogglTimeEntriesImporter.php @@ -53,6 +53,7 @@ public function importData(string $data): void 'email' => $record['Email'], ], [ 'name' => $record['User'], + 'timezone' => 'UTC', 'is_placeholder' => true, ]); $clientId = null; diff --git a/app/Service/TimezoneService.php b/app/Service/TimezoneService.php index 27a43d56..d3a62509 100644 --- a/app/Service/TimezoneService.php +++ b/app/Service/TimezoneService.php @@ -32,4 +32,9 @@ public function getSelectOptions(): array return $options; } + + public function isValid(string $timezone): bool + { + return in_array($timezone, $this->getTimezones(), true); + } } diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 3d08b16c..f6cd3bc7 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -23,7 +23,7 @@ public function up(): void $table->boolean('is_placeholder')->default(false); $table->foreignUuid('current_team_id')->nullable(); $table->string('profile_photo_path', 2048)->nullable(); - $table->string('timezone')->nullable(); + $table->string('timezone'); $table->timestamps(); $table->uniqueIndex('email') diff --git a/playwright.config.ts b/playwright.config.ts index 507e1c49..0eddd35c 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 1 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + workers: process.env.CI ? 1 : 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI ? 'line' : 'html', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/resources/js/Pages/Auth/Register.vue b/resources/js/Pages/Auth/Register.vue index 31fc664d..095d8369 100644 --- a/resources/js/Pages/Auth/Register.vue +++ b/resources/js/Pages/Auth/Register.vue @@ -15,6 +15,7 @@ const form = useForm({ password: '', password_confirmation: '', terms: false, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone ?? null, }); const submit = () => { diff --git a/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue b/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue index 5defcf93..aad47e4c 100644 --- a/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue +++ b/resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue @@ -203,8 +203,17 @@ const page = usePage<{
- + + diff --git a/tests/Feature/RegistrationTest.php b/tests/Feature/RegistrationTest.php index 4373809f..77b9e54a 100644 --- a/tests/Feature/RegistrationTest.php +++ b/tests/Feature/RegistrationTest.php @@ -49,6 +49,42 @@ public function test_new_users_can_register(): void $this->assertAuthenticated(); $response->assertRedirect(RouteServiceProvider::HOME); + $user = User::where('email', 'test@example.com')->firstOrFail(); + $this->assertSame('UTC', $user->timezone); + } + + public function test_new_users_can_register_and_frontend_can_send_timezone_for_user(): void + { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature(), + 'timezone' => 'Europe/Berlin', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); + $user = User::where('email', 'test@example.com')->firstOrFail(); + $this->assertSame('Europe/Berlin', $user->timezone); + } + + public function test_new_users_can_register_and_ignores_invalid_timezones_from_frontend(): void + { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + 'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature(), + 'timezone' => 'Unknown timezone', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); + $user = User::where('email', 'test@example.com')->firstOrFail(); + $this->assertSame('UTC', $user->timezone); } public function test_new_users_can_not_register_if_user_with_email_already_exists(): void diff --git a/tests/Unit/Service/Import/ImportDatabaseHelperTest.php b/tests/Unit/Service/Import/ImportDatabaseHelperTest.php index 9337b724..db325cdd 100644 --- a/tests/Unit/Service/Import/ImportDatabaseHelperTest.php +++ b/tests/Unit/Service/Import/ImportDatabaseHelperTest.php @@ -42,6 +42,7 @@ public function test_get_key_attach_to_existing_creates_model_if_not_existing(): 'email' => 'test@mail.test', ], [ 'name' => 'Test', + 'timezone' => 'UTC', ]); // Assert