Repository: djaiss/peopleOS
Pull request #131: feat: timezone for user
Author: login: djaiss
Created: 2025-03-13T10:38:06Z | Merged: 2025-03-14T01:21:04Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced a dedicated timezone settings section in the administration panel for user management.
  - Added a new logs display component to enhance visibility of user activity.
  - Improved profile and avatar screens with clearer upload instructions and reorganized layouts.
  - Enhanced person overview pages by adding a collapsible section for detailed meeting information.
- **Tests**
  - Strengthened validations for timezone updates to ensure proper functionality and user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 2ada9beb..4f114726 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,23 +1,23 @@
 version: 2
 updates:
   # Enable version updates for npm
-  - package-ecosystem: "npm"
+  - package-ecosystem: 'npm'
     # Look for `package.json` and `lock` files in the `root` directory
-    directory: "/"
+    directory: '/'
     # Check the npm registry for updates every day (weekdays)
     schedule:
-      interval: "weekly"
+      interval: 'weekly'
 
   # Enable version updates for composer
-  - package-ecosystem: "composer"
-    directory: "/"
+  - package-ecosystem: 'composer'
+    directory: '/'
     schedule:
-      interval: "weekly"
+      interval: 'weekly'
 
   # Enable version updates for GitHub Actions
-  - package-ecosystem: "github-actions"
+  - package-ecosystem: 'github-actions'
     # Workflow files stored in the default location of `.github/workflows`
     # You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
-    directory: "/"
+    directory: '/'
     schedule:
-      interval: "weekly"
+      interval: 'weekly'
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 20bcc4f6..a545f08a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,4 +1,3 @@
-
 # Contributor Covenant Code of Conduct
 
 ## Our Pledge
@@ -18,23 +17,23 @@ diverse, inclusive, and healthy community.
 Examples of behavior that contributes to a positive environment for our
 community include:
 
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
   and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall
+- Focusing on what is best not just for us as individuals, but for the overall
   community
 
 Examples of unacceptable behavior include:
 
-* The use of sexualized language or imagery, and sexual attention or advances of
+- The use of sexualized language or imagery, and sexual attention or advances of
   any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email address,
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
   without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
+- Other conduct which could reasonably be considered inappropriate in a
   professional setting
 
 ## Enforcement Responsibilities
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b9a675cb..6d1ce632 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,13 +22,13 @@ A paragraph describing what changed and its impact.
 
 We prioritize readability and maintainability in our codebase:
 
-* We use 4 spaces for indentation in PHP files and 2 spaces for JS/CSS
-* We follow Laravel conventions for PHP code
-* We use Blade for views and minimize logic in templates
-* We use Tailwind CSS for styling with Alpine.js for frontend interactivity
-* We always put spaces after list items, method parameters, and around operators
-* We use type hints and return types in PHP wherever possible
-* We follow PSR-12 coding standards
+- We use 4 spaces for indentation in PHP files and 2 spaces for JS/CSS
+- We follow Laravel conventions for PHP code
+- We use Blade for views and minimize logic in templates
+- We use Tailwind CSS for styling with Alpine.js for frontend interactivity
+- We always put spaces after list items, method parameters, and around operators
+- We use type hints and return types in PHP wherever possible
+- We follow PSR-12 coding standards
 
 Remember this is open source software - write your code with future readers in mind. Make it clean, well-documented, and easy to understand.
 
diff --git a/TODO b/TODO
index c69ff271..97861190 100644
--- a/TODO
+++ b/TODO
@@ -8,17 +8,19 @@ Todo:
   - Add "how we met" to person: DONE
   - rename all `store` to `create` in controllers: DONE
   - add life events
+  - add a block
+    - add addresses
+    - add languages spoken
+    - add social networks
+    - add phone numbers
+    - add nationalities
+  - add support zone
   - add todos
-  - add addresses
-  - add social networks
-  - add phone numbers
   - remove all stuff from controllers
   - Add services to manage instance
   - Add ability to broadcast a message to all users
   - Add passions to person
-  - Add nationality to person and significant other
   - Add hobbies to person
-  - Add languages to person
 
   Country born - city born
   Country/place studies
diff --git a/app/Http/Controllers/Administration/AdministrationTimezoneController.php b/app/Http/Controllers/Administration/AdministrationTimezoneController.php
new file mode 100644
index 00000000..f7857109
--- /dev/null
+++ b/app/Http/Controllers/Administration/AdministrationTimezoneController.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Http\Controllers\Administration;
+
+use App\Http\Controllers\Controller;
+use App\Models\Log;
+use App\Models\User;
+use App\Services\UpdateTimezone;
+use App\Services\UpdateUserInformation;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Validation\Rule;
+use Illuminate\View\View;
+
+class AdministrationTimezoneController extends Controller
+{
+    public function update(Request $request): RedirectResponse
+    {
+        $validated = $request->validate([
+            'timezone' => ['required', 'string', 'timezone:all'],
+        ]);
+
+        (new UpdateTimezone(
+            user: Auth::user(),
+            timezone: $validated['timezone'],
+        ))->execute();
+
+        return redirect()->route('administration.index')
+            ->with('status', trans('Changes saved'));
+    }
+}
diff --git a/app/Http/Controllers/Api/Administration/MeTimezoneController.php b/app/Http/Controllers/Api/Administration/MeTimezoneController.php
new file mode 100644
index 00000000..25d1b679
--- /dev/null
+++ b/app/Http/Controllers/Api/Administration/MeTimezoneController.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Http\Controllers\Api\Administration;
+
+use App\Http\Controllers\Controller;
+use App\Services\UpdateTimezone;
+use Illuminate\Http\Request;
+
+class MeTimezoneController extends Controller
+{
+    /**
+     * Get the current user's timezone.
+     */
+    public function show(Request $request)
+    {
+        return response()->json(['timezone' => $request->user()->timezone]);
+    }
+
+    /**
+     * Update your timezone.
+     */
+    public function update(Request $request)
+    {
+        $validated = $request->validate([
+            'timezone' => ['required', 'string', 'timezone:all'],
+        ]);
+
+        (new UpdateTimezone(
+            user: $request->user(),
+            timezone: $validated['timezone'],
+        ))->execute();
+
+        $response = [
+            'id' => $request->user()->id,
+            'timezone' => $request->user()->timezone,
+        ];
+
+        return response()->json($response);
+    }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index fb6bca24..532faf3f 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -31,13 +31,13 @@ class User extends Authenticatable implements MustVerifyEmail
         'first_name',
         'last_name',
         'nickname',
+        'timezone',
         'email',
         'email_verified_at',
         'password',
         'locale',
         'does_display_full_names',
         'does_display_age',
-        'timezone',
         'two_factor_secret',
         'two_factor_recovery_codes',
         'two_factor_confirmed_at',
@@ -72,6 +72,7 @@ protected function casts(): array
             'first_name' => 'encrypted',
             'last_name' => 'encrypted',
             'nickname' => 'encrypted',
+            'timezone' => 'encrypted',
             'email_verified_at' => 'datetime',
             'password' => 'hashed',
             'does_display_full_names' => 'boolean',
diff --git a/app/Services/UpdateTimezone.php b/app/Services/UpdateTimezone.php
new file mode 100644
index 00000000..b79657b0
--- /dev/null
+++ b/app/Services/UpdateTimezone.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Services;
+
+use App\Jobs\LogUserAction;
+use App\Jobs\UpdateUserLastActivityDate;
+use App\Models\User;
+use Carbon\Carbon;
+use Carbon\Exceptions\InvalidDateException;
+use Carbon\Exceptions\InvalidFormatException;
+use Illuminate\Auth\Events\Registered;
+use InvalidArgumentException;
+
+class UpdateTimezone
+{
+    public function __construct(
+        public User $user,
+        public string $timezone,
+    ) {}
+
+    public function execute(): User
+    {
+        $this->update();
+        $this->updateUserLastActivityDate();
+        $this->log();
+
+        return $this->user;
+    }
+
+    private function update(): void
+    {
+        $this->user->update([
+            'timezone' => $this->timezone,
+        ]);
+    }
+
+    private function updateUserLastActivityDate(): void
+    {
+        UpdateUserLastActivityDate::dispatch($this->user);
+    }
+
+    private function log(): void
+    {
+        LogUserAction::dispatch(
+            user: $this->user,
+            action: 'timezone_update',
+            description: 'Updated their timezone',
+        );
+    }
+}
diff --git a/config/marketing-timestamps.php b/config/marketing-timestamps.php
index 49a7139c..680b6a82 100644
--- a/config/marketing-timestamps.php
+++ b/config/marketing-timestamps.php
@@ -1,7 +1,5 @@
 <?php
 
-declare(strict_types=1);
-
 return [
     'pages' => [
         'marketing/company/handbook/index' => '2025-03-10T20:51:59-04:00',
@@ -10,8 +8,8 @@
         'marketing.company.handbook.principles' => '2025-03-10T20:51:59-04:00',
         'marketing/company/handbook/project' => '2025-03-10T20:51:59-04:00',
         'marketing.company.handbook.project' => '2025-03-10T20:51:59-04:00',
-        'marketing/company/handbook/shipping' => '2025-03-10T20:58:47-04:00',
-        'marketing.company.handbook.shipping' => '2025-03-10T20:58:47-04:00',
+        'marketing/company/handbook/shipping' => '2025-03-10T21:02:25-04:00',
+        'marketing.company.handbook.shipping' => '2025-03-10T21:02:25-04:00',
         'marketing/company/index' => '2025-03-10T20:51:59-04:00',
         'marketing.company.index' => '2025-03-10T20:51:59-04:00',
         'marketing/docs/api/api-management' => '2025-03-07T21:12:58-05:00',
@@ -26,8 +24,8 @@
         'marketing.docs.api.introduction' => '2025-03-08T15:31:01-05:00',
         'marketing/docs/api/logs' => '2025-03-07T21:10:04-05:00',
         'marketing.docs.api.logs' => '2025-03-07T21:10:04-05:00',
-        'marketing/docs/api/profile' => '2025-03-07T17:54:03-05:00',
-        'marketing.docs.api.profile' => '2025-03-07T17:54:03-05:00',
+        'marketing/docs/api/profile' => '2025-03-13T21:15:49-04:00',
+        'marketing.docs.api.profile' => '2025-03-13T21:15:49-04:00',
         'marketing/docs/index' => '2025-03-04T09:10:33-05:00',
         'marketing.docs.index' => '2025-03-04T09:10:33-05:00',
         'marketing/index' => '2025-03-10T20:51:59-04:00',
@@ -39,4 +37,4 @@
         'marketing/why/index' => '2025-03-10T20:51:59-04:00',
         'marketing.why.index' => '2025-03-10T20:51:59-04:00',
     ],
-];
+];
\ No newline at end of file
diff --git a/config/peopleos.php b/config/peopleos.php
index 43e9c13f..d787a865 100644
--- a/config/peopleos.php
+++ b/config/peopleos.php
@@ -71,5 +71,6 @@
         'encounter_creation' => 'Encounter creation',
         'encounter_deletion' => 'Encounter deletion',
         'person_avatar_update' => 'Person avatar update',
+        'timezone_update' => 'Timezone update',
     ],
 ];
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
index 7e2761f9..803bc000 100644
--- a/database/factories/UserFactory.php
+++ b/database/factories/UserFactory.php
@@ -40,6 +40,7 @@ public function definition(): array
             'born_at' => null,
             'does_display_age' => false,
             'is_instance_admin' => false,
+            'timezone' => 'UTC',
         ];
     }
 
diff --git a/database/migrations/2025_03_13_010855_add_timezone_to_users.php b/database/migrations/2025_03_13_010855_add_timezone_to_users.php
new file mode 100644
index 00000000..bebcf7f6
--- /dev/null
+++ b/database/migrations/2025_03_13_010855_add_timezone_to_users.php
@@ -0,0 +1,36 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('timezone');
+        });
+
+        Schema::table('users', function (Blueprint $table) {
+            $table->text('timezone')->nullable()->after('nickname');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('timezone');
+        });
+
+        Schema::table('users', function (Blueprint $table) {
+            $table->string('timezone')->nullable()->after('nickname');
+        });
+    }
+};
diff --git a/resources/css/app.css b/resources/css/app.css
index 9d33e86a..fdc388bb 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -1,5 +1,4 @@
 @import 'tailwindcss';
-
 @plugin '@tailwindcss/forms';
 @plugin '@tailwindcss/typography';
 
diff --git a/resources/views/administration/index.blade.php b/resources/views/administration/index.blade.php
index 85801ffc..5390d2d0 100644
--- a/resources/views/administration/index.blade.php
+++ b/resources/views/administration/index.blade.php
@@ -15,49 +15,16 @@
     <div class="relative bg-gray-50 px-6 pt-8 lg:px-12">
       <div class="mx-auto max-w-2xl px-2 py-2 sm:px-0">
         <!-- Profile -->
-        <h1 class="font-semi-bold mb-4 text-2xl">
-          {{ __('Profile') }}
-        </h1>
-
         @include('administration.partials.profile', ['user' => $user])
 
         <!-- Profile photo -->
