From f3b2c3e01840e1cc2e92a7cbb24777654a7441b1 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Mon, 30 Mar 2026 15:30:46 +0800 Subject: [PATCH] =?UTF-8?q?[FIX]=20=E9=81=B7=E7=A7=BB=E6=A9=9F=E5=8F=B0?= =?UTF-8?q?=E6=8E=88=E6=AC=8A=E7=82=BA=E7=8D=A8=E7=AB=8B=E6=A8=A1=E7=B5=84?= =?UTF-8?q?=EF=BC=9A=E4=BF=AE=E5=BE=A9=E8=AE=8A=E6=95=B8=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E3=80=81=E8=A3=9C=E9=BD=8A=E5=A4=9A=E8=AA=9E=E7=B3=BB=E4=B8=A6?= =?UTF-8?q?=E5=BC=B7=E5=8C=96=E5=A4=9A=E7=A7=9F=E6=88=B6=E6=95=B8=E6=93=9A?= =?UTF-8?q?=E9=9A=94=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MachineSettingController.php | 75 +---- .../Machine/MachinePermissionController.php | 115 +++++++ .../Admin/PermissionController.php | 20 +- app/Models/System/User.php | 2 + ..._30_144031_add_is_admin_to_roles_table.php | 36 +++ ...5939_move_is_admin_from_roles_to_users.php | 57 ++++ ...x_user_is_admin_flag_for_deleted_users.php | 48 +++ database/seeders/RoleSeeder.php | 2 + lang/en.json | 5 +- lang/ja.json | 5 +- lang/zh_TW.json | 7 +- .../basic-settings/machines/index.blade.php | 255 +--------------- .../admin/machines/permissions.blade.php | 285 ++++++++++++++++++ .../admin/permission/roles-edit.blade.php | 2 +- .../layouts/partials/sidebar-menu.blade.php | 7 + routes/web.php | 9 +- 16 files changed, 592 insertions(+), 338 deletions(-) create mode 100644 app/Http/Controllers/Admin/Machine/MachinePermissionController.php create mode 100644 database/migrations/2026_03_30_144031_add_is_admin_to_roles_table.php create mode 100644 database/migrations/2026_03_30_145939_move_is_admin_from_roles_to_users.php create mode 100644 database/migrations/2026_03_30_150252_fix_user_is_admin_flag_for_deleted_users.php create mode 100644 resources/views/admin/machines/permissions.blade.php diff --git a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php index 7275938..36b2403 100644 --- a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php +++ b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php @@ -14,7 +14,6 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Illuminate\Support\Facades\Log; -use App\Models\System\User; class MachineSettingController extends AdminController { @@ -46,18 +45,8 @@ class MachineSettingController extends AdminController } $models_list = $modelQuery->latest()->paginate($per_page)->withQueryString(); - // 3. 處理使用者清單 (Accounts Tab - 授權帳號) - $userQuery = User::query()->with('machines')->whereNotNull('company_id'); // 僅列出租戶帳號以供分配 - if ($tab === 'accounts' && $search) { - $userQuery->where(function ($q) use ($search) { - $q->where('name', 'like', "%{$search}%") - ->orWhere('username', 'like', "%{$search}%") - ->orWhere('email', 'like', "%{$search}%"); - }); - } - $users_list = $userQuery->latest()->paginate($per_page)->withQueryString(); - // 4. 基礎下拉資料 (用於新增/編輯機台的彈窗) + // 3. 基礎下拉資料 (用於新增/編輯機台的彈窗) $models = MachineModel::select('id', 'name')->get(); $paymentConfigs = PaymentConfig::select('id', 'name')->get(); $companies = \App\Models\System\Company::select('id', 'name', 'code')->get(); @@ -65,7 +54,6 @@ class MachineSettingController extends AdminController return view('admin.basic-settings.machines.index', compact( 'machines', 'models_list', - 'users_list', 'models', 'paymentConfigs', 'companies', @@ -222,66 +210,5 @@ class MachineSettingController extends AdminController ]); } - /** - * AJAX: 取得特定帳號的機台分配狀態 (從 MachineController 遷移) - */ - public function getAccountMachines(User $user): \Illuminate\Http\JsonResponse - { - $currentUser = auth()->user(); - // 安全檢查:只能操作自己公司的帳號(除非是系統管理員) - if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) { - return response()->json(['error' => 'Unauthorized'], 403); - } - - // 取得該使用者所屬公司之所有機台 - $machines = Machine::where('company_id', $user->company_id) - ->get(['id', 'name', 'serial_no']); - - $assignedIds = $user->machines()->pluck('machines.id')->toArray(); - - return response()->json([ - 'user' => $user, - 'machines' => $machines, - 'assigned_ids' => $assignedIds - ]); - } - - /** - * AJAX: 儲存特定帳號的機台分配 (從 MachineController 遷移) - */ - public function syncAccountMachines(Request $request, User $user): \Illuminate\Http\JsonResponse - { - $currentUser = auth()->user(); - - // 安全檢查 - if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) { - return response()->json(['error' => 'Unauthorized'], 403); - } - - $request->validate([ - 'machine_ids' => 'nullable|array', - 'machine_ids.*' => 'exists:machines,id' - ]); - - // 加固驗證:確保所有機台 ID 都屬於該使用者的公司 - if ($request->has('machine_ids')) { - $machineIds = array_unique($request->machine_ids); - $validCount = Machine::where('company_id', $user->company_id) - ->whereIn('id', $machineIds) - ->count(); - - if ($validCount !== count($machineIds)) { - return response()->json(['error' => 'Invalid machine IDs provided.'], 422); - } - } - - $user->machines()->sync($request->machine_ids ?? []); - - return response()->json([ - 'success' => true, - 'message' => __('Permissions updated successfully'), - 'assigned_machines' => $user->machines()->select('machines.id', 'machines.name', 'machines.serial_no')->get() - ]); - } } diff --git a/app/Http/Controllers/Admin/Machine/MachinePermissionController.php b/app/Http/Controllers/Admin/Machine/MachinePermissionController.php new file mode 100644 index 0000000..7a75699 --- /dev/null +++ b/app/Http/Controllers/Admin/Machine/MachinePermissionController.php @@ -0,0 +1,115 @@ +input('per_page', 10); + $search = $request->input('search'); + + $currentUser = auth()->user(); + + // 僅列出租戶中具有「is_admin」標記的角色帳號以供分配 + $userQuery = User::query() + ->with(['machines' => function($query) { + $query->withoutGlobalScope('machine_access') + ->select('machines.id', 'machines.name', 'machines.serial_no'); + }]) + ->whereNotNull('company_id'); + + // 非系統管理員僅能看到同公司的帳號 (因 User Model 排除 TenantScoped 全域過濾,需手動注入) + if (!$currentUser->isSystemAdmin()) { + $userQuery->where('company_id', $currentUser->company_id); + } + + if ($search) { + $userQuery->where(function ($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('username', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%"); + }); + } + + $users_list = $userQuery->latest()->paginate($per_page)->withQueryString(); + + return view('admin.machines.permissions', compact('users_list')); + } + + /** + * AJAX: 取得特定帳號的機台分配狀態 + */ + public function getAccountMachines(User $user): JsonResponse + { + $currentUser = auth()->user(); + + // 安全檢查:只能操作自己公司的帳號(除非是系統管理員) + if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) { + return response()->json(['error' => 'Unauthorized'], 403); + } + + // 取得該使用者所屬公司之所有機台 (忽略個別帳號的 machine_access 限制,以公司為單位顯示) + $machines = Machine::withoutGlobalScope('machine_access') + ->where('company_id', $user->company_id) + ->get(['id', 'name', 'serial_no']); + + $assignedIds = $user->machines()->pluck('machines.id')->toArray(); + + return response()->json([ + 'user' => $user, + 'machines' => $machines, + 'assigned_ids' => $assignedIds + ]); + } + + /** + * AJAX: 儲存特定帳號的機台分配 + */ + public function syncAccountMachines(Request $request, User $user): JsonResponse + { + $currentUser = auth()->user(); + + // 安全檢查 + if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) { + return response()->json(['error' => 'Unauthorized'], 403); + } + + $request->validate([ + 'machine_ids' => 'nullable|array', + 'machine_ids.*' => 'exists:machines,id' + ]); + + // 加固驗證:確保所有機台 ID 都屬於該使用者的公司 (使用 withoutGlobalScope 避免管理員自身權限影響驗證邏輯) + if ($request->has('machine_ids')) { + $machineIds = array_unique($request->machine_ids); + $validCount = Machine::withoutGlobalScope('machine_access') + ->where('company_id', $user->company_id) + ->whereIn('id', $machineIds) + ->count(); + + if ($validCount !== count($machineIds)) { + return response()->json(['error' => 'Invalid machine IDs provided.'], 422); + } + } + + $user->machines()->sync($request->machine_ids ?? []); + + return response()->json([ + 'success' => true, + 'message' => __('Permissions updated successfully'), + 'assigned_machines' => $user->machines()->select('machines.id', 'machines.name', 'machines.serial_no')->get() + ]); + } +} diff --git a/app/Http/Controllers/Admin/PermissionController.php b/app/Http/Controllers/Admin/PermissionController.php index ce5e0f6..7463914 100644 --- a/app/Http/Controllers/Admin/PermissionController.php +++ b/app/Http/Controllers/Admin/PermissionController.php @@ -195,11 +195,13 @@ class PermissionController extends Controller $is_system = auth()->user()->isSystemAdmin() ? $request->boolean('is_system') : $role->is_system; - $role->update([ + $updateData = [ 'name' => $validated['name'], 'is_system' => $is_system, 'company_id' => $is_system ? null : $role->company_id, - ]); + ]; + + $role->update($updateData); $perms = $validated['permissions'] ?? []; @@ -363,6 +365,7 @@ class PermissionController extends Controller 'status' => $validated['status'], 'company_id' => $company_id, 'phone' => $validated['phone'] ?? null, + 'is_admin' => (auth()->user()->isSystemAdmin() && !empty($validated['company_id'])), ]); $user->assignRole($role); @@ -430,6 +433,18 @@ class PermissionController extends Controller 'phone' => $validated['phone'] ?? null, ]; + // 只有系統管理員在編輯租戶帳號時,且該帳號原本不是管理員,才可能觸發標記(視需求而定) + // 這裡我們維持 storeAccount 的邏輯:如果是系統管理員幫公司「開站」或「首配」,才自動標記 + // 為求嚴謹,我們檢查該公司是否已經有 is_admin,如果沒有,當前這個人可以是第一個 + if (auth()->user()->isSystemAdmin() && !empty($validated['company_id']) && !$user->is_admin) { + $hasAdmin = \App\Models\System\User::where('company_id', $validated['company_id']) + ->where('is_admin', true) + ->exists(); + if (!$hasAdmin) { + $updateData['is_admin'] = true; + } + } + if (auth()->user()->isSystemAdmin()) { // 防止超級管理員不小心把自己綁定到租客公司或降級 if ($user->id === auth()->id()) { @@ -459,6 +474,7 @@ class PermissionController extends Controller 'guard_name' => 'web', 'company_id' => $target_company_id, 'is_system' => false, + 'is_admin' => true, ]); $newRole->syncPermissions($roleObj->getPermissionNames()); $roleObj = $newRole; diff --git a/app/Models/System/User.php b/app/Models/System/User.php index 26ac998..7c0fac3 100644 --- a/app/Models/System/User.php +++ b/app/Models/System/User.php @@ -31,6 +31,7 @@ class User extends Authenticatable 'avatar', 'role', 'status', + 'is_admin', ]; /** @@ -51,6 +52,7 @@ class User extends Authenticatable protected $casts = [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'is_admin' => 'boolean', ]; /** diff --git a/database/migrations/2026_03_30_144031_add_is_admin_to_roles_table.php b/database/migrations/2026_03_30_144031_add_is_admin_to_roles_table.php new file mode 100644 index 0000000..0d7213e --- /dev/null +++ b/database/migrations/2026_03_30_144031_add_is_admin_to_roles_table.php @@ -0,0 +1,36 @@ +boolean('is_admin')->default(false)->after('is_system'); + }); + + // 資料遷移:將所有租戶中名稱為「管理員」的角色標示為 is_admin = true + // 這樣既有的授權篩選才不會斷掉 + DB::table('roles') + ->whereNotNull('company_id') + ->where('name', '管理員') + ->update(['is_admin' => true]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('roles', function (Blueprint $table) { + $table->dropColumn('is_admin'); + }); + } +}; diff --git a/database/migrations/2026_03_30_145939_move_is_admin_from_roles_to_users.php b/database/migrations/2026_03_30_145939_move_is_admin_from_roles_to_users.php new file mode 100644 index 0000000..01ff242 --- /dev/null +++ b/database/migrations/2026_03_30_145939_move_is_admin_from_roles_to_users.php @@ -0,0 +1,57 @@ +dropColumn('is_admin'); + }); + } + + // 2. 在 users 新增 is_admin + Schema::table('users', function (Blueprint $table) { + $table->boolean('is_admin')->default(false)->after('status'); + }); + + // 3. 資料遷移:針對現有租戶,將每一家公司最先建立的帳號(或是目前名稱為管理員角色的人)標記為 is_admin = true + // 取得所有租戶公司 ID + $companyIds = DB::table('companies')->pluck('id'); + + foreach ($companyIds as $companyId) { + // 優先找該公司 ID 最小的 user (通常是第一個建立的) + $userId = DB::table('users') + ->where('company_id', $companyId) + ->orderBy('id', 'asc') + ->value('id'); + + if ($userId) { + DB::table('users')->where('id', $userId)->update(['is_admin' => true]); + } + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_admin'); + }); + + Schema::table('roles', function (Blueprint $table) { + $table->boolean('is_admin')->default(false)->after('is_system'); + }); + } +}; diff --git a/database/migrations/2026_03_30_150252_fix_user_is_admin_flag_for_deleted_users.php b/database/migrations/2026_03_30_150252_fix_user_is_admin_flag_for_deleted_users.php new file mode 100644 index 0000000..f24d4e3 --- /dev/null +++ b/database/migrations/2026_03_30_150252_fix_user_is_admin_flag_for_deleted_users.php @@ -0,0 +1,48 @@ +whereNotNull('deleted_at')->update(['is_admin' => false]); + + // 2. 針對每一家公司,重新撈取「目前還存活 (deleted_at is null)」的最早建立帳號 + $companyIds = DB::table('companies')->pluck('id'); + + foreach ($companyIds as $companyId) { + // 找該公司中,目前 ID 最小且「尚未被刪除」的 User + $userId = DB::table('users') + ->where('company_id', $companyId) + ->whereNull('deleted_at') + ->orderBy('id', 'asc') + ->value('id'); + + if ($userId) { + // 將該帳號設為管理員,並確保該公司其它生存帳號如果是 true 的先清掉 (一對一標記) + DB::table('users') + ->where('company_id', $companyId) + ->where('id', '!=', $userId) + ->update(['is_admin' => false]); + + DB::table('users')->where('id', $userId)->update(['is_admin' => true]); + } + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // 基本上這是資料修正,回復也不太有意義 + } +}; diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 89d7239..363d985 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -22,6 +22,7 @@ class RoleSeeder extends Seeder 'menu.members', 'menu.machines', 'menu.machines.list', + 'menu.machines.permissions', 'menu.machines.utilization', 'menu.machines.maintenance', 'menu.app', @@ -68,6 +69,7 @@ class RoleSeeder extends Seeder 'menu.members', 'menu.machines', 'menu.machines.list', + 'menu.machines.permissions', 'menu.machines.utilization', 'menu.machines.maintenance', 'menu.app', diff --git a/lang/en.json b/lang/en.json index 4fec5b7..775ed05 100644 --- a/lang/en.json +++ b/lang/en.json @@ -324,6 +324,7 @@ "Machine Model Settings": "Machine Model Settings", "Machine Name": "Machine Name", "Machine Permissions": "Machine Permissions", + "Manage machine access permissions": "Manage machine access permissions", "Machine Registry": "Machine Registry", "Machine Reports": "Machine Reports", "Machine Restart": "Machine Restart", @@ -762,6 +763,7 @@ "menu.machines": "Machine Management", "menu.machines.list": "Machine List", "menu.machines.maintenance": "Maintenance Records", + "menu.machines.permissions": "Machine Permissions", "menu.machines.utilization": "Utilization Rate", "menu.members": "Member Management", "menu.permission": "Permission Settings", @@ -796,5 +798,6 @@ "Authorize Btn": "Authorize", "Authorization updated successfully": "Authorization updated successfully", "Authorized Status": "Authorized", - "Unauthorized Status": "Unauthorized" + "Unauthorized Status": "Unauthorized", + "This is a system administrator role. Its name is locked to ensure system stability.": "This is a system administrator role. Its name is locked to ensure system stability." } \ No newline at end of file diff --git a/lang/ja.json b/lang/ja.json index ca2e0c0..b1d6a61 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -320,6 +320,7 @@ "Machine Model Settings": "機台型號設定", "Machine Name": "機台名", "Machine Permissions": "機台権限", + "Manage machine access permissions": "機台アクセス權限の管理", "Machine Registry": "機台登録", "Machine Reports": "機台レポート", "Machine Restart": "機台再起動", @@ -764,6 +765,7 @@ "menu.machines": "機台管理", "menu.machines.list": "機台リスト", "menu.machines.maintenance": "メンテナンス記録", + "menu.machines.permissions": "機台権限", "menu.machines.utilization": "稼働率", "menu.members": "会員管理", "menu.permission": "權限設定", @@ -799,5 +801,6 @@ "Authorized Machines Management": "認定機台管理", "Authorization updated successfully": "認証が更新されました", "Authorized Status": "認可済み", - "Unauthorized Status": "未認可" + "Unauthorized Status": "未認可", + "This is a system administrator role. Its name is locked to ensure system stability.": "これはシステム管理者ロールです。システムの安定性を確保するため、名称は固定されています。" } \ No newline at end of file diff --git a/lang/zh_TW.json b/lang/zh_TW.json index 7041710..8f3aa72 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -331,7 +331,8 @@ "Machine Model": "機台型號", "Machine Model Settings": "機台型號設定", "Machine Name": "機台名稱", - "Machine Permissions": "授權機台", + "Machine Permissions": "機台權限", + "Manage machine access permissions": "管理機台存取權限", "Machine Registry": "機台清冊", "Machine Reports": "機台報表", "Machine Restart": "機台重啟", @@ -787,6 +788,7 @@ "menu.machines": "機台管理", "menu.machines.list": "機台列表", "menu.machines.maintenance": "維修管理單", + "menu.machines.permissions": "機台權限", "menu.machines.utilization": "機台嫁動率", "menu.members": "會員管理", "menu.permission": "權限設定", @@ -821,5 +823,6 @@ "Authorize Btn": "授權", "Authorization updated successfully": "授權更新成功", "Authorized Status": "已授權", - "Unauthorized Status": "未授權" + "Unauthorized Status": "未授權", + "This is a system administrator role. Its name is locked to ensure system stability.": "這是系統管理員角色,名稱已鎖定以確保系統穩定性。" } \ No newline at end of file diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php index 1cd30e1..eed8012 100644 --- a/resources/views/admin/basic-settings/machines/index.blade.php +++ b/resources/views/admin/basic-settings/machines/index.blade.php @@ -114,70 +114,6 @@ window.dispatchEvent(new CustomEvent('toast', { detail: { message: '{{ __('Error processing request') }}', type: 'error' } })); }); }, - // Machine Permissions (Migrated from Account Management) - showPermissionModal: false, - isPermissionsLoading: false, - targetUserId: null, - targetUserName: '', - allMachines: [], - allMachinesCount: 0, - permissions: {}, - openPermissionModal(user) { - this.targetUserId = user.id; - this.targetUserName = user.name; - this.showPermissionModal = true; - this.isPermissionsLoading = true; - this.permissions = {}; - this.allMachines = []; - this.permissionSearchQuery = ''; - - fetch(`/admin/basic-settings/machines/permissions/accounts/${user.id}`) - .then(res => res.json()) - .then(data => { - if (data.machines) { - this.allMachines = data.machines; - this.allMachinesCount = data.machines.length; - const tempPermissions = {}; - data.machines.forEach(m => { - tempPermissions[m.id] = (data.assigned_ids || []).includes(m.id); - }); - this.permissions = tempPermissions; - } - }) - .catch(e => { - window.dispatchEvent(new CustomEvent('toast', { detail: { message: '{{ __('Failed to load permissions') }}', type: 'error' } })); - }) - .finally(() => { - this.isPermissionsLoading = false; - }); - }, - togglePermission(machineId) { - this.permissions = { ...this.permissions, [machineId]: !this.permissions[machineId] }; - }, - savePermissions() { - const machineIds = Object.keys(this.permissions).filter(id => this.permissions[id]); - - fetch(`/admin/basic-settings/machines/permissions/accounts/${this.targetUserId}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, - 'Accept': 'application/json' - }, - body: JSON.stringify({ machine_ids: machineIds }) - }) - .then(res => res.json()) - .then(data => { - if (data.success) { - window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } })); - setTimeout(() => window.location.reload(), 500); - } else { - throw new Error(data.error || 'Update failed'); - } - }) - .catch(e => { - window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } })); - }); } }" @execute-regenerate.window="executeRegeneration($event.detail)"> @@ -216,10 +152,6 @@ class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all {{ $tab === 'models' ? 'bg-white dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 shadow-sm shadow-cyan-500/10' : 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-200' }}"> {{ __('Models') }} - - {{ __('Authorized Accounts Tab') }} - @@ -237,7 +169,7 @@ @@ -388,81 +320,7 @@ {{ $machines->appends(['tab' => 'machines'])->links('vendor.pagination.luxury') }} - @elseif($tab === 'accounts') - -
- - - - - - - - - - - @forelse($users_list as $user) - - - - - - - @empty - - - - @endforelse - -
- {{ __('Account Info') }} - {{ __('Affiliation') }} - {{ __('Authorized Machines') }} - {{ __('Action') }}
-
-
- - - -
-
- {{ $user->name }} - {{ $user->username }} -
-
-
- - {{ $user->company->name ?? __('System') }} - - -
- @forelse($user->machines as $m) -
- {{ $m->name }} - {{ $m->serial_no }} -
- @empty -
- -- {{ __('None') }} -- -
- @endforelse -
-
- -
-
- -

