feat(setting): membuat pengaturan untuk mengatur akun, profil dan mengubah kata sandi
This commit is contained in:
parent
92a54dbdbf
commit
739f7e761e
58
app/Livewire/Forms/AccountForm.php
Normal file
58
app/Livewire/Forms/AccountForm.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Traits\WithMediaHandler;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Livewire\Form;
|
||||
|
||||
class AccountForm extends Form
|
||||
{
|
||||
use WithMediaHandler;
|
||||
|
||||
public ?User $user = null;
|
||||
|
||||
public string $username = '';
|
||||
|
||||
public string $email = '';
|
||||
|
||||
public string $status = '';
|
||||
|
||||
public ?string $email_verified_at = null;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'username' => ['required', 'alpha_num', 'min:5', 'max:20', Rule::unique('users', 'username')->ignore($this->user)],
|
||||
'email' => ['required', 'email', 'max:100', Rule::unique('users', 'email')->ignore($this->user)],
|
||||
];
|
||||
}
|
||||
|
||||
public function validationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'username' => 'nama pengguna',
|
||||
];
|
||||
}
|
||||
|
||||
public function setAccount(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
$this->username = $user->username;
|
||||
$this->email = $user->email;
|
||||
$this->status = $user->status->label();
|
||||
$this->email_verified_at = $user->email_verified_at ? formatDateTime($user->email_verified_at) : '-';
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->user->update([
|
||||
'username' => $this->username,
|
||||
'email' => $this->email,
|
||||
]);
|
||||
}
|
||||
}
|
||||
54
app/Livewire/Forms/PasswordForm.php
Normal file
54
app/Livewire/Forms/PasswordForm.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Traits\WithMediaHandler;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Livewire\Form;
|
||||
|
||||
class PasswordForm extends Form
|
||||
{
|
||||
use WithMediaHandler;
|
||||
|
||||
public ?User $user = null;
|
||||
|
||||
public string $current_password = '';
|
||||
|
||||
public string $new_password = '';
|
||||
|
||||
public string $new_confirm_password = '';
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'current_password' => ['required', 'string'],
|
||||
'new_password' => ['required', 'string', Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised()],
|
||||
'new_confirm_password' => ['required', 'string', 'same:new_password'],
|
||||
];
|
||||
}
|
||||
|
||||
public function validationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'current_password' => 'kata sandi saat ini',
|
||||
'new_password' => 'kata sandi baru',
|
||||
'new_confirm_password' => 'konfirmasi kata sandi baru',
|
||||
];
|
||||
}
|
||||
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->user->update([
|
||||
'password' => Hash::make($this->new_password),
|
||||
]);
|
||||
}
|
||||
}
|
||||
81
app/Livewire/Forms/ProfileForm.php
Normal file
81
app/Livewire/Forms/ProfileForm.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Enums\Gender;
|
||||
use App\Models\Employee;
|
||||
use App\Rules\PhoneNumber;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Livewire\Form;
|
||||
|
||||
class ProfileForm extends Form
|
||||
{
|
||||
public ?Employee $employee = null;
|
||||
|
||||
public string $full_name = '';
|
||||
|
||||
public string $phone_number = '';
|
||||
|
||||
public string $base_salary = '';
|
||||
|
||||
public string $birthdate = '';
|
||||
|
||||
public string $hire_date = '';
|
||||
|
||||
public string $address = '';
|
||||
|
||||
public string $gender = '';
|
||||
|
||||
public string $status = '';
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'full_name' => ['required', 'string', 'max:100'],
|
||||
'phone_number' => ['required', 'string', new PhoneNumber],
|
||||
'birthdate' => ['required', 'date', 'before:'.Carbon::now()->subYears(18)->format('Y-m-d')],
|
||||
'address' => ['required', 'string'],
|
||||
'gender' => ['required', Rule::in(Gender::cases())],
|
||||
];
|
||||
}
|
||||
|
||||
public function validationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'full_name' => 'nama lengkap',
|
||||
'phone_number' => 'nomor telepon',
|
||||
'base_salary' => 'gaji pokok',
|
||||
'birthdate' => 'tanggal lahir',
|
||||
'address' => 'alamat',
|
||||
'gender' => 'jenis kelamin',
|
||||
];
|
||||
}
|
||||
|
||||
public function setProfile(Employee $employee)
|
||||
{
|
||||
$this->employee = $employee;
|
||||
|
||||
$this->full_name = $employee->full_name;
|
||||
$this->phone_number = $employee->phone_number;
|
||||
$this->base_salary = $employee->base_salary;
|
||||
$this->birthdate = $employee->birthdate;
|
||||
$this->hire_date = formatDate($employee->hire_date);
|
||||
$this->address = $employee->address;
|
||||
$this->gender = $employee->gender->value;
|
||||
$this->status = $employee->status->label();
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->employee->update([
|
||||
'full_name' => $this->full_name,
|
||||
'phone_number' => $this->phone_number,
|
||||
'birthdate' => $this->birthdate,
|
||||
'address' => $this->address,
|
||||
'gender' => $this->gender,
|
||||
]);
|
||||
}
|
||||
}
|
||||
42
app/Livewire/Studio/Setting/Account.php
Normal file
42
app/Livewire/Studio/Setting/Account.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Setting;
|
||||
|
||||
use App\Livewire\Forms\AccountForm;
|
||||
use App\Models\User;
|
||||
use App\Traits\WithUpdatedData;
|
||||
use Flux\Flux;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Akun')]
|
||||
class Account extends Component
|
||||
{
|
||||
use WithUpdatedData;
|
||||
|
||||
public AccountForm $form;
|
||||
|
||||
public function mount(User $user)
|
||||
{
|
||||
$this->form->setAccount($user);
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->form->update();
|
||||
|
||||
Flux::toast(
|
||||
heading: 'Berhasil',
|
||||
text: 'Akun berhasil diperbarui.',
|
||||
variant: 'success',
|
||||
duration: 3000
|
||||
);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.studio.setting.account', [
|
||||
'pageTitle' => 'Akun',
|
||||
]);
|
||||
}
|
||||
}
|
||||
62
app/Livewire/Studio/Setting/Password.php
Normal file
62
app/Livewire/Studio/Setting/Password.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Setting;
|
||||
|
||||
use App\Livewire\Forms\PasswordForm;
|
||||
use App\Models\User;
|
||||
use App\Traits\WithUpdatedData;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Kata Sandi')]
|
||||
class Password extends Component
|
||||
{
|
||||
use WithUpdatedData;
|
||||
|
||||
public PasswordForm $form;
|
||||
|
||||
public function mount(User $user)
|
||||
{
|
||||
$this->form->setUser($user);
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
if (! Hash::check($this->form->current_password, $this->form->user->password)) {
|
||||
Flux::toast(
|
||||
heading: 'Gagal',
|
||||
text: 'Kata sandi yang Anda masukkan tidak sesuai dengan kata sandi saat ini.',
|
||||
variant: 'danger',
|
||||
duration: 3000
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auth()->logoutOtherDevices($this->form->current_password);
|
||||
|
||||
$this->form->update();
|
||||
|
||||
auth()->logout();
|
||||
session()->invalidate();
|
||||
session()->regenerateToken();
|
||||
|
||||
Flux::toast(
|
||||
heading: 'Berhasil',
|
||||
text: 'Kata sandi berhasil diperbarui.',
|
||||
variant: 'success',
|
||||
duration: 3000
|
||||
);
|
||||
|
||||
$this->redirectRoute('login', navigate: true);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.studio.setting.password', [
|
||||
'pageTitle' => 'Kata Sandi',
|
||||
]);
|
||||
}
|
||||
}
|
||||
42
app/Livewire/Studio/Setting/Profile.php
Normal file
42
app/Livewire/Studio/Setting/Profile.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Setting;
|
||||
|
||||
use App\Livewire\Forms\ProfileForm;
|
||||
use App\Models\Employee;
|
||||
use App\Traits\WithUpdatedData;
|
||||
use Flux\Flux;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Profil')]
|
||||
class Profile extends Component
|
||||
{
|
||||
use WithUpdatedData;
|
||||
|
||||
public ProfileForm $form;
|
||||
|
||||
public function mount(Employee $employee)
|
||||
{
|
||||
$this->form->setProfile($employee);
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->form->update();
|
||||
|
||||
Flux::toast(
|
||||
heading: 'Berhasil',
|
||||
text: 'Profil berhasil diperbarui.',
|
||||
variant: 'success',
|
||||
duration: 3000
|
||||
);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.studio.setting.profile', [
|
||||
'pageTitle' => 'Profil',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -19,9 +19,6 @@ protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'gender' => Gender::class,
|
||||
'birthdate' => 'date',
|
||||
'hire_date' => 'date',
|
||||
'resign_date' => 'date',
|
||||
'status' => EmploymentStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -21,10 +21,13 @@ class="max-lg:hidden! hidden dark:flex" />
|
||||
</flux:navbar.item>
|
||||
</flux:navbar>
|
||||
<flux:dropdown position="top" align="end">
|
||||
<flux:avatar circle size="sm" src="https://unavatar.io/x/calebporzio" as="button" />
|
||||
<flux:profile circle name="{{ auth()->user()?->employee?->full_name }}"
|
||||
avatar="https://unavatar.io/x/calebporzio" />
|
||||
<flux:navmenu>
|
||||
<flux:navmenu.item href="#" icon="cog">Pengaturan</flux:navmenu.item>
|
||||
<flux:navmenu.item href="#" icon="paint-brush">Tema</flux:navmenu.item>
|
||||
<flux:navmenu.item href="{{ route('studio.setting.account', auth()->id()) }}" icon="cog"
|
||||
wire:navigate.hover>
|
||||
Pengaturan
|
||||
</flux:navmenu.item>
|
||||
<flux:navmenu.item href="#" icon="chart-bar">Statistik</flux:navmenu.item>
|
||||
<flux:menu.separator />
|
||||
@livewire('auth.logout')
|
||||
|
||||
24
resources/views/components/setting-tabs.blade.php
Normal file
24
resources/views/components/setting-tabs.blade.php
Normal file
@ -0,0 +1,24 @@
|
||||
<div class="overflow-x-auto pb-2">
|
||||
<flux:navbar>
|
||||
<flux:navbar.item href="{{ route('studio.setting.account', auth()->id()) }}" icon="key"
|
||||
:current="request()->routeIs('studio.setting.account', auth()->id())" wire:navigate.hover wire:ignore>
|
||||
Akun
|
||||
</flux:navbar.item>
|
||||
|
||||
<flux:navbar.item href="{{ route('studio.setting.profile', auth()->user()?->employee?->id) }}" icon="user"
|
||||
:current="request()->routeIs('studio.setting.profile', auth()->user()?->employee?->id)" wire:navigate.hover
|
||||
wire:ignore>
|
||||
Profil
|
||||
</flux:navbar.item>
|
||||
|
||||
<flux:navbar.item href="{{ route('studio.setting.password', auth()->id()) }}" icon="lock-closed"
|
||||
:current="request()->routeIs('studio.setting.password', auth()->id())" wire:navigate.hover wire:ignore>
|
||||
Kata
|
||||
Sandi
|
||||
</flux:navbar.item>
|
||||
</flux:navbar>
|
||||
</div>
|
||||
|
||||
<flux:text class="text-xs italic block md:hidden" color="yellow">
|
||||
* Geser ke kanan untuk melihat selengkapnya.
|
||||
</flux:text>
|
||||
38
resources/views/livewire/studio/setting/account.blade.php
Normal file
38
resources/views/livewire/studio/setting/account.blade.php
Normal file
@ -0,0 +1,38 @@
|
||||
<flux:main>
|
||||
@include('components.setting-tabs')
|
||||
|
||||
<div class="flex flex-col gap-4 mt-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 items-start">
|
||||
<div class="col-span-1 lg:col-span-2">
|
||||
<flux:card class="space-y-6 p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<flux:field>
|
||||
<flux:label>Nama Pengguna <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan nama pengguna"
|
||||
wire:model.live.debounce.500ms="form.username" autocomplete="off" />
|
||||
<flux:error name="form.username" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Email <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan email" wire:model.live.debounce.500ms="form.email"
|
||||
autocomplete="off" />
|
||||
<flux:error name="form.email" />
|
||||
</flux:field>
|
||||
|
||||
<flux:input label="Status Akun" wire:model="form.status" readonly />
|
||||
|
||||
<flux:input label="Email Terverifikasi Pada" wire:model="form.email_verified_at" readonly />
|
||||
|
||||
</div>
|
||||
</flux:card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-start mt-4">
|
||||
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
|
||||
Simpan
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:main>
|
||||
40
resources/views/livewire/studio/setting/password.blade.php
Normal file
40
resources/views/livewire/studio/setting/password.blade.php
Normal file
@ -0,0 +1,40 @@
|
||||
<flux:main>
|
||||
@include('components.setting-tabs')
|
||||
|
||||
<div class="flex flex-col gap-4 mt-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 items-start">
|
||||
<div class="col-span-1 lg:col-span-2">
|
||||
<flux:card class="space-y-6 p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<flux:field>
|
||||
<flux:label>Kata Sandi Saat Ini <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan kata sandi saat ini" type="password"
|
||||
wire:model.live.debounce.500ms="form.current_password" viewable />
|
||||
<flux:error name="form.current_password" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Kata Sandi Baru <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan kata sandi baru" type="password"
|
||||
wire:model.live.debounce.500ms="form.new_password" viewable />
|
||||
<flux:error name="form.new_password" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Konfirmasi Kata Sandi <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan konfirmasi kata sandi" type="password"
|
||||
wire:model.live.debounce.500ms="form.new_confirm_password" viewable />
|
||||
<flux:error name="form.new_confirm_password" />
|
||||
</flux:field>
|
||||
</div>
|
||||
</flux:card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-start mt-4">
|
||||
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
|
||||
Simpan
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:main>
|
||||
89
resources/views/livewire/studio/setting/profile.blade.php
Normal file
89
resources/views/livewire/studio/setting/profile.blade.php
Normal file
@ -0,0 +1,89 @@
|
||||
<flux:main>
|
||||
@include('components.setting-tabs')
|
||||
|
||||
<div class="flex flex-col gap-4 mt-4">
|
||||
<div class="flex flex-col lg:flex-row gap-4">
|
||||
<div class="w-full lg:w-3/4 space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 items-start">
|
||||
<div class="col-span-1 lg:col-span-2">
|
||||
<flux:card class="space-y-6 p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="md:col-span-2">
|
||||
<flux:field>
|
||||
<flux:label>Nama Lengkap <span class="text-red-500 ms-1">*</span>
|
||||
</flux:label>
|
||||
<flux:input placeholder="Masukkan nama lengkap"
|
||||
wire:model.live.debounce.500ms="form.full_name" autofocus
|
||||
autocomplete="off" />
|
||||
<flux:error name="form.full_name" />
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Nomor Telepon <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Masukkan nomor telepon" mask="9999 9999 99999"
|
||||
wire:model.live.debounce.500ms="form.phone_number" autocomplete="off" />
|
||||
<flux:error name="form.phone_number" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Gaji Pokok</flux:label>
|
||||
<flux:input.group>
|
||||
<flux:input.group.prefix>Rp</flux:input.group.prefix>
|
||||
<flux:input wire:model="form.base_salary" readonly />
|
||||
</flux:input.group>
|
||||
<flux:error name="form.base_salary" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Tanggal Lahir <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:date-picker with-today wire:model.live.debounce.500ms="form.birthdate"
|
||||
autocomplete="off" locale="id-ID" selectable-header />
|
||||
<flux:error name="form.birthdate" />
|
||||
</flux:field>
|
||||
|
||||
<flux:input label="Tanggal Bergabung" wire:model="form.hire_date" readonly />
|
||||
|
||||
<div class="md:col-span-2">
|
||||
<flux:field>
|
||||
<flux:label>Alamat <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:textarea placeholder="Masukkan alamat lengkap"
|
||||
wire:model.live.debounce.500ms="form.address" autocomplete="off"
|
||||
rows="2" />
|
||||
<flux:error name="form.address" />
|
||||
</flux:field>
|
||||
</div>
|
||||
</div>
|
||||
</flux:card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full lg:w-1/3 space-y-4">
|
||||
<flux:card class="space-y-6 p-6">
|
||||
<flux:field>
|
||||
<flux:label>Jenis Kelamin <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:radio.group wire:model.live="form.gender" variant="buttons" class="w-full *:flex-1">
|
||||
@foreach (\App\Enums\Gender::cases() as $gender)
|
||||
<flux:radio value="{{ $gender->value }}">
|
||||
{{ $gender->label() }}
|
||||
</flux:radio>
|
||||
@endforeach
|
||||
</flux:radio.group>
|
||||
<flux:error name="form.gender" />
|
||||
</flux:field>
|
||||
</flux:card>
|
||||
|
||||
<flux:card class="space-y-6 p-6">
|
||||
<flux:input label="Status Kerja" wire:model="form.status" readonly />
|
||||
</flux:card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-start mt-4">
|
||||
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
|
||||
Simpan
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:main>
|
||||
@ -23,15 +23,26 @@
|
||||
use App\Livewire\Studio\Master\User\Create as UserCreate;
|
||||
use App\Livewire\Studio\Master\User\Edit as UserEdit;
|
||||
use App\Livewire\Studio\Master\User\Index as UserIndex;
|
||||
use App\Livewire\Studio\Setting\Account as AccountComponent;
|
||||
use App\Livewire\Studio\Setting\Password as PasswordComponent;
|
||||
use App\Livewire\Studio\Setting\Profile as ProfileComponent;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::middleware('auth')
|
||||
->prefix('studio')
|
||||
->group(function () {
|
||||
Route::prefix('setting')
|
||||
->name('studio.setting.')
|
||||
->group(function () {
|
||||
Route::get('/account/{user}', AccountComponent::class)->name('account');
|
||||
Route::get('/profile/{employee}', ProfileComponent::class)->name('profile');
|
||||
Route::get('/password/{user}', PasswordComponent::class)->name('password');
|
||||
});
|
||||
|
||||
Route::prefix('dashboard')
|
||||
->name('studio.dashboard.')
|
||||
->group(function () {
|
||||
Route::get('overview', OverviewComponent::class)->name('overview');
|
||||
Route::get('/overview', OverviewComponent::class)->name('overview');
|
||||
});
|
||||
|
||||
Route::prefix('master')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user