-        <h2 class="font-semi-bold mb-1 text-lg">{{ __('Profile photo') }}</h2>
-        <p class="mb-4 text-sm text-zinc-500">{{ __('You can upload a profile photo to use as your avatar, or use the default avatar.') }}</p>
-
         @include('administration.partials.avatar', ['user' => $user])
 
-        <!-- Last activity -->
-        <h2 class="font-semi-bold mb-4 text-lg">
-          {{ __('Last activity') }}
-        </h2>
-
-        <div class="mb-8 border border-gray-200 bg-white sm:rounded-lg">
-          <!-- last actions -->
-          @foreach ($logs as $log)
-            <div class="flex items-center justify-between border-b border-gray-200 p-3 text-sm first:rounded-t-lg last:rounded-b-lg last:border-b-0 hover:bg-blue-50">
-              <div class="flex items-center gap-3">
-                <x-lucide-activity class="size-3 min-w-3 text-zinc-600 dark:text-zinc-400" />
-                <div class="">
-                  <p class="flex items-center gap-1">
-                    <span class="">{{ $log['user']['name'] }}</span>
-                    |
-                    <span class="font-mono text-xs">{{ $log['action'] }}</span>
-                  </p>
-                  <p>{{ $log['description'] }}</p>
-                </div>
-              </div>
-
-              <p class="font-mono text-xs">{{ $log['created_at'] }}</p>
-            </div>
-          @endforeach
+        <!-- Timezone -->
+        @include('administration.partials.timezone', ['user' => $user])
 
-          @if ($has_more_logs)
-            <div class="flex justify-center rounded-b-lg p-3 text-sm hover:bg-blue-50">
-              <x-link href="{{ route('administration.logs.index') }}" class="text-center">{{ __('Browse all activity') }}</x-link>
-            </div>
-          @endif
-        </div>
+        <!-- Last activity -->
+        @include('administration.partials.logs', ['has_more_logs' => $has_more_logs, 'logs' => $logs])
       </div>
     </div>
   </div>
diff --git a/resources/views/administration/partials/avatar.blade.php b/resources/views/administration/partials/avatar.blade.php
index 7c319b83..ab58fe47 100644
--- a/resources/views/administration/partials/avatar.blade.php
+++ b/resources/views/administration/partials/avatar.blade.php
@@ -4,6 +4,9 @@
  */
 ?>
 
+<h2 class="font-semi-bold mb-1 text-lg">{{ __('Profile photo') }}</h2>
+<p class="mb-4 text-sm text-zinc-500">{{ __('You can upload a profile photo to use as your avatar, or use the default avatar.') }}</p>
+
 <div class="mb-8">
   <div x-data="{
     isFlipped: false,
diff --git a/resources/views/administration/partials/logs.blade.php b/resources/views/administration/partials/logs.blade.php
new file mode 100644
index 00000000..c7eb3b3a
--- /dev/null
+++ b/resources/views/administration/partials/logs.blade.php
@@ -0,0 +1,37 @@
+<?php
+/*
+ * @var array $logs
+ * @var bool $has_more_logs
+ */
+?>
+
+<h2 class="font-semi-bold mb-4 text-lg">
+  {{ __('Last activity') }}
+</h2>
+
+<div class="mb-8 border border-gray-200 bg-white sm:rounded-lg">
+  <!-- last actions -->
+  @foreach ($logs as $log)
+    <div class="flex items-center justify-between border-b border-gray-200 p-3 text-sm first:rounded-t-lg last:rounded-b-lg last:border-b-0 hover:bg-blue-50">
+      <div class="flex items-center gap-3">
+        <x-lucide-activity class="size-3 min-w-3 text-zinc-600 dark:text-zinc-400" />
+        <div class="">
+          <p class="flex items-center gap-1">
+            <span class="">{{ $log['user']['name'] }}</span>
+            |
+            <span class="font-mono text-xs">{{ $log['action'] }}</span>
+          </p>
+          <p>{{ $log['description'] }}</p>
+        </div>
+      </div>
+
+      <p class="font-mono text-xs">{{ $log['created_at'] }}</p>
+    </div>
+  @endforeach
+
+  @if ($has_more_logs)
+    <div class="flex justify-center rounded-b-lg p-3 text-sm hover:bg-blue-50">
+      <x-link href="{{ route('administration.logs.index') }}" class="text-center">{{ __('Browse all activity') }}</x-link>
+    </div>
+  @endif
+</div>
diff --git a/resources/views/administration/partials/profile.blade.php b/resources/views/administration/partials/profile.blade.php
index 111ddda2..873168ee 100644
--- a/resources/views/administration/partials/profile.blade.php
+++ b/resources/views/administration/partials/profile.blade.php
@@ -4,6 +4,10 @@
  */
 ?>
 
+<h1 class="font-semi-bold mb-4 text-2xl">
+  {{ __('Profile') }}
+</h1>
+
 <form action="{{ route('administration.update') }}" method="post" class="mb-8 border border-gray-200 bg-white sm:rounded-lg" x-data="{ showActions: false }">
   @csrf
   @method('put')
@@ -58,7 +62,7 @@
   </div>
 
   <div x-cloak x-show="showActions" x-transition:enter="transition duration-200 ease-out" x-transition:enter-start="-translate-y-2 transform opacity-0" x-transition:enter-end="translate-y-0 transform opacity-100" x-transition:leave="transition duration-150 ease-in" x-transition:leave-start="translate-y-0 transform opacity-100" x-transition:leave-end="-translate-y-2 transform opacity-0" class="flex justify-between border-t border-gray-200 p-3">
-    <x-button.secondary wire:click="toggleAddMode" class="mr-2">
+    <x-button.secondary @click="showActions = false" class="mr-2">
       {{ __('Cancel') }}
     </x-button.secondary>
 
diff --git a/resources/views/administration/partials/timezone.blade.php b/resources/views/administration/partials/timezone.blade.php
new file mode 100644
index 00000000..c029627e
--- /dev/null
+++ b/resources/views/administration/partials/timezone.blade.php
@@ -0,0 +1,83 @@
+<?php
+/*
+ * @var \App\Models\User $user
+ */
+?>
+
+<h2 class="font-semi-bold mb-1 text-lg">
+  {{ __('Timezone') }}
+</h2>
+
+<form x-target="timezone-form" x-target.back="timezone-form" id="timezone-form" action="{{ route('administration.timezone.update') }}" method="post" class="mb-8 border border-gray-200 bg-white sm:rounded-lg" x-data="{ showActions: false }">
+  @csrf
+  @method('put')
+
+  <!-- timezone -->
+  <div class="grid grid-cols-3 items-center rounded-t-lg p-3 hover:bg-blue-50 last:rounded-b-lg">
+    <x-input-label for="timezone" :value="__('Timezone')" class="col-span-1" />
+    <div class="w-full justify-self-end col-span-2">
+      <select @focus="showActions = true" @blur="showActions = false" id="timezone" name="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 disabled:text-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
+        <optgroup label="UTC">
+            <option value="UTC" @selected($user->timezone === 'UTC')>Coordinated Universal Time (UTC) - UTC+00:00</option>
+        </optgroup>
+        <optgroup label="Africa">
+            <option value="Africa/Casablanca" @selected($user->timezone === 'Africa/Casablanca')>Western European Time (WET) - UTC+00:00</option>
+            <option value="Africa/Lagos" @selected($user->timezone === 'Africa/Lagos')>West Africa Time (WAT) - UTC+01:00</option>
+            <option value="Africa/Cairo" @selected($user->timezone === 'Africa/Cairo')>Eastern European Time (EET) - UTC+02:00</option>
+            <option value="Africa/Johannesburg" @selected($user->timezone === 'Africa/Johannesburg')>South Africa Standard Time (SAST) - UTC+02:00</option>
+        </optgroup>
+        <optgroup label="America">
+            <option value="America/St_Johns" @selected($user->timezone === 'America/St_Johns')>Newfoundland Standard Time (NST) - UTC-03:30</option>
+            <option value="America/Halifax" @selected($user->timezone === 'America/Halifax')>Atlantic Standard Time (AST) - UTC-04:00</option>
+            <option value="America/New_York" @selected($user->timezone === 'America/New_York')>Eastern Standard Time (EST) - UTC-05:00</option>
+            <option value="America/Chicago" @selected($user->timezone === 'America/Chicago')>Central Standard Time (CST) - UTC-06:00</option>
+            <option value="America/Denver" @selected($user->timezone === 'America/Denver')>Mountain Standard Time (MST) - UTC-07:00</option>
+            <option value="America/Los_Angeles" @selected($user->timezone === 'America/Los_Angeles')>Pacific Standard Time (PST) - UTC-08:00</option>
+            <option value="America/Anchorage" @selected($user->timezone === 'America/Anchorage')>Alaska Standard Time (AKST) - UTC-09:00</option>
+            <option value="Pacific/Honolulu" @selected($user->timezone === 'Pacific/Honolulu')>Hawaii-Aleutian Standard Time (HAST) - UTC-10:00</option>
+        </optgroup>
+        <optgroup label="Antarctica">
+            <option value="Antarctica/Palmer" @selected($user->timezone === 'Antarctica/Palmer')>Chile Summer Time (CLST) - UTC-03:00</option>
+            <option value="Antarctica/McMurdo" @selected($user->timezone === 'Antarctica/McMurdo')>New Zealand Standard Time (NZST) - UTC+12:00</option>
+        </optgroup>
+        <optgroup label="Asia">
+            <option value="Asia/Dubai" @selected($user->timezone === 'Asia/Dubai')>Gulf Standard Time (GST) - UTC+04:00</option>
+            <option value="Asia/Karachi" @selected($user->timezone === 'Asia/Karachi')>Pakistan Standard Time (PKT) - UTC+05:00</option>
+            <option value="Asia/Kolkata" @selected($user->timezone === 'Asia/Kolkata')>India Standard Time (IST) - UTC+05:30</option>
+            <option value="Asia/Dhaka" @selected($user->timezone === 'Asia/Dhaka')>Bangladesh Standard Time (BST) - UTC+06:00</option>
+            <option value="Asia/Jakarta" @selected($user->timezone === 'Asia/Jakarta')>Western Indonesia Time (WIB) - UTC+07:00</option>
+            <option value="Asia/Shanghai" @selected($user->timezone === 'Asia/Shanghai')>China Standard Time (CST) - UTC+08:00</option>
+            <option value="Asia/Tokyo" @selected($user->timezone === 'Asia/Tokyo')>Japan Standard Time (JST) - UTC+09:00</option>
+            <option value="Asia/Seoul" @selected($user->timezone === 'Asia/Seoul')>Korea Standard Time (KST) - UTC+09:00</option>
+        </optgroup>
+        <optgroup label="Australia">
+            <option value="Australia/Perth" @selected($user->timezone === 'Australia/Perth')>Australian Western Standard Time (AWST) - UTC+08:00</option>
+            <option value="Australia/Adelaide" @selected($user->timezone === 'Australia/Adelaide')>Australian Central Standard Time (ACST) - UTC+09:30</option>
+            <option value="Australia/Sydney" @selected($user->timezone === 'Australia/Sydney')>Australian Eastern Standard Time (AEST) - UTC+10:00</option>
+        </optgroup>
+        <optgroup label="Europe">
+            <option value="Europe/London" @selected($user->timezone === 'Europe/London')>Greenwich Mean Time (GMT) - UTC+00:00</option>
+            <option value="Europe/Berlin" @selected($user->timezone === 'Europe/Berlin')>Central European Time (CET) - UTC+01:00</option>
+            <option value="Europe/Moscow" @selected($user->timezone === 'Europe/Moscow')>Moscow Standard Time (MSK) - UTC+03:00</option>
+        </optgroup>
+        <optgroup label="Pacific">
+            <option value="Pacific/Auckland" @selected($user->timezone === 'Pacific/Auckland')>New Zealand Standard Time (NZST) - UTC+12:00</option>
+            <option value="Pacific/Fiji" @selected($user->timezone === 'Pacific/Fiji')>Fiji Time (FJT) - UTC+12:00</option>
+        </optgroup>
+    </select>
+
+      <x-input-error class="mt-2" :messages="$errors->get('timezone')" />
+    </div>
+  </div>
+
+  <!-- actions -->
+  <div x-cloak x-show="showActions" x-transition:enter="transition duration-200 ease-out" x-transition:enter-start="-translate-y-2 transform opacity-0" x-transition:enter-end="translate-y-0 transform opacity-100" x-transition:leave="transition duration-150 ease-in" x-transition:leave-start="translate-y-0 transform opacity-100" x-transition:leave-end="-translate-y-2 transform opacity-0" class="flex justify-between border-t border-gray-200 p-3">
+    <x-button.secondary @click="showActions = false" class="mr-2">
+      {{ __('Cancel') }}
+    </x-button.secondary>
+
+    <x-button.primary>
+      {{ __('Save') }}
+    </x-button.primary>
+  </div>
+</form>
diff --git a/resources/views/marketing/docs/api/profile.blade.php b/resources/views/marketing/docs/api/profile.blade.php
index 642ffece..351d93d9 100644
--- a/resources/views/marketing/docs/api/profile.blade.php
+++ b/resources/views/marketing/docs/api/profile.blade.php
@@ -11,6 +11,12 @@
       <li>
         <a href="#update-the-information-about-the-logged-user" class="text-blue-500 hover:underline">Update the information about the logged user</a>
       </li>