{{ __('No accounts found') }}

-
-
-
-
- {{ $users_list->appends(['tab' => 'accounts'])->links('vendor.pagination.luxury') }} -
+ @else @@ -1167,116 +1025,7 @@ /> - - @endsection \ No newline at end of file diff --git a/resources/views/admin/machines/permissions.blade.php b/resources/views/admin/machines/permissions.blade.php new file mode 100644 index 0000000..785205e --- /dev/null +++ b/resources/views/admin/machines/permissions.blade.php @@ -0,0 +1,285 @@ +@extends('layouts.admin') + +@section('content') +
+ +
+
+

{{ __('Machine Permissions') }}

+

{{ + __('Manage machine access permissions') }}

+
+
+ + +
+ +
+
+ + + + + + + +
+
+ +
+ + + + + + + + + + + @forelse($users_list as $user) + + + + + + + @empty + + + + @endforelse + +
+ {{ __('Account Info') }} + {{ __('Company Name') }} + {{ __('Authorized Machines') }} + {{ __('Action') }}
+
+
+ + + +
+
+ {{ $user->name }} + {{ $user->username }} +
+
+
+ + {{ $user->company->name ?? __('System') }} + + +
+ @forelse($user->machines as $m) +
+ {{ $m->name }} + {{ $m->serial_no }} +
+ @empty +
+ -- {{ __('None') }} -- +
+ @endforelse +
+
+ +
+
+ +

