diff --git a/.DS_Store b/.DS_Store index 6549c6a..1996be4 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/app/Enums/UserGender.php b/app/Enums/UserGender.php new file mode 100644 index 0000000..1f3e12f --- /dev/null +++ b/app/Enums/UserGender.php @@ -0,0 +1,27 @@ + __('enums.user.gender.Male'), + self::Female => __('enums.user.gender.Female'), + self::Other => __('enums.user.gender.Other'), + self::Unset => __('enums.user.gender.Unset'), + }; + } + + public function labelPowergridFilter(): String + { + return $this -> labels(); + } +} \ No newline at end of file diff --git a/app/Enums/UserStatus.php b/app/Enums/UserStatus.php new file mode 100644 index 0000000..6f147cc --- /dev/null +++ b/app/Enums/UserStatus.php @@ -0,0 +1,25 @@ + __('enums.user.status.Active'), + self::Suspended => __('enums.user.status.Suspended'), + self::Deleting => __('enums.user.status.Deleting'), + }; + } + public function labelPowergridFilter(): String + { + return $this -> labels(); + } + +} \ No newline at end of file diff --git a/app/Livewire/Admin/RoleTable.php b/app/Livewire/Admin/RoleTable.php index ad4223c..35aab28 100644 --- a/app/Livewire/Admin/RoleTable.php +++ b/app/Livewire/Admin/RoleTable.php @@ -50,17 +50,16 @@ final class RoleTable extends PowerGridComponent return PowerGrid::fields() ->add('id') ->add('name') + ->add('permissions_list' ,fn(Role $model)=> $model->permissions->pluck('name')->implode(', ')) ->add('created_at_formatted', fn (Role $model) => Carbon::parse($model->created_at)->format('d/m/Y H:i:s')); } public function columns(): array { return [ - Column::make('ID', 'id')->sortable()->searchable(), - Column::make('名稱', 'name')->sortable()->searchable(), - //Column::make('權限', 'permissions_list', function ($role) { - // return $role->permissions->pluck('name')->implode(', '); - //}), + Column::make(__('roles.no'), 'id')->sortable()->searchable(), + Column::make(__('roles.name'), 'name')->sortable()->searchable(), + Column::make(__('roles.permissions'), 'permissions_list'), Column::make('Created at', 'created_at_formatted', 'created_at')->sortable(), Column::action('Action') ]; @@ -78,12 +77,12 @@ final class RoleTable extends PowerGridComponent { return [ Button::add('edit') - ->slot('編輯') + ->slot(__('roles.edit')) ->icon('solid-pencil-square') ->class('inline-flex items-center gap-1 px-3 py-1 rounded ') ->dispatchTo('admin.role-form', 'openEditRoleModal', ['id' => $row->id]), Button::add('delete') - ->slot('刪除') + ->slot(__('delete')) ->icon('solid-trash') ->class('inline-flex items-center gap-1 px-3 py-1 rounded ') ->dispatchTo('admin.role-form', 'deleteRole', ['id' => $row->id]), diff --git a/app/Livewire/Admin/Roles.php b/app/Livewire/Admin/Roles.php deleted file mode 100644 index 47e67f7..0000000 --- a/app/Livewire/Admin/Roles.php +++ /dev/null @@ -1,104 +0,0 @@ - 'required|string|max:255', - ]; - - protected $paginationTheme = 'tailwind'; - - public function getRolesProperty() - { - return Role::where('name', 'like', "%{$this->search}%") - ->orderBy($this->sortField, $this->sortDirection) - ->paginate(10); - } - - public function sortBy($field) - { - if ($this->sortField === $field) { - $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; - } else { - $this->sortField = $field; - $this->sortDirection = 'asc'; - } - } - public function mount() - { - $this->permissions = Permission::all(); - } - - public function openCreateModal() - { - $this->resetFields(); - $this->showCreateModal = true; - } - - public function openEditModal($id) - { - $role = Role::findOrFail($id); - $this->editingRoleId = $role->id; - $this->name = $role->name; - $this->selectedPermissions = $role->permissions()->pluck('id')->toArray(); - $this->showCreateModal = true; - } - - public function save() - { - $this->validate(); - - if ($this->editingRoleId) { - $role = Role::findOrFail($this->editingRoleId); - $role->update(['name' => $this->name]); - $role->syncPermissions($this->selectedPermissions); - session()->flash('message', '角色已更新'); - } else { - $role = Role::create(['name' => $this->name]); - $role->syncPermissions($this->selectedPermissions); - session()->flash('message', '角色已新增'); - } - - $this->resetFields(); - $this->showCreateModal = false; - } - - public function delete($id) - { - Role::findOrFail($id)->delete(); - session()->flash('message', '角色已刪除'); - } - - public function resetFields() - { - $this->name = ''; - $this->selectedPermissions = []; - $this->editingRoleId = null; - } - - public function render() - { - return view('livewire.admin.roles', [ - 'roles' => $this->roles, - ]); - } -} diff --git a/app/Livewire/Admin/UserForm.php b/app/Livewire/Admin/UserForm.php index bd78453..71037c1 100644 --- a/app/Livewire/Admin/UserForm.php +++ b/app/Livewire/Admin/UserForm.php @@ -5,6 +5,8 @@ namespace App\Livewire\Admin; use Livewire\Component; use App\Models\User; +use App\Enums\UserGender; +use App\Enums\UserStatus; use Spatie\Permission\Models\Role; class UserForm extends Component @@ -12,20 +14,44 @@ class UserForm extends Component protected $listeners = ['openCreateUserModal','openEditUserModal', 'deleteUser','bulkDeleteUser']; public bool $showCreateModal = false; - public ?int $userId = null; - public $name; - public $email; - public $roles = []; // 所有角色清單 + + public array $genderOptions =[]; + public array $statusOptions =[]; + public $rolesOptions = []; // 所有角色清單 public $selectedRoles = []; // 表單中選到的權限 + public ?int $userId = null; + + public array $fields = [ + 'name' =>'', + 'email' => '', + 'phone' => '', + 'birthday' => '', + 'gender' => 'unset', + 'status' => 0, + ]; + protected $rules = [ - 'name' => 'required|string|max:255', - 'email' => 'required|string|max:255', + 'fields.name' => 'required|string|max:255', + 'fields.email' => 'required|string|email|max:255', + 'fields.phone' => 'nullable|regex:/^09\d{8}$/', + 'fields.birthday' =>'nullable|date', + 'fields.gender' => 'required|in:male,female,other,unset', + 'fields.status' => 'required|integer|in:0,1,2', ]; public function mount() { - $this->roles = Role::all(); + $this->fields['birthday'] = now()->toDateString(); + $this->genderOptions = collect(UserGender::cases())->map(fn ($gender) => [ + 'name' => $gender->labels(), + 'value' => $gender->value, + ])->toArray(); + $this->statusOptions = collect(UserStatus::cases())->map(fn ($status) => [ + 'name' => $status->labels(), + 'value' => $status->value, + ])->toArray(); + $this->rolesOptions = Role::all(); } public function openCreateUserModal() @@ -38,8 +64,8 @@ class UserForm extends Component { $user = User::findOrFail($id); $this->userId = $user->id; - $this->name = $user->name; - $this->email =$user->email; + $this->fields = $user->only(array_keys($this->fields)); + $this->selectedRoles = $user->roles()->pluck('id')->toArray(); $this->showCreateModal = true; } @@ -49,24 +75,19 @@ class UserForm extends Component $this->validate(); if ($this->userId) { - $role = User::findOrFail($this->userId); - $role->update([ - 'name' => $this->name, - 'email' => $this->email, - ]); - $role->syncRolses($this->selectedRoles); + $user = User::findOrFail($this->userId); + $user->update($this->fields); + $user->syncRoles($this->selectedRoles); session()->flash('message', '使用者已更新'); } else { - $role = User::create([ - 'name' => $this->name, - 'email' => $this->email, - ]); - $role->syncRolses($this->selectedRoles); + $user = User::create($this->fields); + $user->syncRoles($this->selectedRoles); session()->flash('message', '使用者已新增'); } $this->resetFields(); $this->showCreateModal = false; + $this->dispatch('pg:eventRefresh-user-table'); } public function deleteUser($id) @@ -77,10 +98,19 @@ class UserForm extends Component public function resetFields() { - $this->name = ''; - $this->email = ''; - $this->selectedRoles = []; + foreach ($this->fields as $key => $value) { + if ($key == 'gender') { + $this->fields[$key] = 'unset'; + } elseif ($key == 'status') { + $this->fields[$key] = 0; + } elseif ($key == 'birthday') { + $this->fields[$key] = now()->toDateString(); + } else { + $this->fields[$key] = ''; + } + } $this->userId = null; + $this->selectedRoles = []; } public function render() diff --git a/app/Livewire/Admin/UserTable.php b/app/Livewire/Admin/UserTable.php index ef565be..cc65c2e 100644 --- a/app/Livewire/Admin/UserTable.php +++ b/app/Livewire/Admin/UserTable.php @@ -3,6 +3,8 @@ namespace App\Livewire\Admin; use App\Models\User; +use App\Enums\UserGender; +use App\Enums\UserStatus; use Illuminate\Support\Carbon; use Illuminate\Database\Eloquent\Builder; use PowerComponents\LivewirePowerGrid\Button; @@ -11,14 +13,14 @@ use PowerComponents\LivewirePowerGrid\Facades\Filter; use PowerComponents\LivewirePowerGrid\Facades\PowerGrid; use PowerComponents\LivewirePowerGrid\PowerGridFields; use PowerComponents\LivewirePowerGrid\PowerGridComponent; -use PowerComponents\LivewirePowerGrid\Traits\WithExport; +//use PowerComponents\LivewirePowerGrid\Traits\WithExport; use PowerComponents\LivewirePowerGrid\Components\SetUp\Exportable; use PowerComponents\LivewirePowerGrid\Facades\Rule; use Livewire\Attributes\On; final class UserTable extends PowerGridComponent { - use WithExport; + //use WithExport ; public string $tableName = 'user-table'; @@ -71,25 +73,68 @@ final class UserTable extends PowerGridComponent ->add('id') ->add('name') ->add('email') - ->add('created_at_formatted', fn (User $model) => Carbon::parse($model->created_at)->format('d/m/Y H:i:s')); + ->add('phone') + ->add('birthday_formatted',fn (User $model) => Carbon::parse($model->birthday)->format('Y-m-d')) + ->add('gender', fn (User $model) => UserGender::from($model->gender)->labels()) + ->add('status', fn (User $model) => UserStatus::from($model->status)->labels()) + ->add('roles' ,fn(User $model)=> $model->roles->pluck('name')->implode(', ')) + ->add('created_at_formatted', fn (User $model) => Carbon::parse($model->created_at)->format('Y-m-d H:i:s')); } public function columns(): array { return [ Column::make('ID', 'id'), - Column::make('名稱', 'name')->sortable()->searchable(), - Column::make('Email', 'email')->sortable()->searchable(), + Column::make(__('users.name'), 'name') + ->sortable() + ->searchable() + ->editOnClick( + hasPermission: true, + dataField: 'name', + fallback: 'N/A', + saveOnMouseOut: true + ), + Column::make('Email', 'email') + ->sortable() + ->searchable() + ->editOnClick( + hasPermission: true, + dataField: 'email', + fallback: 'N/A', + saveOnMouseOut: true + ), + Column::make(__('users.phone'), 'phone') + ->sortable() + ->searchable() + ->editOnClick( + hasPermission: true, + dataField: 'phone', + fallback: 'N/A', + saveOnMouseOut: true + ), + + Column::make(__('users.gender'), 'gender','users.gender'), + Column::make(__('users.birthday'), 'birthday_formatted')->sortable()->searchable(), + Column::make(__('users.status'), 'status','users.status'), + Column::make(__('users.role'), 'roles'), Column::make('建立時間', 'created_at_formatted', 'created_at')->sortable(), Column::action('操作') ]; } - + public function filters(): array { return [ - Filter::inputText('name')->placeholder('Dish Name'), - Filter::inputText('email')->placeholder('Dish Email'), + Filter::inputText('name')->placeholder(__('users.name')), + Filter::inputText('email')->placeholder('Email'), + Filter::inputText('phone')->placeholder(__('users.phone')), + Filter::enumSelect('gender','users.gender') + ->datasource(UserGender::cases()) + ->optionLabel('users.gender'), + Filter::datepicker('birthday'), + Filter::enumSelect('status', 'users.status') + ->datasource(UserStatus::cases()) + ->optionLabel('users.status'), Filter::datetimepicker('created_at'), ]; } @@ -99,17 +144,36 @@ final class UserTable extends PowerGridComponent return [ Button::add('edit') - ->slot('編輯') + ->slot(__('users.edit')) ->icon('solid-pencil-square') ->class('inline-flex items-center gap-1 px-3 py-1 rounded ') ->dispatchTo('admin.user-form', 'openEditUserModal', ['id' => $row->id]), Button::add('delete') - ->slot('刪除') + ->slot(__('users.delete')) ->icon('solid-trash') ->class('inline-flex items-center gap-1 px-3 py-1 rounded ') ->dispatchTo('admin.user-form', 'deleteUser', ['id' => $row->id]), ]; } + + public function onUpdatedEditable($id, $field, $value): void + { + $updated = User::query()->where('id', $id)->update([ + $field => $value, + ]); + if ($updated) { + $this->fillData(); + } + } + public function onUpdatedToggleable($id, $field, $value): void + { + $updated = User::query()->where('id', $id)->update([ + $field => $value, + ]); + if ($updated) { + $this->fillData(); + } + } #[On('bulkDelete.{tableName}')] public function bulkDelete(): void { diff --git a/app/Models/User.php b/app/Models/User.php index ac2ca92..b799f3a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -21,6 +21,10 @@ class User extends Authenticatable protected $fillable = [ 'name', 'email', + 'phone', + 'birthday', + 'gender', + 'status', 'password', ]; @@ -44,6 +48,7 @@ class User extends Authenticatable return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'birthday' => 'date' ]; } } diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 05fb5d9..a3937df 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -15,6 +15,10 @@ return new class extends Migration $table->id(); $table->string('name'); $table->string('email')->unique(); + $table->string('phone', 10)->unique(); + $table->date('birthday')->nullable(); // 生日 + $table->enum('gender', ['unset','male', 'female', 'other'])->default('unset'); // 性別 + $table->tinyInteger('status')->default(0); // 啟動 $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); diff --git a/database/seeders/CreateAdminUserSeeder.php b/database/seeders/CreateAdminUserSeeder.php index 11b4bfd..97d063b 100644 --- a/database/seeders/CreateAdminUserSeeder.php +++ b/database/seeders/CreateAdminUserSeeder.php @@ -16,12 +16,16 @@ class CreateAdminUserSeeder extends Seeder $user = User::create([ 'name' => 'Allen Yan(admin)', 'email' => 'admin@gmail.com', + 'phone' => '0900000000', + 'birthday' => now()->toDateString(), 'password' => bcrypt('aa1234') ]); $user->assignRole('Admin'); $user = User::create([ 'name' => 'Allen Yan(User)', 'email' => 'allen.yan@gmail.com', + 'phone' => '0900000001', + 'birthday' => now()->toDateString(), 'password' => bcrypt('aa1234') ]); $user->assignRole('User'); diff --git a/resources/lang/zh-TW/enums.php b/resources/lang/zh-TW/enums.php new file mode 100644 index 0000000..348fee1 --- /dev/null +++ b/resources/lang/zh-TW/enums.php @@ -0,0 +1,11 @@ +'男', + 'user.gender.Female' =>'女', + 'user.gender.Other' =>'其他', + 'user.gender.Unset' =>'未定義', + 'user.status.Active' => '正常', + 'user.status.Suspended' => '停權', + 'user.status.Deleting' => '刪除中', +]; \ No newline at end of file diff --git a/resources/lang/zh-TW/roles.php b/resources/lang/zh-TW/roles.php index 8bf2a9b..6bf372b 100644 --- a/resources/lang/zh-TW/roles.php +++ b/resources/lang/zh-TW/roles.php @@ -8,6 +8,7 @@ return [ 'no' => '編號', 'name' => '名稱', + 'permissions' => '權限', 'create' => '新增', 'action' => '操作', diff --git a/resources/lang/zh-TW/users.php b/resources/lang/zh-TW/users.php index 29a442f..9750b20 100644 --- a/resources/lang/zh-TW/users.php +++ b/resources/lang/zh-TW/users.php @@ -8,6 +8,11 @@ return [ 'no' => '編號', 'name' => '名稱', + 'phone' => '手機門號', + 'gender' => '性別', + 'birthday' => '生日', + 'status' => '狀態', + 'role' =>'角色', 'create' => '新增', 'action' => '操作', diff --git a/resources/views/livewire/admin/user-form.blade.php b/resources/views/livewire/admin/user-form.blade.php index 77cbae8..d44fed3 100644 --- a/resources/views/livewire/admin/user-form.blade.php +++ b/resources/views/livewire/admin/user-form.blade.php @@ -1,28 +1,40 @@ -
-@if ($showCreateModal) - -
- + +
+ + + + + - + +
- -
- - -
- - -
-
-
- @endif -
\ No newline at end of file + +
+ + +
+
+ \ No newline at end of file diff --git a/storage/.DS_Store b/storage/.DS_Store new file mode 100644 index 0000000..f74fb04 Binary files /dev/null and b/storage/.DS_Store differ