+      <li>
+        <a href="#get-the-timezone-of-the-logged-user" class="text-blue-500 hover:underline">Get the timezone of the logged user</a>
+      </li>
+      <li>
+        <a href="#update-the-timezone-of-the-logged-user" class="text-blue-500 hover:underline">Update the timezone of the logged user</a>
+      </li>
     </ul>
   </div>
 
@@ -30,6 +36,14 @@
             <span class="text-orange-500">PUT</span>
             /api/me
           </a>
+          <a href="#get-the-timezone-of-the-logged-user">
+            <span class="text-blue-700">GET</span>
+            /api/me/timezone
+          </a>
+          <a href="#update-the-timezone-of-the-logged-user">
+            <span class="text-orange-500">PUT</span>
+            /api/me/timezone
+          </a>
         </div>
       </x-marketing.code>
     </div>
@@ -186,6 +200,91 @@
     </div>
   </div>
 
+  <!-- GET /api/me/timezone -->
+  <div class="mb-10 grid grid-cols-1 gap-6 border-b border-gray-200 pb-10 sm:grid-cols-2">
+    <div>
+      <h3 id="get-the-timezone-of-the-logged-user" class="mb-2 text-lg font-bold">Get the timezone of the logged user</h3>
+      <p class="mb-10">This endpoint retrieves the timezone information of the currently logged in user.</p>
+
+      <!-- parameters -->
+      <div x-cloak x-data="{ open: false }" class="mb-10">
+        <div @click="open = !open" x-bind:class="open ? 'border-b border-gray-200' : ''" class="mb-2 flex cursor-pointer items-center justify-between pb-2">
+          <p class="font-semibold">Parameters</p>
+          <x-lucide-chevron-right x-bind:class="open ? 'rotate-90' : ''" class="h-4 w-4 text-gray-500 transition-transform duration-300" />
+        </div>
+
+        <div x-show="open" x-transition class="mt-2">
+          <p class="text-gray-500">This endpoint does not have any parameters.</p>
+        </div>
+      </div>
+
+      <!-- response attributes -->
+      <div x-cloak x-data="{ open: false }">
+        <div @click="open = !open" x-bind:class="open ? 'border-b border-gray-200' : ''" class="flex cursor-pointer items-center justify-between pb-2">
+          <p class="font-semibold">Response attributes</p>
+          <x-lucide-chevron-right x-bind:class="open ? 'rotate-90' : ''" class="h-4 w-4 text-gray-500 transition-transform duration-300" />
+        </div>
+
+        <div x-show="open" x-transition>
+          <x-marketing.attribute name="timezone" type="string" description="The timezone of the user in IANA format." />
+        </div>
+      </div>
+    </div>
+    <div>
+      <x-marketing.code title="/api/me/timezone" verb="GET" verbClass="text-blue-700">
+        <div>{</div>
+        <div class="pl-4">
+          "timezone":
+          <span class="text-lime-700">"America/New_York"</span>
+        </div>
+        <div>}</div>
+      </x-marketing.code>
+    </div>
+  </div>
+
+  <!-- PUT /api/me/timezone -->
+  <div class="mb-10 grid grid-cols-1 gap-6 border-b border-gray-200 pb-10 sm:grid-cols-2">
+    <div>
+      <h3 id="update-the-timezone-of-the-logged-user" class="mb-2 text-lg font-bold">Update the timezone of the logged user</h3>
+      <p class="mb-2">This endpoint updates the timezone of the currently logged in user.</p>
+      <p class="mb-10">The timezone must be a valid IANA timezone identifier (e.g., "America/New_York", "Europe/London").</p>
+
+      <!-- parameters -->
+      <div x-cloak x-data="{ open: false }" class="mb-10">
+        <div @click="open = !open" x-bind:class="open ? 'border-b border-gray-200' : ''" class="mb-2 flex cursor-pointer items-center justify-between pb-2">
+          <p class="font-semibold">Parameters</p>
+          <x-lucide-chevron-right x-bind:class="open ? 'rotate-90' : ''" class="h-4 w-4 text-gray-500 transition-transform duration-300" />
+        </div>
+
+        <div x-show="open" x-transition>
+          <x-marketing.attribute name="timezone" required="true" type="string" description="The timezone of the user in IANA format (e.g., 'America/New_York', 'Europe/London')." />
+        </div>
+      </div>
+
+      <!-- response attributes -->
+      <div x-cloak x-data="{ open: false }">
+        <div @click="open = !open" x-bind:class="open ? 'border-b border-gray-200' : ''" class="flex cursor-pointer items-center justify-between pb-2">
+          <p class="font-semibold">Response attributes</p>
+          <x-lucide-chevron-right x-bind:class="open ? 'rotate-90' : ''" class="h-4 w-4 text-gray-500 transition-transform duration-300" />
+        </div>
+
+        <div x-show="open" x-transition>
+          <x-marketing.attribute name="timezone" type="string" description="The updated timezone of the user in IANA format." />
+        </div>
+      </div>
+    </div>
+    <div>
+      <x-marketing.code title="/api/me/timezone" verb="PUT" verbClass="text-orange-500">
+        <div>{</div>
+        <div class="pl-4">
+          "timezone":
+          <span class="text-lime-700">"Europe/London"</span>
+        </div>
+        <div>}</div>
+      </x-marketing.code>
+    </div>
+  </div>
+
   <div>
     <x-marketing.edit-github />
   </div>
diff --git a/resources/views/persons/overview/index.blade.php b/resources/views/persons/overview/index.blade.php
index ebbc673f..f0402e70 100644
--- a/resources/views/persons/overview/index.blade.php
+++ b/resources/views/persons/overview/index.blade.php
@@ -5,6 +5,8 @@
  */
 ?>
 
+@include('persons.overview.partials.information')
+
 @include('persons.overview.partials.how-we-met')
 
 @include('persons.overview.partials.encounters')
diff --git a/resources/views/persons/overview/partials/information.blade.php b/resources/views/persons/overview/partials/information.blade.php
new file mode 100644
index 00000000..5e141f07
--- /dev/null
+++ b/resources/views/persons/overview/partials/information.blade.php
@@ -0,0 +1,113 @@
+<?php
+/*
+ * @var Person $person
+ */
+?>
+
+<section id="edit-how-we-met-form" x-data="{
+  expanded: {{ $person->how_we_met_shown ? 'true' : 'false' }},
+}" class="mb-8">
+  <div class="mb-4 flex items-center justify-between">
+    <div class="flex items-center gap-2">
+      <x-lucide-user class="h-5 w-5 text-purple-500" />
+      <h2 class="text-lg font-semibold text-gray-900">{{ __('Information') }}</h2>
+    </div>
+    <div class="flex items-center gap-2">
+      <a x-target="edit-how-we-met-form" href="{{ route('persons.how-we-met.create', $person->slug) }}" class="inline-flex cursor-pointer items-center gap-1 rounded-md bg-gray-50 px-2 py-1 text-sm font-medium text-gray-600 hover:bg-gray-200">
+        <span x-text="expanded ? '{{ __('Show less') }}' : '{{ __('Show more') }}'"></span>
+        <x-lucide-chevron-down x-show="!expanded" class="h-4 w-4" />
+        <x-lucide-chevron-up x-show="expanded" class="h-4 w-4" />
+      </a>
+      <a x-target="edit-how-we-met-form" href="{{ route('persons.how-we-met.edit', $person->slug) }}" class="inline-flex items-center gap-1 rounded-md bg-purple-50 px-3 py-2 text-sm font-medium text-purple-600 hover:bg-purple-100">
+        <x-lucide-pencil class="mr-1 h-3 w-3" />
+        {{ __('Edit') }}
+      </a>
+    </div>
+  </div>
+
+  <!-- Collapsed State -->
+  <div x-show="!expanded" x-transition class="rounded-lg border border-gray-200 bg-white">
+    <div class="relative flex items-center gap-4 p-4">
+      <div class="flex shrink-0 items-center gap-3 text-sm text-gray-500">
+        @if ($person->howWeMetSpecialDate)
+          <div class="flex items-center gap-2">
+            <x-lucide-calendar class="h-4 w-4" />
+            <x-tooltip text="{{ $person->howWeMetSpecialDate?->age }}">
+              <span>{{ $person->howWeMetSpecialDate?->date }}</span>
+            </x-tooltip>
+          </div>
+        @endif
+
+        <div class="flex items-center gap-1">
+          <x-lucide-map-pin class="h-4 w-4" />
+          <span>{{ $person->how_we_met_location }}</span>
+        </div>
+      </div>
+      <p class="line-clamp-1 flex-1 text-sm text-gray-600">{{ $person->how_we_met }}</p>
+
+      @if ($person->howWeMetSpecialDate?->should_be_reminded)
+        <div class="absolute -top-2 -right-1 rounded-full border border-gray-200 bg-white p-1">
+          <x-lucide-bell-ring class="h-4 w-4 text-gray-400" />
+        </div>
+      @endif
+    </div>
+  </div>
+
+  <!-- Expanded State -->
+  <div x-cloak x-show="expanded" x-transition class="rounded-lg border border-gray-200 bg-white p-4">
+    <div class="space-y-4">
+      <!-- Meeting Details Grid -->
+      <div class="grid grid-cols-2 gap-4">
+        <!-- Date -->
+        <div class="flex items-center gap-2">
+          <x-lucide-calendar class="h-4 w-4 text-gray-400" />
+          @if ($person->howWeMetSpecialDate)
+            <x-tooltip text="{{ $person->howWeMetSpecialDate?->age }}">
+              <span class="text-sm text-gray-600">{{ $person->howWeMetSpecialDate?->date }}</span>
+            </x-tooltip>
+          @else
+            <span class="text-sm text-gray-600">{{ __('Unknown') }}</span>
+          @endif
+        </div>
+
+        <!-- Location -->
+        @if ($person->how_we_met_location)
+          <div class="flex items-center gap-2">
+            <x-lucide-map-pin class="h-4 w-4 text-gray-400" />
+            <span class="text-sm text-gray-600">{{ $person->how_we_met_location }}</span>
+          </div>
+        @endif
+
+        <!-- Introduced By -->
+        <div class="flex items-center gap-2">
+          <x-lucide-users class="h-4 w-4 text-gray-400" />
+          <span class="text-sm text-gray-600">sdfs</span>
+        </div>
+      </div>
+
+      <!-- Reminder -->
+      @if ($person->howWeMetSpecialDate?->should_be_reminded)
+        <div class="border-t pt-4">
+          <h3 class="mb-2 text-sm font-medium text-gray-900">{{ __('Reminder') }}</h3>
+          <p class="text-sm text-gray-600">{{ __('A reminder will be sent to you on :date.', ['date' => $person->howWeMetSpecialDate?->date]) }}</p>
+        </div>
+      @endif
+
+      <!-- First Impressions -->
+      @if ($person->how_we_met_first_impressions)
+        <div class="border-t pt-4">
+          <h3 class="mb-2 text-sm font-medium text-gray-900">{{ __('First Impressions') }}</h3>
+          <p class="text-sm text-gray-600">{{ $person->how_we_met_first_impressions }}</p>
+        </div>
+      @endif
+
+      <!-- Story -->
+      @if ($person->how_we_met)
+        <div class="border-t pt-4">
+          <h3 class="mb-2 text-sm font-medium text-gray-900">{{ __('The Story') }}</h3>
+          <p class="text-sm whitespace-pre-wrap text-gray-600">{{ $person->how_we_met }}</p>
+        </div>
+      @endif
+    </div>
+  </div>
+</section>
diff --git a/routes/api.php b/routes/api.php
index 6b0622f0..3057e302 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -8,6 +8,7 @@
 use App\Http\Controllers\Api\Administration\AdministrationLogsController;
 use App\Http\Controllers\Api\Administration\AdministrationPruneAccountController;
 use App\Http\Controllers\Api\Administration\MeController;
+use App\Http\Controllers\Api\Administration\MeTimezoneController;
 use App\Http\Controllers\Api\Persons\PersonController;
 use App\Http\Controllers\Api\Persons\PersonNoteController;
 use App\Http\Controllers\Api\Persons\PersonWorkHistoryController;
@@ -17,6 +18,8 @@
     // logged user
     Route::get('me', [MeController::class, 'show'])->name('me');
     Route::put('me', [MeController::class, 'update'])->name('me.update');
+    Route::get('me/timezone', [MeTimezoneController::class, 'show'])->name('me.timezone.show');
+    Route::put('me/timezone', [MeTimezoneController::class, 'update'])->name('me.timezone.update');
 
     // persons
     Route::get('persons', [PersonController::class, 'index'])->name('persons.index');
