diff --git a/.env.example b/.env.example index f0a2039..34962e9 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,10 @@ APP_KEY= APP_DEBUG=true APP_URL=http://localhost +APP_DEFAULT_USER_TIMEZONE=UTC +# Valid languages: ru | en +APP_DEFAULT_LOCALE=ru + LOG_CHANNEL=daily LOG_DEPRECATIONS_CHANNEL=deprecations LOG_LEVEL=debug diff --git a/app/Dto/Request/Private/Profile/UpdateSettings.php b/app/Dto/Request/Private/Profile/UpdateSettings.php new file mode 100644 index 0000000..464b390 --- /dev/null +++ b/app/Dto/Request/Private/Profile/UpdateSettings.php @@ -0,0 +1,24 @@ +lang; + } + + public function getTimezone(): ?string + { + return $this->timezone; + } +} diff --git a/app/Enums/Lang.php b/app/Enums/Lang.php new file mode 100644 index 0000000..76f3f62 --- /dev/null +++ b/app/Enums/Lang.php @@ -0,0 +1,46 @@ + 'Русский', + self::En => 'English' + }; + } + + public function getLocale(): string + { + return match ($this) { + self::Ru => 'ru', + self::En => 'en' + }; + } + + public static function toArray(): array + { + $choices = []; + foreach (self::cases() as $lang) { + $choices[] = [ + 'name' => $lang->name, + 'value' => $lang->value, + 'title' => $lang->getTitle(), + 'locale' => $lang->getLocale() + ]; + } + return $choices; + } + + public static function toCollection(): Collection + { + return collect(self::toArray()); + } +} diff --git a/app/Helpers/Helpers.php b/app/Helpers/Helpers.php new file mode 100644 index 0000000..fd28ac2 --- /dev/null +++ b/app/Helpers/Helpers.php @@ -0,0 +1,24 @@ + $value) { + $timezone[$value] = $value . ' (UTC ' . now($value)->format('P') . ')'; + } + return collect($timezone)->sortKeys(); + }); + } + + public static function getUserTimeZone() { + return auth()->user()?->timezone ?? config('app.user_timezone'); + } +} diff --git a/app/Http/Controllers/Private/ProfileController.php b/app/Http/Controllers/Private/ProfileController.php index 3f3eea9..50b9785 100644 --- a/app/Http/Controllers/Private/ProfileController.php +++ b/app/Http/Controllers/Private/ProfileController.php @@ -2,8 +2,11 @@ namespace App\Http\Controllers\Private; +use App\Enums\Lang; +use App\Helpers\Helpers; use App\Http\Requests\Private\Profile\UpdatePasswordRequest; use App\Http\Requests\Private\Profile\UpdateRequest; +use App\Http\Requests\Private\Profile\UpdateSettingsRequest; use App\Services\Private\ProfileService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -25,7 +28,9 @@ final class ProfileController extends Controller public function settings(Request $request): View { return view('private/profile/settings', [ - 'user' => $request->user() + 'user' => $request->user(), + 'languages' => Lang::toCollection()->pluck(value: 'title', key: 'value')->toArray(), + 'timezone' => Helpers::getTimeZoneList()->toArray(), ]); } @@ -52,4 +57,16 @@ final class ProfileController extends Controller } return redirect()->route('profile.edit')->withSuccess($result->getMessage()); } + + public function updateSettings(UpdateSettingsRequest $request): RedirectResponse + { + $data = $request->getDto(); + $user = $request->user(); + + $result = $this->profileService->updateSettings($data, $user); + if ($result->isError()) { + return redirect()->back()->withInput()->withErrors($result->getMessage()); + } + return redirect()->route('profile.settings')->withSuccess($result->getMessage()); + } } diff --git a/app/Http/Requests/Private/Profile/UpdateSettingsRequest.php b/app/Http/Requests/Private/Profile/UpdateSettingsRequest.php new file mode 100644 index 0000000..68f7ada --- /dev/null +++ b/app/Http/Requests/Private/Profile/UpdateSettingsRequest.php @@ -0,0 +1,38 @@ + ['nullable', new Enum(Lang::class)], + 'timezone' => ['nullable', Rule::in(Helpers::getTimeZoneList()->keys()->toArray())] + ]; + } + + public function getDto(): UpdateSettings + { + $lang = $this->input('lang', null); + if (!is_null($lang)) { + $lang = Lang::from((int) $lang); + } + + return new UpdateSettings( + lang: $lang, + timezone: $this->input('timezone', null), + ); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 1145d40..973a42a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; +use App\Enums\Lang; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -34,7 +35,7 @@ final class User extends Authenticatable 'password', 'timezone', 'is_active', - 'locale', + 'lang', ]; /** @@ -54,7 +55,8 @@ final class User extends Authenticatable */ protected $casts = [ 'email_verified_at' => 'datetime', - 'is_active' => 'boolean' + 'is_active' => 'boolean', + 'lang' => Lang::class, ]; /** diff --git a/app/Services/Private/ProfileService.php b/app/Services/Private/ProfileService.php index 4a71184..6cae92a 100644 --- a/app/Services/Private/ProfileService.php +++ b/app/Services/Private/ProfileService.php @@ -4,6 +4,7 @@ namespace App\Services\Private; use App\Dto\Request\Private\Profile\Update; use App\Dto\Request\Private\Profile\UpdatePassword; +use App\Dto\Request\Private\Profile\UpdateSettings; use App\Models\User; use App\ServiceResults\ServiceResultError; use App\ServiceResults\ServiceResultSuccess; @@ -40,4 +41,19 @@ final class ProfileService extends Service } return $this->ok(__('The password has been changed')); } + + public function updateSettings(UpdateSettings $update, User $user): ServiceResultError | ServiceResultSuccess + { + try { + $data = [ + 'lang' => $update->getLang(), + 'timezone' => $update->getTimezone(), + ]; + $this->userCommandHandler->handleUpdate($user, $data); + } catch (\Throwable $e) { + report($e->getMessage()); + return $this->errService($e->getMessage()); + } + return $this->ok(__('The settings have been saved')); + } } diff --git a/app/View/Components/Private/Forms/Select.php b/app/View/Components/Private/Forms/Select.php new file mode 100644 index 0000000..7fc96c8 --- /dev/null +++ b/app/View/Components/Private/Forms/Select.php @@ -0,0 +1,54 @@ + value], ... ] + * @param null|string $value + */ + public function __construct( + private readonly string $title, + private readonly string $name, + private readonly array $list, + private readonly ?string $value = '' + ) { } + + protected function getName(): string + { + return $this->name; + } + + private function getTitle(): string + { + return Str::ucfirst($this->title); + } + + private function getValue(): string + { + return (string) old($this->getRequestName(), $this->value); + } + + private function getList(): array + { + return $this->list; + } + + public function render(): View + { + return view('private.components.forms.select', [ + 'title' => $this->getTitle(), + 'name' => $this->getName(), + 'requestName' => $this->getRequestName(), + 'list' => $this->getList(), + 'value' => $this->getValue() + ]); + } + +} diff --git a/config/app.php b/config/app.php index 15f6c33..cf96293 100644 --- a/config/app.php +++ b/config/app.php @@ -70,6 +70,7 @@ return [ */ 'timezone' => 'UTC', + 'user_timezone' => env('APP_DEFAULT_USER_TIMEZONE', 'UTC'), /* |-------------------------------------------------------------------------- @@ -82,7 +83,7 @@ return [ | */ - 'locale' => 'ru', + 'locale' => env('APP_DEFAULT_LOCALE', 'ru'), /* |-------------------------------------------------------------------------- @@ -209,7 +210,7 @@ return [ */ 'aliases' => Facade::defaultAliases()->merge([ - // 'ExampleClass' => App\Example\ExampleClass::class, + 'Helpers' => \App\Helpers\Helpers::class, ])->toArray(), ]; diff --git a/lang/en.json b/lang/en.json index 6431b8a..9aea158 100644 --- a/lang/en.json +++ b/lang/en.json @@ -45,5 +45,7 @@ "Dashboard": "Dashboard", "Save": "Save", "Profile saved successfully": "Profile saved successfully", - "The password has been changed": "The password has been changed" + "The password has been changed": "The password has been changed", + "Default": "default", + "The settings have been saved": "The settings have been saved" } diff --git a/lang/en/validation.php b/lang/en/validation.php index c470d1c..bfda1c8 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -213,5 +213,7 @@ return [ 'updated_at' => 'updated at', 'username' => 'username', 'year' => 'year', + 'lang' => 'language', + 'timezone' => 'timezone', ], ]; diff --git a/lang/ru.json b/lang/ru.json index da44446..0fbc0e1 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -45,5 +45,7 @@ "Dashboard": "Dashboard", "Save": "Сохранить", "Profile saved successfully": "Профиль успешно сохранен", - "The password has been changed": "Пароль был изменен" + "The password has been changed": "Пароль был изменен", + "Default": "По умолчанию", + "The settings have been saved": "Настройки были сохранены" } diff --git a/lang/ru/validation.php b/lang/ru/validation.php index 2af40fd..670d101 100644 --- a/lang/ru/validation.php +++ b/lang/ru/validation.php @@ -213,5 +213,7 @@ return [ 'updated_at' => 'обновлено в', 'username' => 'никнейм', 'year' => 'год', + 'lang' => 'язык', + 'timezone' => 'часовой пояс', ], ]; diff --git a/resources/views/private/components/forms/select.blade.php b/resources/views/private/components/forms/select.blade.php new file mode 100644 index 0000000..341fde5 --- /dev/null +++ b/resources/views/private/components/forms/select.blade.php @@ -0,0 +1,12 @@ +
+ + + @error($name) + {{ $message }} + @enderror +
diff --git a/resources/views/private/profile/settings.blade.php b/resources/views/private/profile/settings.blade.php new file mode 100644 index 0000000..18b55ea --- /dev/null +++ b/resources/views/private/profile/settings.blade.php @@ -0,0 +1,23 @@ +@section('meta_title', __('Settings')) +@section('h1', __('Settings')) + +
+
+
+
+
+ @csrf + @method('PUT') + + + + + + + +
+
+
+
+
+
diff --git a/routes/web.php b/routes/web.php index 48ce5e0..5077d13 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,7 +17,7 @@ Route::middleware('guest')->group(function () { Route::get('login', [\App\Http\Controllers\AuthController::class, 'login'])->name('login'); Route::middleware(['throttle:login'])->post('login', [\App\Http\Controllers\AuthController::class, 'authorization'])->name('authorization'); }); -Route::middleware(['auth', 'verified'])->group(function () { +Route::middleware(['auth', 'verified', 'user.locale'])->group(function () { Route::post('logout', [\App\Http\Controllers\AuthController::class, 'logout'])->name('logout'); Route::get('/', [\App\Http\Controllers\Private\DashboardController::class, 'index'])->name('home'); Route::prefix('profile')->as('profile.') @@ -26,5 +26,6 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::put('/', [\App\Http\Controllers\Private\ProfileController::class, 'update'])->name('update'); Route::put('password', [\App\Http\Controllers\Private\ProfileController::class, 'updatePassword'])->name('update-password'); Route::get('settings', [\App\Http\Controllers\Private\ProfileController::class, 'settings'])->name('settings'); + Route::put('settings', [\App\Http\Controllers\Private\ProfileController::class, 'updateSettings'])->name('update-settings'); }); });