Add currency selection to user profile settings (#41)

This commit is contained in:
Víctor Falcón 2025-12-29 07:47:45 +01:00 committed by Víctor Falcón
parent 0f66ab7478
commit 700f9325a3
7 changed files with 77 additions and 0 deletions

View File

@ -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'])],
];
}
}

View File

@ -29,6 +29,7 @@ class User extends Authenticatable
'password',
'encryption_salt',
'onboarded_at',
'currency_code',
];
/**

View File

@ -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),

View File

@ -0,0 +1,28 @@
<?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->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');
});
}
};

View File

@ -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({
/>
</div>
<div className="grid gap-2">
<Label htmlFor="currency_code">
Currency
</Label>
<Select
name="currency_code"
defaultValue={auth.user.currency_code}
required
>
<SelectTrigger className="mt-1 w-full">
<SelectValue placeholder="Select currency" />
</SelectTrigger>
<SelectContent>
{CURRENCY_OPTIONS.map(
(currency) => (
<SelectItem
key={currency}
value={currency}
>
{currency}
</SelectItem>
),
)}
</SelectContent>
</Select>
<InputError
className="mt-2"
message={errors.currency_code}
/>
</div>
{mustVerifyEmail &&
auth.user.email_verified_at === null && (
<div>

View File

@ -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;

View File

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