diff --git a/routes/web.php b/routes/web.php
index 0fb70a30..f97154ce 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -10,6 +10,7 @@
 use App\Http\Controllers\Administration\AdministrationPersonalizationController;
 use App\Http\Controllers\Administration\AdministrationPruneAccountController;
 use App\Http\Controllers\Administration\AdministrationSecurityController;
+use App\Http\Controllers\Administration\AdministrationTimezoneController;
 use App\Http\Controllers\DashboardController;
 use App\Http\Controllers\Instance\InstanceController;
 use App\Http\Controllers\Instance\InstanceDestroyAccountController;
@@ -121,6 +122,7 @@ function (): void {
 
     Route::get('administration', [AdministrationController::class, 'index'])->name('administration.index');
     Route::put('administration', [AdministrationController::class, 'update'])->name('administration.update');
+    Route::put('administration/timezone', [AdministrationTimezoneController::class, 'update'])->name('administration.timezone.update');
     Route::put('administration/avatar', [AdministrationAvatarController::class, 'update'])->name('administration.avatar.update');
     Route::get('administration/logs', [AdministrationLogsController::class, 'index'])->name('administration.logs.index');
 
diff --git a/tests/Feature/Api/Administration/MeTimezoneControllerTest.php b/tests/Feature/Api/Administration/MeTimezoneControllerTest.php
new file mode 100644
index 00000000..d5189102
--- /dev/null
+++ b/tests/Feature/Api/Administration/MeTimezoneControllerTest.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Tests\Feature\Api\Administration;
+
+use App\Models\User;
+use Illuminate\Auth\Events\Registered;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Facades\Event;
+use Laravel\Sanctum\Sanctum;
+use PHPUnit\Framework\Attributes\Test;
+use Tests\TestCase;
+
+class MeTimezoneControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    #[Test]
+    public function it_can_update_user_timezone(): void
+    {
+        $user = User::factory()->create(['timezone' => 'UTC']);
+        Sanctum::actingAs($user);
+
+        $response = $this->json('PUT', '/api/me/timezone', [
+            'timezone' => 'America/New_York',
+        ]);
+
+        $response->assertOk();
+        $this->assertEquals('America/New_York', $user->refresh()->timezone);
+    }
+
+    #[Test]
+    public function it_validates_timezone_input(): void
+    {
+        $user = User::factory()->create(['timezone' => 'UTC']);
+        Sanctum::actingAs($user);
+
+        $response = $this->json('PUT', '/api/me/timezone', [
+            'timezone' => 'Invalid_Timezone',
+        ]);
+
+        $response->assertStatus(422)
+            ->assertJsonValidationErrors(['timezone']);
+    }
+
+    #[Test]
+    public function it_requires_authentication(): void
+    {
+        $response = $this->json('PUT', '/api/me/timezone', [
+            'timezone' => 'Europe/London',
+        ]);
+
+        $response->assertStatus(401);
+    }
+
+    #[Test]
+    public function it_can_get_current_user_timezone(): void
+    {
+        $user = User::factory()->create(['timezone' => 'Europe/Paris']);
+        Sanctum::actingAs($user);
+
+        $response = $this->json('GET', '/api/me/timezone');
+
+        $response->assertOk()
+            ->assertJson([
+                'timezone' => 'Europe/Paris',
+            ]);
+    }
+}
diff --git a/tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php b/tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php
new file mode 100644
index 00000000..d2a80f50
--- /dev/null
+++ b/tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Tests\Feature\Controllers\Administration;
+
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use PHPUnit\Framework\Attributes\Test;
+use Tests\TestCase;
+
+class AdministrationTimezoneControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    #[Test]
+    public function user_can_update_their_timezone(): void
+    {
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        $response = $this->actingAs($user)
+            ->from('/administration')
+            ->put('/administration/timezone', [
+                'timezone' => 'America/New_York',
+            ]);
+
+        $response->assertRedirect('/administration');
+        $response->assertSessionHas('status', 'Changes saved');
+
+        $this->assertEquals('America/New_York', $user->timezone);
+    }
+
+    #[Test]
+    public function timezone_is_required(): void
+    {
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        $response = $this->actingAs($user)
+            ->from('/administration')
+            ->put('/administration/timezone', [
+                'timezone' => '',
+            ]);
+
+        $response->assertRedirect('/administration');
+        $response->assertSessionHasErrors(['timezone' => 'The timezone field is required.']);
+
+        $this->assertEquals('UTC', $user->timezone);
+    }
+
+    #[Test]
+    public function timezone_must_be_a_string(): void
+    {
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        $response = $this->actingAs($user)
+            ->from('/administration')
+            ->put('/administration/timezone', [
+                'timezone' => 123,
+            ]);
+
+        $response->assertRedirect('/administration');
+        $response->assertSessionHasErrors(['timezone' => 'The timezone field must be a string.']);
+
+        $this->assertEquals('UTC', $user->timezone);
+    }
+
+    #[Test]
+    public function unauthenticated_user_cannot_update_timezone(): void
+    {
+        $response = $this->put('/administration/timezone', [
+            'timezone' => 'America/New_York',
+        ]);
+
+        $response->assertRedirect('/login');
+    }
+}
diff --git a/tests/Unit/Services/CreateAccountTest.php b/tests/Unit/Services/CreateAccountTest.php
index 54dd8f33..8d6fce09 100644
--- a/tests/Unit/Services/CreateAccountTest.php
+++ b/tests/Unit/Services/CreateAccountTest.php
@@ -48,11 +48,11 @@ private function executeService(): void
             'account_id' => $user->account_id,
             'email' => 'ross.geller@friends.com',
             'status' => UserStatus::ACTIVE->value,
-            'timezone' => 'UTC',
         ]);
 
         $this->assertEquals('Ross', $user->first_name);
         $this->assertEquals('Geller', $user->last_name);