{{ __('No accounts found') }}

+
+
+
+
+ {{ $users_list->links('vendor.pagination.luxury') }} +
+
+ + + +
+@endsection diff --git a/resources/views/admin/permission/roles-edit.blade.php b/resources/views/admin/permission/roles-edit.blade.php index 422ca84..3c3272c 100644 --- a/resources/views/admin/permission/roles-edit.blade.php +++ b/resources/views/admin/permission/roles-edit.blade.php @@ -140,7 +140,7 @@
name === 'super-admin' ? 'readonly' : '' }}> @error('name') diff --git a/resources/views/layouts/partials/sidebar-menu.blade.php b/resources/views/layouts/partials/sidebar-menu.blade.php index a6393c8..0c2efe8 100644 --- a/resources/views/layouts/partials/sidebar-menu.blade.php +++ b/resources/views/layouts/partials/sidebar-menu.blade.php @@ -64,6 +64,13 @@ @endcan + @can('menu.machines.permissions') +
  • + + {{ __('Machine Permissions') }} +
  • + @endcan + @can('menu.machines.utilization')
  • diff --git a/routes/web.php b/routes/web.php index b99f630..128cf31 100644 --- a/routes/web.php +++ b/routes/web.php @@ -37,7 +37,10 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( Route::resource('gift-definitions', App\Http\Controllers\Admin\GiftDefinitionController::class)->except(['show', 'create', 'edit']); Route::prefix('machines')->name('machines.')->group(function () { - // Route::get('/permissions', [App\Http\Controllers\Admin\MachineController::class , 'permissions'])->name('permissions'); // Merged into Sub-account Management + Route::get('/permissions', [App\Http\Controllers\Admin\Machine\MachinePermissionController::class, 'index'])->name('permissions')->middleware('can:menu.machines.permissions'); + Route::get('/permissions/accounts/{user}', [App\Http\Controllers\Admin\Machine\MachinePermissionController::class, 'getAccountMachines'])->name('permissions.accounts.get'); + Route::post('/permissions/accounts/{user}', [App\Http\Controllers\Admin\Machine\MachinePermissionController::class, 'syncAccountMachines'])->name('permissions.accounts.sync'); + Route::get('/utilization', [App\Http\Controllers\Admin\MachineController::class , 'utilization'])->name('utilization'); Route::get('/utilization-ajax/{id?}', [App\Http\Controllers\Admin\MachineController::class, 'utilizationData'])->name('utilization-ajax'); Route::get('/{machine}/slots-ajax', [App\Http\Controllers\Admin\MachineController::class, 'slotsAjax'])->name('slots-ajax'); @@ -186,9 +189,7 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name( Route::post('/', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'store'])->name('store'); Route::post('/{machine}/regenerate-token', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'regenerateToken'])->name('regenerate-token'); - // 權限管理 (從 MachineController 遷移) - Route::get('/permissions/accounts/{user}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'getAccountMachines'])->name('permissions.accounts.get'); - Route::post('/permissions/accounts/{user}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'syncAccountMachines'])->name('permissions.accounts.sync'); + Route::post('/{machine}/regenerate-token', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'regenerateToken'])->name('regenerate-token'); }); // 客戶金流設定