From 700f9325a31de8df77d52dad09abc75be4a35d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Mon, 29 Dec 2025 07:47:45 +0100 Subject: [PATCH] Add currency selection to user profile settings (#41) --- .../Settings/ProfileUpdateRequest.php | 1 + app/Models/User.php | 1 + database/factories/UserFactory.php | 1 + ...63338_add_currency_code_to_users_table.php | 28 +++++++++++++ resources/js/pages/settings/account.tsx | 41 +++++++++++++++++++ resources/js/types/index.d.ts | 2 + tests/Feature/Settings/ProfileUpdateTest.php | 3 ++ 7 files changed, 77 insertions(+) create mode 100644 database/migrations/2025_12_29_063338_add_currency_code_to_users_table.php diff --git a/app/Http/Requests/Settings/ProfileUpdateRequest.php b/app/Http/Requests/Settings/ProfileUpdateRequest.php index 64cf26b0..880e0aa7 100644 --- a/app/Http/Requests/Settings/ProfileUpdateRequest.php +++ b/app/Http/Requests/Settings/ProfileUpdateRequest.php @@ -27,6 +27,7 @@ class ProfileUpdateRequest extends FormRequest 'max:255', Rule::unique(User::class)->ignore($this->user()->id), ], + 'currency_code' => ['required', 'string', 'max:3', Rule::in(['USD', 'EUR', 'GBP', 'JPY', 'CHF', 'CAD', 'AUD', 'CNY', 'INR', 'MXN'])], ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index f58441a5..cb745c35 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -29,6 +29,7 @@ class User extends Authenticatable 'password', 'encryption_salt', 'onboarded_at', + 'currency_code', ]; /** diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 87ce11e2..b48fe78d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -25,6 +25,7 @@ class UserFactory extends Factory return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), + 'currency_code' => 'USD', 'email_verified_at' => now(), 'password' => static::$password ??= 'password', 'remember_token' => Str::random(10), diff --git a/database/migrations/2025_12_29_063338_add_currency_code_to_users_table.php b/database/migrations/2025_12_29_063338_add_currency_code_to_users_table.php new file mode 100644 index 00000000..df3c1a14 --- /dev/null +++ b/database/migrations/2025_12_29_063338_add_currency_code_to_users_table.php @@ -0,0 +1,28 @@ +string('currency_code', 3)->default('USD')->after('email'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('currency_code'); + }); + } +}; diff --git a/resources/js/pages/settings/account.tsx b/resources/js/pages/settings/account.tsx index f1ec1b41..843a39f5 100644 --- a/resources/js/pages/settings/account.tsx +++ b/resources/js/pages/settings/account.tsx @@ -8,6 +8,13 @@ import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { useTwoFactorAuth } from '@/hooks/use-two-factor-auth'; import AppLayout from '@/layouts/app-layout'; @@ -16,6 +23,7 @@ import { edit as accountEdit } from '@/routes/account'; import { disable, enable } from '@/routes/two-factor'; import { send } from '@/routes/verification'; import { type BreadcrumbItem, type SharedData } from '@/types'; +import { CURRENCY_OPTIONS } from '@/types/account'; import { Transition } from '@headlessui/react'; import { Form, Head, Link, usePage } from '@inertiajs/react'; import { ShieldBan, ShieldCheck } from 'lucide-react'; @@ -114,6 +122,39 @@ export default function Account({ /> +
+ + + + + +
+ {mustVerifyEmail && auth.user.email_verified_at === null && (
diff --git a/resources/js/types/index.d.ts b/resources/js/types/index.d.ts index 55f18de3..2c1e51d6 100644 --- a/resources/js/types/index.d.ts +++ b/resources/js/types/index.d.ts @@ -1,6 +1,7 @@ import { InertiaLinkProps } from '@inertiajs/react'; import { LucideIcon } from 'lucide-react'; import { ReactNode } from 'react'; +import { CurrencyCode } from './account'; import { PricingConfig } from './pricing'; import { UUID } from './uuid'; @@ -52,6 +53,7 @@ export interface User { id: UUID; name: string; email: string; + currency_code: CurrencyCode; avatar?: string; email_verified_at: string | null; two_factor_enabled?: boolean; diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php index 91745103..23257d01 100644 --- a/tests/Feature/Settings/ProfileUpdateTest.php +++ b/tests/Feature/Settings/ProfileUpdateTest.php @@ -20,6 +20,7 @@ test('profile information can be updated', function () { ->patch(route('profile.update'), [ 'name' => 'Test User', 'email' => 'test@example.com', + 'currency_code' => 'EUR', ]); $response @@ -31,6 +32,7 @@ test('profile information can be updated', function () { expect($user->name)->toBe('Test User'); expect($user->email)->toBe('test@example.com'); expect($user->email_verified_at)->toBeNull(); + expect($user->currency_code)->toBe('EUR'); }); test('email verification status is unchanged when the email address is unchanged', function () { @@ -41,6 +43,7 @@ test('email verification status is unchanged when the email address is unchanged ->patch(route('profile.update'), [ 'name' => 'Test User', 'email' => $user->email, + 'currency_code' => $user->currency_code, ]); $response