+        $this->assertEquals('UTC', $user->timezone);
 
         $this->assertInstanceOf(
             User::class,
diff --git a/tests/Unit/Services/UpdateTimezoneTest.php b/tests/Unit/Services/UpdateTimezoneTest.php
new file mode 100644
index 00000000..793d78fc
--- /dev/null
+++ b/tests/Unit/Services/UpdateTimezoneTest.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Tests\Unit\Services;
+
+use App\Jobs\LogUserAction;
+use App\Jobs\UpdateUserLastActivityDate;
+use App\Models\User;
+use App\Services\UpdateTimezone;
+use Illuminate\Foundation\Testing\DatabaseTransactions;
+use Illuminate\Support\Facades\Queue;
+use PHPUnit\Framework\Attributes\Test;
+use Tests\TestCase;
+
+class UpdateTimezoneTest extends TestCase
+{
+    use DatabaseTransactions;
+
+    #[Test]
+    public function it_updates_user_timezone(): void
+    {
+        Queue::fake();
+
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        $updatedUser = (new UpdateTimezone(
+            user: $user,
+            timezone: 'America/New_York',
+        ))->execute();
+
+        $this->assertEquals('America/New_York', $updatedUser->timezone);
+
+        $this->assertInstanceOf(
+            User::class,
+            $updatedUser
+        );
+
+        Queue::assertPushed(UpdateUserLastActivityDate::class, function (UpdateUserLastActivityDate $job) use ($user): bool {
+            return $job->user->id === $user->id;
+        });
+
+        Queue::assertPushed(LogUserAction::class, function (LogUserAction $job) use ($user): bool {
+            return $job->action === 'timezone_update'
+                && $job->user->id === $user->id
+                && $job->description === 'Updated their timezone';
+        });
+    }
+}
\ No newline at end of file


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>resources/views/persons/overview/partials/information.blade.php (1)</summary><blockquote>

`108-108`: **Normalize class ordering for consistency**

The class attributes have inconsistent ordering with `text-sm whitespace-pre-wrap text-gray-600`. Consider standardizing class order throughout the codebase for better maintainability.


```diff
- <p class="text-sm whitespace-pre-wrap text-gray-600">{{ $person->how_we_met }}</p>
+ <p class="text-sm text-gray-600 whitespace-pre-wrap">{{ $person->how_we_met }}</p>
```

</blockquote></details>
<details>
<summary>CONTRIBUTING.md (1)</summary><blockquote>

`25-31`: **List Formatting Update: Consistent Bulleted Style**

The "Coding Conventions" section now reflects a change in the bullet style. According to the PR summary, hyphens have been replaced with plus signs for list items, which enhances consistency with other documentation files (e.g. in CODE_OF_CONDUCT.md). Please verify that the plus sign formatting is correctly rendered in the final Markdown output and is applied uniformly across similar sections.

</blockquote></details>
<details>
<summary>CODE_OF_CONDUCT.md (2)</summary><blockquote>

`20-27`: **Bullet List Formatting: Acceptable Behaviors**

The bullet list for examples of acceptable behavior has been reformatted (removing the hyphen prefixes in favor of a new style, as described in the project guidelines). This update should help maintain a uniform presentation across the documentation. Please double-check that the new format (e.g. using plus signs) renders correctly in all Markdown viewers.

---

`30-37`: **Bullet List Formatting: Unacceptable Behaviors & Wording Suggestion**

Similar to the acceptable behaviors section, the list under "Examples of unacceptable behavior" has been reformatted. Additionally, a static analysis hint suggests varying the wording for improved impact—particularly around line 32 where terms like "trolling, insulting or derogatory comments" are listed. Consider using a synonym or rephrasing to further strengthen the message, if it aligns with your project’s tone.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[style] ~32-~32: Try using a synonym here to strengthen your wording.
Context: ...ind - Trolling, insulting or derogatory comments, and personal or political attacks - Pu...

(COMMENT_REMARK)

</details>

</details>

</blockquote></details>
<details>
<summary>resources/views/layouts/handbook.blade.php (1)</summary><blockquote>

`21-21`: **Breadcrumb HTML indentation adjustment**

The indentation of breadcrumb HTML elements has been significantly modified. While this improves readability in the source code, I notice the indentation is quite extensive. Consider reducing the level of indentation to improve maintainability.

```diff
-                                                                                                              <a href="' .
+              <a href="' .
```


Also applies to: 26-27, 32-33, 36-36

</blockquote></details>
<details>
<summary>database/migrations/2025_03_13_010855_add_timezone_to_users.php (2)</summary><blockquote>

`14-20`: **Consider preserving existing timezone data during migration**

The current implementation drops the existing 'timezone' column before creating the new one with TEXT type. This will result in data loss for any existing user timezone settings in a production environment.


Consider modifying the migration to preserve existing data:

```diff
public function up(): void
{
+    // Create a temporary column and copy existing data
+    Schema::table('users', function (Blueprint $table) {
+        $table->string('timezone_temp')->nullable()->after('nickname');
+    });
+    
+    DB::statement('UPDATE users SET timezone_temp = timezone');
+    
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('timezone');
    });

    Schema::table('users', function (Blueprint $table) {
        $table->text('timezone')->nullable()->after('nickname');
    });
    
+    // Copy data back and remove temporary column
+    DB::statement('UPDATE users SET timezone = timezone_temp');
+    
+    Schema::table('users', function (Blueprint $table) {
+        $table->dropColumn('timezone_temp');
+    });
}
```

Don't forget to add the `use Illuminate\Support\Facades\DB;` statement at the top of the file if you implement this approach.

---

`28-34`: **Ensure rollback handles potential data loss**

The down method also drops and recreates the timezone column, which would result in data loss during a rollback.


Consider applying the same data preservation approach to the down method:

```diff
public function down(): void
{
+    // Create a temporary column and copy existing data
+    Schema::table('users', function (Blueprint $table) {
+        $table->string('timezone_temp')->nullable()->after('nickname');
+    });
+    
+    DB::statement('UPDATE users SET timezone_temp = timezone');
+    
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('timezone');
    });

    Schema::table('users', function (Blueprint $table) {
        $table->string('timezone')->nullable()->after('nickname');
    });
    
+    // Copy data back and remove temporary column
+    DB::statement('UPDATE users SET timezone = timezone_temp');
+    
+    Schema::table('users', function (Blueprint $table) {
+        $table->dropColumn('timezone_temp');
+    });
}
```

</blockquote></details>
<details>
<summary>resources/views/administration/partials/logs.blade.php (1)</summary><blockquote>

`1-37`: **LGTM! Clean, well-structured logs partial**

The logs partial is well-organized with clear parameter documentation, proper component usage, and responsive styling.



Consider ensuring that the timestamps shown at line 28 are formatted according to the user's timezone preference, which is the focus of this PR. This might already be happening in the controller, but if not, you could format it here using the authenticated user's timezone.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a2aec5090267fe85a28e7b2254a40bac81a9fd28 and f55809c46bd03fce6ad3a707e6e9c9f2153e329e.

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `.github/dependabot.yml` (1 hunks)
* `CODE_OF_CONDUCT.md` (1 hunks)
* `CONTRIBUTING.md` (1 hunks)
* `TODO` (1 hunks)
* `app/Models/User.php` (2 hunks)
* `database/migrations/2025_03_13_010855_add_timezone_to_users.php` (1 hunks)
* `resources/css/app.css` (0 hunks)
* `resources/views/administration/index.blade.php` (1 hunks)
* `resources/views/administration/partials/avatar.blade.php` (1 hunks)
* `resources/views/administration/partials/logs.blade.php` (1 hunks)
* `resources/views/administration/partials/profile.blade.php` (1 hunks)
* `resources/views/administration/partials/timezone.blade.php` (1 hunks)
* `resources/views/layouts/handbook.blade.php` (1 hunks)
* `resources/views/persons/overview/index.blade.php` (1 hunks)
* `resources/views/persons/overview/partials/information.blade.php` (1 hunks)
* `tests/Unit/Services/CreateAccountTest.php` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* resources/css/app.css

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>CODE_OF_CONDUCT.md</summary>

[style] ~32-~32: Try using a synonym here to strengthen your wording.
Context: ...ind - Trolling, insulting or derogatory comments, and personal or political attacks - Pu...

(COMMENT_REMARK)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (13)</summary><blockquote>

<details>
<summary>resources/views/persons/overview/index.blade.php (1)</summary>

`8-9`: **LGTM: Including the new information partial**

The addition of the information partial view is a good improvement to modularize the UI components.

</details>
<details>
<summary>resources/views/persons/overview/partials/information.blade.php (4)</summary>

`16-20`: **Check the Show more/less link target and route**

The "Show more"/"Show less" toggle has the `x-target="edit-how-we-met-form"` attribute but uses the route `route('persons.how-we-met.create', $person->slug)`. This appears to be mixing toggle functionality with a navigation link, which could cause confusion. Consider using a button with a click handler for the toggle action instead of a link with a route.

---

`21-24`: **Similar issue with the Edit button target attribute**

The Edit button also has `x-target="edit-how-we-met-form"` but correctly uses an edit route. If `x-target` is a custom directive for targeting forms, ensure it's implemented consistently.

---

`7-9`: **LGTM: Well-implemented Alpine.js toggle functionality**

The Alpine.js implementation for the expandable section is clean and properly uses the person's preference (`$person->how_we_met_shown`) to determine the initial state.

---

`28-54`: **LGTM: Well-structured collapsed and expanded states**

The collapsed and expanded state implementations are well-organized with appropriate conditional rendering and good user experience considerations.



Also applies to: 56-112

</details>
<details>
<summary>.github/dependabot.yml (1)</summary>

`4-4`: **Consistent use of single quotes in YAML configuration**

The quotes in the YAML configuration have been standardized to single quotes. This is a good styling consistency improvement that doesn't affect functionality.



Also applies to: 6-6, 9-9, 12-14, 18-19, 21-21, 23-23

</details>
<details>
<summary>resources/views/administration/partials/avatar.blade.php (1)</summary>

`7-9`: **Improved UI with descriptive header for profile photo section**

Adding a header and descriptive text for the profile photo section improves user experience by clearly explaining the purpose of this section.

</details>
<details>
<summary>resources/views/administration/partials/profile.blade.php (1)</summary>

`7-10`: **Added clear heading for profile section**

Adding a heading to the profile section improves the UI structure and provides clear visual hierarchy. This change aligns with the improvements in the avatar section, creating a consistent UI pattern across administration components.

</details>
<details>
<summary>app/Models/User.php (2)</summary>

`34-34`: **LGTM! Appropriate model update for timezone support**

Adding 'timezone' to the fillable array allows it to be mass-assigned through forms or API requests.

---

`75-75`: **Good security practice using encryption for timezone**

Encrypting the timezone value is a good security practice for storing user preferences.


This change also aligns with the migration's update from string to text type, as encrypted values require more storage space.

</details>
<details>
<summary>tests/Unit/Services/CreateAccountTest.php (1)</summary>

`55-55`: **LGTM! Good test coverage for default timezone**

The assertion ensures that new user accounts have 'UTC' set as the default timezone, which is an important check for the new timezone functionality.

</details>
<details>
<summary>TODO (1)</summary>

`11-17`: **LGTM - Task reorganization looks good**

The reorganization of related tasks under "add a block" creates a clearer structure and better organization of related features that need to be implemented.

</details>
<details>
<summary>resources/views/administration/index.blade.php (1)</summary>

`26-27`: **LGTM - Good modularization of logs component**

The replacement of inline logs display with a partial view is a good refactoring that improves code organization and maintainability.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-13T10:43:31Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>app/Http/Controllers/Administration/AdministrationTimezoneController.php (2)</summary><blockquote>

`8-9`: **Unused imports.**

The `Log` and `User` classes are imported but not used in the controller.

```diff
-use App\Models\Log;
-use App\Models\User;
```

---

`11-16`: **Some imports are unused.**

The following imports are not used in this controller:
- UpdateUserInformation
- Rule
- View

```diff
-use App\Services\UpdateUserInformation;
-use Illuminate\Validation\Rule;
-use Illuminate\View\View;
```

</blockquote></details>
<details>
<summary>app/Services/UpdateTimezone.php (3)</summary><blockquote>

`1-15`: **Remove unused imports**

The file imports several exception classes and other components that aren't being used in the code:
- `Carbon\Exceptions\InvalidDateException`
- `Carbon\Exceptions\InvalidFormatException`
- `Illuminate\Auth\Events\Registered`
- `InvalidArgumentException`

These should be removed to keep the imports clean and avoid confusion.

---

`39-42`: **Consider handling job dispatch failures**

The code doesn't handle potential failures when dispatching the UpdateUserLastActivityDate job. Consider adding error handling to ensure robustness.


```diff
 private function updateUserLastActivityDate(): void
 {
-    UpdateUserLastActivityDate::dispatch($this->user);
+    try {
+        UpdateUserLastActivityDate::dispatch($this->user);
+    } catch (\Throwable $e) {
+        // Log the error but continue execution
+        \Log::error('Failed to update user last activity date: ' . $e->getMessage(), [
+            'user_id' => $this->user->id,
+        ]);
+    }
 }
```

---

`44-51`: **Consider handling job dispatch failures for logging**

Similar to the previous comment, add error handling for the LogUserAction job dispatch.


```diff
 private function log(): void
 {
-    LogUserAction::dispatch(
-        user: $this->user,
-        action: 'timezone_update',
-        description: 'Updated their timezone',
-    );
+    try {
+        LogUserAction::dispatch(
+            user: $this->user,
+            action: 'timezone_update',
+            description: 'Updated their timezone',
+        );
+    } catch (\Throwable $e) {
+        // Log the error but continue execution
+        \Log::error('Failed to log user action: ' . $e->getMessage(), [
+            'user_id' => $this->user->id,
+            'action' => 'timezone_update',
+        ]);
+    }
 }
```

</blockquote></details>
<details>
<summary>tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php (2)</summary><blockquote>

`12-91`: **Add test for invalid timezone validation**

The tests currently check for empty and non-string timezones, but don't verify that invalid timezone strings are rejected. Add a test to ensure proper validation of timezone identifiers.


```diff
+    #[Test]
+    public function timezone_must_be_a_valid_timezone_identifier(): void
+    {
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        $response = $this->actingAs($user)
+            ->from('/administration')
+            ->put('/administration/timezone', [
+                'timezone' => 'Invalid/Timezone',
+            ]);
+
+        $response->assertRedirect('/administration');
+        $response->assertSessionHasErrors(['timezone' => 'The timezone must be a valid timezone identifier.']);
+
+        $this->assertDatabaseHas('users', [
+            'id' => $user->id,
+            'timezone' => 'UTC', // Timezone should remain unchanged
+        ]);
+    }
```

---

`12-91`: **Add test to verify log creation**

Add a test to verify that updating the timezone creates a log entry and updates the user's last activity date.


```diff
+    #[Test]
+    public function updating_timezone_creates_log_and_updates_activity_date(): void
+    {
+        $user = User::factory()->create([
+            'timezone' => 'UTC',
+        ]);
+
+        // Mock the dispatch for LogUserAction to verify it's called
+        \Queue::fake();
+
+        $response = $this->actingAs($user)
+            ->from('/administration')
+            ->put('/administration/timezone', [
+                'timezone' => 'America/New_York',
+            ]);
+
+        // Assert the timezone was updated
+        $this->assertDatabaseHas('users', [
+            'id' => $user->id,
+            'timezone' => 'America/New_York',
+        ]);
+
+        // Assert the jobs were dispatched
+        \Queue::assertPushed(LogUserAction::class, function ($job) use ($user) {
+            return $job->user->id === $user->id &&
+                   $job->action === 'timezone_update';
+        });
+
+        \Queue::assertPushed(UpdateUserLastActivityDate::class, function ($job) use ($user) {
+            return $job->user->id === $user->id;
+        });
+    }
```

Note: You'll need to import the job classes at the top of the file:
```php
use App\Jobs\LogUserAction;
use App\Jobs\UpdateUserLastActivityDate;
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a966befa3df61d5963e39c0cc8dd21b2b606446b and 23f634b5cfec87df3310b451820cc0febc13c55e.

</details>

<details>
<summary>📒 Files selected for processing (7)</summary>

* `app/Http/Controllers/Administration/AdministrationTimezoneController.php` (1 hunks)
* `app/Services/UpdateTimezone.php` (1 hunks)
* `config/peopleos.php` (1 hunks)
* `resources/views/administration/partials/timezone.blade.php` (1 hunks)
* `routes/web.php` (2 hunks)
* `tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php` (1 hunks)
* `tests/Unit/Services/UpdateTimezoneTest.php` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* resources/views/administration/partials/timezone.blade.php

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>app/Http/Controllers/Administration/AdministrationTimezoneController.php</summary>

[warning] 20-20: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L20
Added line #L20 was not covered by tests

---

[warning] 22-24: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L22-L24
Added lines #L22 - L24 were not covered by tests

---

[warning] 26-29: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L26-L29
Added lines #L26 - L29 were not covered by tests

---

[warning] 31-32: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L31-L32
Added lines #L31 - L32 were not covered by tests

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>config/peopleos.php (1)</summary>

`74-74`: **Good addition of timezone update action.**

The new 'timezone_update' action entry aligns well with the existing action logging structure and supports the new timezone functionality being implemented.

</details>
<details>
<summary>routes/web.php (2)</summary>

`13-13`: **LGTM - Controller import added correctly.**

The import for the new AdministrationTimezoneController follows the established pattern for controller imports.

---

`125-125`: **Route placement and naming are consistent.**

The new route for timezone updates follows the existing pattern for administration routes - properly placed in the authenticated routes group and using consistent naming conventions.

</details>
<details>
<summary>tests/Unit/Services/UpdateTimezoneTest.php (1)</summary>

`16-51`: **Test case looks comprehensive.**

The test effectively verifies the complete functionality of the UpdateTimezone service:
- Uses DatabaseTransactions for proper test isolation
- Tests the timezone update functionality
- Verifies queued jobs for activity tracking and logging
- Follows the project's testing patterns

</details>
<details>
<summary>app/Http/Controllers/Administration/AdministrationTimezoneController.php (1)</summary>

`20-33`: <details>
<summary>❌ Incorrect review comment</summary>

**Test coverage missing for controller.**

The static analysis indicates this controller isn't covered by tests, despite having a unit test for the underlying service. Feature tests should be added to ensure the HTTP endpoint works correctly.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if feature tests exist for this controller
fd "AdministrationTimezoneControllerTest" tests/Feature
```

Length of output: 137

---



</details>

**Correction: Feature tests exist for this controller**

The static analysis claim that the controller lacks test coverage appears to be incorrect. We found a feature test at `tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php` which covers the HTTP endpoint for `AdministrationTimezoneController`. No additional tests need to be added at this time.



> Likely an incorrect or invalid review comment.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 20-20: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L20
Added line #L20 was not covered by tests

---

[warning] 22-24: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L22-L24
Added lines #L22 - L24 were not covered by tests

---

[warning] 26-29: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L26-L29
Added lines #L26 - L29 were not covered by tests

---

[warning] 31-32: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L31-L32
Added lines #L31 - L32 were not covered by tests

</details>

</details>

</details>
<details>
<summary>app/Services/UpdateTimezone.php (2)</summary>

`16-21`: **Constructor implementation looks good**

The constructor is well-designed with properly typed parameters using PHP 8's constructor property promotion.

---

`23-30`: **Execute method is well-structured**

The execute method follows a good pattern by orchestrating multiple operations and returning the updated user. The code is clean and readable.

</details>
<details>
<summary>tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php (4)</summary>

`16-36`: **Test for happy path looks good**

The test for successfully updating a timezone is well-implemented. It verifies the redirect, session message, and database state.

---

`38-58`: **Validation test for required field is thorough**

Good test for ensuring the timezone field is required, checking both the error message and confirming the database wasn't updated.

---

`60-80`: **String validation test is well-implemented**

Properly tests the validation of the timezone field to ensure it's a string.

---

`82-90`: **Authentication test is good**

The test for unauthenticated access is simple and effective.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-13T23:21:11Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>app/Http/Controllers/Administration/AdministrationTimezoneController.php (2)</summary><blockquote>

`8-9`: **Remove unused imports.**

The following imports are declared but not used in this controller:
- `App\Models\Log` (line 8)
- `App\Models\User` (line 9)
- `App\Services\UpdateUserInformation` (line 11)
- `Illuminate\Validation\Rule` (line 15)
- `Illuminate\View\View` (line 16)

Removing unused imports helps maintain cleaner code and prevents confusion.

```diff
-use App\Models\Log;
-use App\Models\User;
use App\Services\UpdateTimezone;
-use App\Services\UpdateUserInformation;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
-use Illuminate\Validation\Rule;
-use Illuminate\View\View;
```


Also applies to: 11-11, 15-16

---

`31-32`: **Consider extracting the success message to a language file.**

The success message is currently hardcoded in the translation function. Consider moving it to a language file for better maintainability and consistency.

```diff
-            ->with('status', trans('Changes saved'));
+            ->with('status', trans('administration.timezone.updated'));
```

Then define this in a language file, for example in `resources/lang/en/administration.php`:

```php
return [
    'timezone' => [
        'updated' => 'Changes saved',
    ],
];
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 084aa470363b9f5c9a544de8d132b424fe6b964f and 9bf44f7f6501c4424c0af94aefff33cbdee7318a.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `app/Http/Controllers/Administration/AdministrationTimezoneController.php` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>app/Http/Controllers/Administration/AdministrationTimezoneController.php (3)</summary>

`22-24`: **Good use of timezone validation.**

The code correctly uses the `timezone:all` validation rule to ensure that only valid PHP timezones are accepted, addressing the previous review comment that suggested adding timezone validation.

---

`26-29`: **Concise service instantiation and execution.**

Good use of named parameters for clarity and the service pattern for encapsulating business logic. This keeps the controller thin and focused on handling the HTTP request lifecycle.

---

`20-33`: <details>
<summary>❓ Verification inconclusive</summary>

**Add test coverage for this controller method.**

According to the past review comments, lines 22-24 were flagged as not covered by tests. Ensure comprehensive test coverage for the validation logic and redirection in this controller method.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are tests for this controller method

echo "Checking for tests covering AdministrationTimezoneController::update"
rg -i --type php "class.*AdministrationTimezoneController.*Test" tests
rg -i --type php "test.*timezone" tests/Feature/Controllers/Administration
```

Length of output: 438

---

---

*Once the script outputs are reviewed and the test file confirms that the controller method update is indeed being covered, the review comment can be updated accordingly.*

---

**Final Review Comment:**

The test file `tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php` now exists and appears to cover the update method of the `AdministrationTimezoneController`. However, please ensure that the test explicitly validates the validation logic and the proper redirection behavior, including checking for the status message, matching previous review recommendations.

- Verify that test cases authenticate a user.
- Confirm that test cases include assertions for validation errors when invalid timezones are provided.
- Ensure the redirection target and session message (`'Changes saved'`) are tested.

Please update or add tests accordingly if any of these aspects are missing.



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify if AdministrationTimezoneControllerTest.php calls the update method or related functionality in tests

echo "Checking for 'update(' keyword in AdministrationTimezoneControllerTest.php"
rg -i "update(" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php

echo "Checking for redirection assertions (e.g., redirect()->route) in AdministrationTimezoneControllerTest.php"
rg -i "redirect()->route" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php

echo "Checking for status variable (e.g., with('status', ...) in AdministrationTimezoneControllerTest.php"
rg -i "with(.*'status'" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php
```

Length of output: 1060

---

Could you please run the above script and share its output? This will help us verify that the test file explicitly covers the following:

- The invocation of the `update()` method.
- The redirection behavior via `redirect()->route`.
- The assertion for the session status message (e.g., using `with('status', ...)`).

Once confirmed, we can finalize the review comment with confidence.

---

🏁 Script executed:

```shell
#!/bin/bash
# Re-run verification with properly escaped regex patterns

echo "Checking for 'update(' keyword in AdministrationTimezoneControllerTest.php"
rg -i "update\(" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php

echo "Checking for redirection assertions (redirect()->route) in AdministrationTimezoneControllerTest.php"
rg -i "redirect\(\)->route" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php

echo "Checking for session status assertions (with('status') in AdministrationTimezoneControllerTest.php"
rg -i "with\('status'" tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php
```

Length of output: 912

---



</details>

**Action Required: Enhance Test Coverage for the `update()` Method**

It appears that although the test file  
`tests/Feature/Controllers/Administration/AdministrationTimezoneControllerTest.php` exists, our automated checks did not reveal explicit tests that verify the key behaviors of the `update()` method. In particular, we couldn’t confirm that the test invokes the `update()` method, checks for the redirection via `redirect()->route('administration.index')`, or asserts the session status message set with `with('status', trans('Changes saved'))`.

Please ensure that your tests:
- Explicitly call the controller’s `update()` method or simulate a request that triggers it.
- Validate the input logic (especially for the `timezone` field) and confirm that validation errors occur for invalid inputs.
- Assert the redirection target and check for the presence of the correct session status message.

If any of these aspects aren’t currently covered, kindly update the test cases accordingly.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-14T01:03:58Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>tests/Feature/Api/Administration/MeTimezoneControllerTest.php (4)</summary><blockquote>

`8-10`: **Unused imports found.**

The `Illuminate\Auth\Events\Registered` and `Illuminate\Support\Facades\Event` imports are not being used in this class. It's best practice to remove unused imports to keep the code clean.

```diff
-use Illuminate\Auth\Events\Registered;
-use Illuminate\Support\Facades\Event;
```

---

`33-45`: **Consider expanding validation tests.**

The current test only checks for one invalid timezone format. Consider adding tests for other validation scenarios:
1. Empty timezone string
2. Null timezone value  
3. Various malformed timezone strings

This will ensure more comprehensive validation coverage.

---

`15-70`: **Consider adding a test for the same timezone case.**

When a user attempts to update their timezone to the same value they already have, the system should handle this gracefully. Consider adding a test to verify this behavior.

```php
#[Test]
public function it_handles_updating_to_same_timezone(): void
{
    $user = User::factory()->create(['timezone' => 'Europe/Paris']);
    Sanctum::actingAs($user);

    $response = $this->json('PUT', '/api/me/timezone', [
        'timezone' => 'Europe/Paris',
    ]);

    $response->assertOk();
    $this->assertEquals('Europe/Paris', $user->refresh()->timezone);
}
```

---

`15-70`: **Consider adding a test for missing timezone value.**

It would be valuable to test what happens when the timezone field is missing entirely in the request payload, as this is a common client error.

```php
#[Test]
public function it_validates_missing_timezone(): void
{
    $user = User::factory()->create(['timezone' => 'UTC']);
    Sanctum::actingAs($user);

    $response = $this->json('PUT', '/api/me/timezone', []);

    $response->assertStatus(422)
        ->assertJsonValidationErrors(['timezone']);
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9bf44f7f6501c4424c0af94aefff33cbdee7318a and 458aeea72ffcfbbe1ba0063501d7dc7f554a8d8a.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `app/Http/Controllers/Api/Administration/MeTimezoneController.php` (1 hunks)
* `config/marketing-timestamps.php` (3 hunks)
* `resources/views/marketing/docs/api/profile.blade.php` (3 hunks)
* `routes/api.php` (2 hunks)
* `tests/Feature/Api/Administration/MeTimezoneControllerTest.php` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (15)</summary><blockquote>

<details>
<summary>routes/api.php (2)</summary>

`11-11`: **Import looks good for the new controller.**

The import statement for the `MeTimezoneController` has been properly added, which is required for the new timezone-related routes.

---

`21-22`: **Routes properly structured for timezone management.**

The added routes follow Laravel best practices with:
- Clear route naming conventions (`me.timezone.show` and `me.timezone.update`)
- Proper HTTP verbs (GET for retrieval, PUT for updates)
- Correct controller method mapping
- Placement in the logical section with other user-related routes

These routes will integrate well with the existing user management functionality.

</details>
<details>
<summary>config/marketing-timestamps.php (1)</summary>

`11-12`: **Documentation timestamps correctly updated.**

The timestamp updates reflect the recent changes to the documentation for the shipping and API profile pages, which now include information about the new timezone functionality.



Also applies to: 27-28

</details>
<details>
<summary>app/Http/Controllers/Api/Administration/MeTimezoneController.php (5)</summary>

`1-10`: **Controller setup follows project standards.**

The file header includes proper namespace, imports, and strict type declaration, adhering to the project's coding standards.

---

`13-19`: **Show method is concise and appropriate.**

The `show` method efficiently retrieves and returns the user's timezone from the authenticated request user object. The implementation is straightforward and follows RESTful principles.

---

`24-28`: **Good validation for timezone input.**

The validation rules ensure:
1. The timezone is required
2. It's a string
3. It's a valid timezone (using Laravel's `timezone:all` validation rule)

This prevents invalid data from being processed.

---

`30-34`: **Separation of concerns with dedicated service.**

Using the `UpdateTimezone` service class for the update logic follows good software design principles by separating concerns. This makes the code more maintainable and testable.

---

`35-40`: **Consistent response format.**

The response includes both the user ID and the updated timezone, maintaining consistency with other API endpoints in the application.

</details>
<details>
<summary>resources/views/marketing/docs/api/profile.blade.php (4)</summary>

`14-19`: **Table of contents updated appropriately.**

The new timezone endpoints are properly documented in the table of contents with clear, descriptive link text.

---

`39-46`: **Endpoint listing includes new timezone routes.**

The endpoints section now includes the new timezone routes with appropriate HTTP methods, making it easy for API consumers to understand the available endpoints.

---

`203-243`: **GET timezone endpoint documentation is complete.**

The documentation for the GET timezone endpoint includes:
- Clear description of purpose
- Properly structured parameter section (noting there are no parameters)
- Well-defined response attributes
- Example JSON response showing the timezone format

This provides API consumers with all the information needed to use this endpoint.

---

`245-286`: **PUT timezone endpoint documentation is thorough.**

The documentation for updating a user's timezone:
- Clearly explains the purpose
- Specifies that the timezone must be a valid IANA identifier
- Properly documents the required parameter
- Includes example response format

This documentation will help developers understand how to correctly format their requests to update timezones.

</details>
<details>
<summary>tests/Feature/Api/Administration/MeTimezoneControllerTest.php (3)</summary>

`19-31`: **Test looks good for the happy path.**

The test correctly validates that a user can update their timezone through the API. It sets up the test user, authenticates them, makes the request, and verifies both the response status and the actual database update.

---

`47-55`: **Authentication requirement test is solid.**

This test correctly verifies that unauthenticated users cannot update their timezone, which is an important security check.

---

`57-69`: **GET endpoint test is well implemented.**

The test correctly verifies that an authenticated user can retrieve their timezone. It sets up the test data, makes the request, and verifies both the response status and content.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-14T01:20:44Z

Review comments:
diff_hunk: @@ -0,0 +1,113 @@
+<?php
+/*
+ * @var Person $person
+ */
+?>
+
+<section id="edit-how-we-met-form" x-data="{
+  expanded: {{ $person->how_we_met_shown ? 'true' : 'false' }},
+}" class="mb-8">
+  <div class="mb-4 flex items-center justify-between">
+    <div class="flex items-center gap-2">
+      <x-lucide-user class="h-5 w-5 text-purple-500" />
+      <h2 class="text-lg font-semibold text-gray-900">{{ __('Information') }}</h2>
+    </div>
+    <div class="flex items-center gap-2">
+      <a x-target="edit-how-we-met-form" href="{{ route('persons.how-we-met.create', $person->slug) }}" class="inline-flex cursor-pointer items-center gap-1 rounded-md bg-gray-50 px-2 py-1 text-sm font-medium text-gray-600 hover:bg-gray-200">
+        <span x-text="expanded ? '{{ __('Show less') }}' : '{{ __('Show more') }}'"></span>
+        <x-lucide-chevron-down x-show="!expanded" class="h-4 w-4" />
+        <x-lucide-chevron-up x-show="expanded" class="h-4 w-4" />
+      </a>
+      <a x-target="edit-how-we-met-form" href="{{ route('persons.how-we-met.edit', $person->slug) }}" class="inline-flex items-center gap-1 rounded-md bg-purple-50 px-3 py-2 text-sm font-medium text-purple-600 hover:bg-purple-100">
+        <x-lucide-pencil class="mr-1 h-3 w-3" />
+        {{ __('Edit') }}
+      </a>
+    </div>
+  </div>
+
+  <!-- Collapsed State -->
+  <div x-show="!expanded" x-transition class="rounded-lg border border-gray-200 bg-white">
+    <div class="relative flex items-center gap-4 p-4">
+      <div class="flex shrink-0 items-center gap-3 text-sm text-gray-500">
+        @if ($person->howWeMetSpecialDate)
+          <div class="flex items-center gap-2">
+            <x-lucide-calendar class="h-4 w-4" />
+            <x-tooltip text="{{ $person->howWeMetSpecialDate?->age }}">
+              <span>{{ $person->howWeMetSpecialDate?->date }}</span>
+            </x-tooltip>
+          </div>
+        @endif
+
+        <div class="flex items-center gap-1">
+          <x-lucide-map-pin class="h-4 w-4" />
+          <span>{{ $person->how_we_met_location }}</span>
+        </div>
+      </div>
+      <p class="line-clamp-1 flex-1 text-sm text-gray-600">{{ $person->how_we_met }}</p>
+
+      @if ($person->howWeMetSpecialDate?->should_be_reminded)
+        <div class="absolute -top-2 -right-1 rounded-full border border-gray-200 bg-white p-1">
+          <x-lucide-bell-ring class="h-4 w-4 text-gray-400" />
+        </div>
+      @endif
+    </div>
+  </div>
+
+  <!-- Expanded State -->
+  <div x-cloak x-show="expanded" x-transition class="rounded-lg border border-gray-200 bg-white p-4">
+    <div class="space-y-4">
+      <!-- Meeting Details Grid -->
+      <div class="grid grid-cols-2 gap-4">
+        <!-- Date -->
+        <div class="flex items-center gap-2">
+          <x-lucide-calendar class="h-4 w-4 text-gray-400" />
+          @if ($person->howWeMetSpecialDate)
+            <x-tooltip text="{{ $person->howWeMetSpecialDate?->age }}">
+              <span class="text-sm text-gray-600">{{ $person->howWeMetSpecialDate?->date }}</span>
+            </x-tooltip>
+          @else
+            <span class="text-sm text-gray-600">{{ __('Unknown') }}</span>
+          @endif
+        </div>
+
+        <!-- Location -->
+        @if ($person->how_we_met_location)
+          <div class="flex items-center gap-2">
+            <x-lucide-map-pin class="h-4 w-4 text-gray-400" />
+            <span class="text-sm text-gray-600">{{ $person->how_we_met_location }}</span>
+          </div>
+        @endif
+
+        <!-- Introduced By -->
+        <div class="flex items-center gap-2">
+          <x-lucide-users class="h-4 w-4 text-gray-400" />
+          <span class="text-sm text-gray-600">sdfs</span>
path: resources/views/persons/overview/partials/information.blade.php
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix placeholder text "sdfs" in Introduced By section**

The text "sdfs" appears to be placeholder text and should be replaced with actual data from the person model, likely something like `{{ $person->introduced_by ?? __('Unknown') }}`.


```diff
- <span class="text-sm text-gray-600">sdfs</span>
+ <span class="text-sm text-gray-600">{{ $person->introduced_by ?? __('Unknown') }}</span>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
          <span class="text-sm text-gray-600">{{ $person->introduced_by ?? __('Unknown') }}</span>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T10:43:29Z
author_association: NONE
diff_hunk: @@ -0,0 +1,83 @@
+<?php
+/*
+ * @var \App\Models\Person $person
+ */
+?>
path: resources/views/administration/partials/timezone.blade.php
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix PHPDoc comment to use User model instead of Person model**

The PHPDoc comment specifies a Person model, but based on the file inclusion in `index.blade.php`, this partial receives a User model. The comment should be updated to match the actual variable being used.

```diff
- * @var \App\Models\Person $person
+ * @var \App\Models\User $user
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
<?php
/*
 * @var \App\Models\User $user
 */
?>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Fixed in commits e39777f to a966bef
created_at: 2025-03-13T10:43:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,83 @@
+<?php
+/*
+ * @var \App\Models\Person $person
+ */
+?>
+
+<h2 class="font-semi-bold mb-1 text-lg">
+  {{ __('Timezone') }}
+</h2>
+
+<form action="{{ route('administration.update') }}" method="post" class="mb-8 border border-gray-200 bg-white sm:rounded-lg" x-data="{ showActions: false }">
+  @csrf
+  @method('put')
+
+  <!-- timezone -->
+  <div class="grid grid-cols-3 items-center rounded-t-lg p-3 hover:bg-blue-50 last:rounded-b-lg">
+    <x-input-label for="timezone" :value="__('Timezone')" class="col-span-1" />
+    <div class="w-full justify-self-end col-span-2">
+      <select @focus="showActions = true" @blur="showActions = false" id="timezone" name="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 disabled:text-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
+        <optgroup label="UTC">
+            <option value="UTC">Coordinated Universal Time (UTC) - UTC+00:00</option>
+        </optgroup>
+        <optgroup label="Africa">
+            <option value="Africa/Casablanca">Western European Time (WET) - UTC+00:00</option>
+            <option value="Africa/Lagos">West Africa Time (WAT) - UTC+01:00</option>
+            <option value="Africa/Cairo">Eastern European Time (EET) - UTC+02:00</option>
+            <option value="Africa/Johannesburg">South Africa Standard Time (SAST) - UTC+02:00</option>
+        </optgroup>
+        <optgroup label="America">
+            <option value="America/St_Johns">Newfoundland Standard Time (NST) - UTC-03:30</option>
+            <option value="America/Halifax">Atlantic Standard Time (AST) - UTC-04:00</option>
+            <option value="America/New_York">Eastern Standard Time (EST) - UTC-05:00</option>
+            <option value="America/Chicago">Central Standard Time (CST) - UTC-06:00</option>
+            <option value="America/Denver">Mountain Standard Time (MST) - UTC-07:00</option>
+            <option value="America/Los_Angeles">Pacific Standard Time (PST) - UTC-08:00</option>
+            <option value="America/Anchorage">Alaska Standard Time (AKST) - UTC-09:00</option>
+            <option value="Pacific/Honolulu">Hawaii-Aleutian Standard Time (HAST) - UTC-10:00</option>
+        </optgroup>
+        <optgroup label="Antarctica">
+            <option value="Antarctica/Palmer">Chile Summer Time (CLST) - UTC-03:00</option>
+            <option value="Antarctica/McMurdo">New Zealand Standard Time (NZST) - UTC+12:00</option>
+        </optgroup>
+        <optgroup label="Asia">
+            <option value="Asia/Dubai">Gulf Standard Time (GST) - UTC+04:00</option>
+            <option value="Asia/Karachi">Pakistan Standard Time (PKT) - UTC+05:00</option>
+            <option value="Asia/Kolkata">India Standard Time (IST) - UTC+05:30</option>
+            <option value="Asia/Dhaka">Bangladesh Standard Time (BST) - UTC+06:00</option>
+            <option value="Asia/Jakarta">Western Indonesia Time (WIB) - UTC+07:00</option>
+            <option value="Asia/Shanghai">China Standard Time (CST) - UTC+08:00</option>
+            <option value="Asia/Tokyo">Japan Standard Time (JST) - UTC+09:00</option>
+            <option value="Asia/Seoul">Korea Standard Time (KST) - UTC+09:00</option>
+        </optgroup>
+        <optgroup label="Australia">
+            <option value="Australia/Perth">Australian Western Standard Time (AWST) - UTC+08:00</option>
+            <option value="Australia/Adelaide">Australian Central Standard Time (ACST) - UTC+09:30</option>
+            <option value="Australia/Sydney">Australian Eastern Standard Time (AEST) - UTC+10:00</option>
+        </optgroup>
+        <optgroup label="Europe">
+            <option value="Europe/London">Greenwich Mean Time (GMT) - UTC+00:00</option>
+            <option value="Europe/Berlin">Central European Time (CET) - UTC+01:00</option>
+            <option value="Europe/Moscow">Moscow Standard Time (MSK) - UTC+03:00</option>
+        </optgroup>
+        <optgroup label="Pacific">
+            <option value="Pacific/Auckland">New Zealand Standard Time (NZST) - UTC+12:00</option>
+            <option value="Pacific/Fiji">Fiji Time (FJT) - UTC+12:00</option>
+        </optgroup>
+    </select>
+
+      <x-input-error class="mt-2" :messages="$errors->get('timezone')" />
+    </div>
+  </div>
+
+  <!-- actions -->
+  <div x-cloak x-show="showActions" x-transition:enter="transition duration-200 ease-out" x-transition:enter-start="-translate-y-2 transform opacity-0" x-transition:enter-end="translate-y-0 transform opacity-100" x-transition:leave="transition duration-150 ease-in" x-transition:leave-start="translate-y-0 transform opacity-100" x-transition:leave-end="-translate-y-2 transform opacity-0" class="flex justify-between border-t border-gray-200 p-3">
+    <x-button.secondary wire:click="toggleAddMode" class="mr-2">
+      {{ __('Cancel') }}
+    </x-button.secondary>
path: resources/views/administration/partials/timezone.blade.php
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Replace wire:click with appropriate Alpine.js directive**

The form uses Alpine.js for reactivity (`x-data`, `x-show`), but the Cancel button uses Livewire's `wire:click` directive. Since there's no Livewire component declared, this will not work as expected.

```diff
- <x-button.secondary wire:click="toggleAddMode" class="mr-2">
+ <x-button.secondary @click="showActions = false" type="button" class="mr-2">
    {{ __('Cancel') }}
</x-button.secondary>
```

This change ensures the cancel button works correctly with the Alpine.js reactivity system used in the form.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    <x-button.secondary @click="showActions = false" type="button" class="mr-2">
      {{ __('Cancel') }}
    </x-button.secondary>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Fixed in commits e39777f to a966bef
created_at: 2025-03-13T10:43:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,83 @@
+<?php
+/*
+ * @var \App\Models\Person $person
+ */
+?>
+
+<h2 class="font-semi-bold mb-1 text-lg">
+  {{ __('Timezone') }}
+</h2>
+
+<form action="{{ route('administration.update') }}" method="post" class="mb-8 border border-gray-200 bg-white sm:rounded-lg" x-data="{ showActions: false }">
+  @csrf
+  @method('put')
+
+  <!-- timezone -->
+  <div class="grid grid-cols-3 items-center rounded-t-lg p-3 hover:bg-blue-50 last:rounded-b-lg">
+    <x-input-label for="timezone" :value="__('Timezone')" class="col-span-1" />
+    <div class="w-full justify-self-end col-span-2">
+      <select @focus="showActions = true" @blur="showActions = false" id="timezone" name="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 disabled:text-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
+        <optgroup label="UTC">
+            <option value="UTC">Coordinated Universal Time (UTC) - UTC+00:00</option>
+        </optgroup>
+        <optgroup label="Africa">
+            <option value="Africa/Casablanca">Western European Time (WET) - UTC+00:00</option>
+            <option value="Africa/Lagos">West Africa Time (WAT) - UTC+01:00</option>
+            <option value="Africa/Cairo">Eastern European Time (EET) - UTC+02:00</option>
+            <option value="Africa/Johannesburg">South Africa Standard Time (SAST) - UTC+02:00</option>
+        </optgroup>
+        <optgroup label="America">
+            <option value="America/St_Johns">Newfoundland Standard Time (NST) - UTC-03:30</option>
+            <option value="America/Halifax">Atlantic Standard Time (AST) - UTC-04:00</option>
+            <option value="America/New_York">Eastern Standard Time (EST) - UTC-05:00</option>
+            <option value="America/Chicago">Central Standard Time (CST) - UTC-06:00</option>
+            <option value="America/Denver">Mountain Standard Time (MST) - UTC-07:00</option>
+            <option value="America/Los_Angeles">Pacific Standard Time (PST) - UTC-08:00</option>
+            <option value="America/Anchorage">Alaska Standard Time (AKST) - UTC-09:00</option>
+            <option value="Pacific/Honolulu">Hawaii-Aleutian Standard Time (HAST) - UTC-10:00</option>
+        </optgroup>
+        <optgroup label="Antarctica">
+            <option value="Antarctica/Palmer">Chile Summer Time (CLST) - UTC-03:00</option>
+            <option value="Antarctica/McMurdo">New Zealand Standard Time (NZST) - UTC+12:00</option>
+        </optgroup>
+        <optgroup label="Asia">
+            <option value="Asia/Dubai">Gulf Standard Time (GST) - UTC+04:00</option>
+            <option value="Asia/Karachi">Pakistan Standard Time (PKT) - UTC+05:00</option>
+            <option value="Asia/Kolkata">India Standard Time (IST) - UTC+05:30</option>
+            <option value="Asia/Dhaka">Bangladesh Standard Time (BST) - UTC+06:00</option>
+            <option value="Asia/Jakarta">Western Indonesia Time (WIB) - UTC+07:00</option>
+            <option value="Asia/Shanghai">China Standard Time (CST) - UTC+08:00</option>
+            <option value="Asia/Tokyo">Japan Standard Time (JST) - UTC+09:00</option>
+            <option value="Asia/Seoul">Korea Standard Time (KST) - UTC+09:00</option>
+        </optgroup>
+        <optgroup label="Australia">
+            <option value="Australia/Perth">Australian Western Standard Time (AWST) - UTC+08:00</option>
+            <option value="Australia/Adelaide">Australian Central Standard Time (ACST) - UTC+09:30</option>
+            <option value="Australia/Sydney">Australian Eastern Standard Time (AEST) - UTC+10:00</option>
+        </optgroup>
+        <optgroup label="Europe">
+            <option value="Europe/London">Greenwich Mean Time (GMT) - UTC+00:00</option>
+            <option value="Europe/Berlin">Central European Time (CET) - UTC+01:00</option>
+            <option value="Europe/Moscow">Moscow Standard Time (MSK) - UTC+03:00</option>
+        </optgroup>
+        <optgroup label="Pacific">
+            <option value="Pacific/Auckland">New Zealand Standard Time (NZST) - UTC+12:00</option>
+            <option value="Pacific/Fiji">Fiji Time (FJT) - UTC+12:00</option>
+        </optgroup>
+    </select>
path: resources/views/administration/partials/timezone.blade.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add selected attribute to pre-select the user's current timezone**

The timezone dropdown should pre-select the user's current timezone setting. Currently, there's no `selected` attribute on any option.

```diff
<select @focus="showActions = true" @blur="showActions = false" id="timezone" name="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 disabled:text-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
    <optgroup label="UTC">
-        <option value="UTC">Coordinated Universal Time (UTC) - UTC+00:00</option>
+        <option value="UTC" {{ $user->timezone === 'UTC' ? 'selected' : '' }}>Coordinated Universal Time (UTC) - UTC+00:00</option>
    </optgroup>
    <!-- Apply similar changes to other options -->
```

This ensures that when the form loads, it shows the user's current timezone.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      <select @focus="showActions = true" @blur="showActions = false" id="timezone" name="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-xs focus:border-indigo-500 focus:ring-indigo-500 disabled:text-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
        <optgroup label="UTC">
            <option value="UTC" {{ $user->timezone === 'UTC' ? 'selected' : '' }}>Coordinated Universal Time (UTC) - UTC+00:00</option>
        </optgroup>
        <optgroup label="Africa">
            <option value="Africa/Casablanca">Western European Time (WET) - UTC+00:00</option>
            <option value="Africa/Lagos">West Africa Time (WAT) - UTC+01:00</option>
            <option value="Africa/Cairo">Eastern European Time (EET) - UTC+02:00</option>
            <option value="Africa/Johannesburg">South Africa Standard Time (SAST) - UTC+02:00</option>
        </optgroup>
        <optgroup label="America">
            <option value="America/St_Johns">Newfoundland Standard Time (NST) - UTC-03:30</option>
            <option value="America/Halifax">Atlantic Standard Time (AST) - UTC-04:00</option>
            <option value="America/New_York">Eastern Standard Time (EST) - UTC-05:00</option>
            <option value="America/Chicago">Central Standard Time (CST) - UTC-06:00</option>
            <option value="America/Denver">Mountain Standard Time (MST) - UTC-07:00</option>
            <option value="America/Los_Angeles">Pacific Standard Time (PST) - UTC-08:00</option>
            <option value="America/Anchorage">Alaska Standard Time (AKST) - UTC-09:00</option>
            <option value="Pacific/Honolulu">Hawaii-Aleutian Standard Time (HAST) - UTC-10:00</option>
        </optgroup>
        <optgroup label="Antarctica">
            <option value="Antarctica/Palmer">Chile Summer Time (CLST) - UTC-03:00</option>
            <option value="Antarctica/McMurdo">New Zealand Standard Time (NZST) - UTC+12:00</option>
        </optgroup>
        <optgroup label="Asia">
            <option value="Asia/Dubai">Gulf Standard Time (GST) - UTC+04:00</option>
            <option value="Asia/Karachi">Pakistan Standard Time (PKT) - UTC+05:00</option>
            <option value="Asia/Kolkata">India Standard Time (IST) - UTC+05:30</option>
            <option value="Asia/Dhaka">Bangladesh Standard Time (BST) - UTC+06:00</option>
            <option value="Asia/Jakarta">Western Indonesia Time (WIB) - UTC+07:00</option>
            <option value="Asia/Shanghai">China Standard Time (CST) - UTC+08:00</option>
            <option value="Asia/Tokyo">Japan Standard Time (JST) - UTC+09:00</option>
            <option value="Asia/Seoul">Korea Standard Time (KST) - UTC+09:00</option>
        </optgroup>
        <optgroup label="Australia">
            <option value="Australia/Perth">Australian Western Standard Time (AWST) - UTC+08:00</option>
            <option value="Australia/Adelaide">Australian Central Standard Time (ACST) - UTC+09:30</option>
            <option value="Australia/Sydney">Australian Eastern Standard Time (AEST) - UTC+10:00</option>
        </optgroup>
        <optgroup label="Europe">
            <option value="Europe/London">Greenwich Mean Time (GMT) - UTC+00:00</option>
            <option value="Europe/Berlin">Central European Time (CET) - UTC+01:00</option>
            <option value="Europe/Moscow">Moscow Standard Time (MSK) - UTC+03:00</option>
        </optgroup>
        <optgroup label="Pacific">
            <option value="Pacific/Auckland">New Zealand Standard Time (NZST) - UTC+12:00</option>
            <option value="Pacific/Fiji">Fiji Time (FJT) - UTC+12:00</option>
        </optgroup>
      </select>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Fixed in commits e39777f to a966bef
created_at: 2025-03-13T10:43:30Z
author_association: NONE
diff_hunk: @@ -15,49 +15,16 @@
     <div class="relative bg-gray-50 px-6 pt-8 lg:px-12">
       <div class="mx-auto max-w-2xl px-2 py-2 sm:px-0">
         <!-- Profile -->
-        <h1 class="font-semi-bold mb-4 text-2xl">
-          {{ __('Profile') }}
-        </h1>
-
         @include('administration.partials.profile', ['user' => $user])
 
         <!-- Profile photo -->
-        <h2 class="font-semi-bold mb-1 text-lg">{{ __('Profile photo') }}</h2>
-        <p class="mb-4 text-sm text-zinc-500">{{ __('You can upload a profile photo to use as your avatar, or use the default avatar.') }}</p>
-
         @include('administration.partials.avatar', ['user' => $user])
 
-        <!-- Last activity -->
-        <h2 class="font-semi-bold mb-4 text-lg">
-          {{ __('Last activity') }}
-        </h2>
-
-        <div class="mb-8 border border-gray-200 bg-white sm:rounded-lg">
-          <!-- last actions -->
-          @foreach ($logs as $log)
-            <div class="flex items-center justify-between border-b border-gray-200 p-3 text-sm first:rounded-t-lg last:rounded-b-lg last:border-b-0 hover:bg-blue-50">
-              <div class="flex items-center gap-3">
-                <x-lucide-activity class="size-3 min-w-3 text-zinc-600 dark:text-zinc-400" />
-                <div class="">
-                  <p class="flex items-center gap-1">
-                    <span class="">{{ $log['user']['name'] }}</span>
-                    |
-                    <span class="font-mono text-xs">{{ $log['action'] }}</span>
-                  </p>
-                  <p>{{ $log['description'] }}</p>
-                </div>
-              </div>
-
-              <p class="font-mono text-xs">{{ $log['created_at'] }}</p>
-            </div>
-          @endforeach
+        <!-- Timezone -->
+        @include('administration.partials.timezone', ['user' => $user])
path: resources/views/administration/index.blade.php
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Variable inconsistency between parent and partial view**

There's a mismatch between the variable passed to the timezone partial and what the partial expects. This file passes `['user' => $user]`, but the timezone.blade.php file has a docblock expecting a `$person` variable. Either update the docblock in the partial or adjust the variable name passed here for consistency.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T10:43:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Http\Controllers\Administration;
+
+use App\Http\Controllers\Controller;
+use App\Models\Log;
+use App\Models\User;
+use App\Services\UpdateTimezone;
+use App\Services\UpdateUserInformation;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Validation\Rule;
+use Illuminate\View\View;
+
+class AdministrationTimezoneController extends Controller
+{
+    public function update(Request $request): RedirectResponse
+    {
+        $validated = $request->validate([
+            'timezone' => ['required', 'string', 'max:255'],
+        ]);
path: app/Http/Controllers/Administration/AdministrationTimezoneController.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add timezone validation against a list of valid timezones.**

The validation only checks that the timezone is a required string of max 255 characters, but doesn't verify it's a valid timezone identifier.

```diff
$validated = $request->validate([
-    'timezone' => ['required', 'string', 'max:255'],
+    'timezone' => ['required', 'string', 'max:255', 'timezone'],
]);
```

You'll need to create a custom validation rule or use a package that provides timezone validation to ensure only valid PHP timezones are accepted.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        $validated = $request->validate([
            'timezone' => ['required', 'string', 'max:255', 'timezone'],
        ]);
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 22-24: app/Http/Controllers/Administration/AdministrationTimezoneController.php#L22-L24
Added lines #L22 - L24 were not covered by tests

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T23:21:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Services;
+
+use App\Jobs\LogUserAction;
+use App\Jobs\UpdateUserLastActivityDate;
+use App\Models\User;
+use Carbon\Carbon;
+use Carbon\Exceptions\InvalidDateException;
+use Carbon\Exceptions\InvalidFormatException;
+use Illuminate\Auth\Events\Registered;
+use InvalidArgumentException;
+
+class UpdateTimezone
+{
+    public function __construct(
+        public User $user,
+        public string $timezone,
+    ) {}
+
+    public function execute(): User
+    {
+        $this->update();
+        $this->updateUserLastActivityDate();
+        $this->log();
+
+        return $this->user;
+    }
+
+    private function update(): void
+    {
+        $this->user->update([
+            'timezone' => $this->timezone,
+        ]);
+    }
path: app/Services/UpdateTimezone.php
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add timezone validation**

Currently, there's no validation to ensure the provided timezone string is valid. This could lead to issues if an invalid timezone identifier is provided.


```diff
 private function update(): void
 {
+    // Validate the timezone before updating
+    if (!in_array($this->timezone, timezone_identifiers_list())) {
+        throw new InvalidArgumentException('Invalid timezone provided: ' . $this->timezone);
+    }
+
     $this->user->update([
         'timezone' => $this->timezone,
     ]);
 }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    private function update(): void
    {
        // Validate the timezone before updating
        if (!in_array($this->timezone, timezone_identifiers_list())) {
            throw new InvalidArgumentException('Invalid timezone provided: ' . $this->timezone);
        }

        $this->user->update([
            'timezone' => $this->timezone,
        ]);
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T23:21:10Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-13T10:38:18Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Summary by CodeRabbit

- **New Features**
  - Introduced a dedicated timezone settings section in the administration panel for user management.
  - Added a new logs display component to enhance visibility of user activity.
  - Improved profile and avatar screens with clearer upload instructions and reorganized layouts.
  - Enhanced person overview pages by adding a collapsible section for detailed meeting information.
  - Added API endpoints for retrieving and updating user timezone settings.
- **Tests**
  - Strengthened validations for timezone updates to ensure proper functionality and user experience.
## Walkthrough
The changes update several configuration and resource files. In the repository settings, quotation marks in the Dependabot configuration have been switched from double to single quotes, while documentation files (CODE_OF_CONDUCT.md and CONTRIBUTING.md) see bullet formatting adjustments. The TODO list is restructured with new tasks, and the User model now treats the timezone attribute differently by adjusting its mass assignment and enabling encryption via casting. A migration modifies the users table by updating the timezone column. In the CSS and Blade view files, whitespace cleanups and layout restructuring are implemented: header and paragraph elements have been added or removed, Blade components for logs and timezone have been introduced, and new partials for profile and information details in the administration and persons sections have been created. Finally, a unit test now asserts that the user’s timezone is 'UTC'.

## Poem
> I'm a rabbit, hopping through the code,  
> Skipping over quotes and bullet loads.  
> New tasks and views, all lined up neat,  
> Timezones encrypted—oh, what a treat!  
> With every change, I bounce and delight,  
> Celebrating updates from dawn to night!  
> 🐇✨

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=djaiss/peopleOS&utm_content=131):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codecov-commenter
created_at: 2025-03-13T11:18:23Z
body: ## [Codecov](https://app.codecov.io/gh/djaiss/peopleOS/pull/131?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Regis+Freyd) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 69.28%. Comparing base [(`c5a765f`)](https://app.codecov.io/gh/djaiss/peopleOS/commit/c5a765f8af9d6632aa5590513bee229346e59a9b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Regis+Freyd) to head [(`458aeea`)](https://app.codecov.io/gh/djaiss/peopleOS/commit/458aeea72ffcfbbe1ba0063501d7dc7f554a8d8a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Regis+Freyd).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##               main     #131      +/-   ##
============================================
+ Coverage     68.91%   69.28%   +0.36%     
- Complexity      617      625       +8     
============================================
  Files           148      151       +3     
  Lines          3748     3793      +45     
============================================
+ Hits           2583     2628      +45     
  Misses         1165     1165              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/djaiss/peopleOS/pull/131?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Regis+Freyd).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Regis+Freyd).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE