diff --git a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php index 36b2403..d131a3d 100644 --- a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php +++ b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php @@ -45,8 +45,28 @@ class MachineSettingController extends AdminController } $models_list = $modelQuery->latest()->paginate($per_page)->withQueryString(); + // 3. 處理機台權限 (Permissions Tab) - 僅顯示 is_admin 帳號 + $users_list = null; + if ($tab === 'permissions') { + $userQuery = \App\Models\System\User::query() + ->where('is_admin', true) + ->with(['company', 'machines']); + + if ($search) { + $userQuery->where(function($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('username', 'like', "%{$search}%"); + }); + } - // 3. 基礎下拉資料 (用於新增/編輯機台的彈窗) + if ($request->filled('company_id')) { + $userQuery->where('company_id', $request->company_id); + } + + $users_list = $userQuery->latest()->paginate($per_page)->withQueryString(); + } + + // 4. 基礎下拉資料 (用於新增/編輯機台的彈窗) $models = MachineModel::select('id', 'name')->get(); $paymentConfigs = PaymentConfig::select('id', 'name')->get(); $companies = \App\Models\System\Company::select('id', 'name', 'code')->get(); @@ -54,6 +74,7 @@ class MachineSettingController extends AdminController return view('admin.basic-settings.machines.index', compact( 'machines', 'models_list', + 'users_list', 'models', 'paymentConfigs', 'companies', diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php index eed8012..ce4e7bf 100644 --- a/resources/views/admin/basic-settings/machines/index.blade.php +++ b/resources/views/admin/basic-settings/machines/index.blade.php @@ -114,6 +114,80 @@ window.dispatchEvent(new CustomEvent('toast', { detail: { message: '{{ __('Error processing request') }}', type: 'error' } })); }); }, + // Permission 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/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] }; + }, + toggleSelectAll() { + const filtered = this.allMachines.filter(m => + !this.permissionSearchQuery || + m.name.toLowerCase().includes(this.permissionSearchQuery.toLowerCase()) || + m.serial_no.toLowerCase().includes(this.permissionSearchQuery.toLowerCase()) + ); + if (filtered.length === 0) return; + const allSelected = filtered.every(m => this.permissions[m.id]); + filtered.forEach(m => this.permissions[m.id] = !allSelected); + }, + savePermissions() { + const machineIds = Object.keys(this.permissions).filter(id => this.permissions[id]); + + fetch(`/admin/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)"> @@ -131,7 +205,7 @@ {{ __('Add Machine') }} - @else + @elseif($tab === 'models') @@ -152,26 +226,43 @@ 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') }} + + {{ __('Machine Permissions') }} + - - - - - - - - - - + + + + + + + + + + + + + @if($tab === 'permissions' && auth()->user()->isSystemAdmin()) + + + + + + + + @endif + @if($tab === 'machines') @@ -320,7 +411,108 @@ {{ $machines->appends(['tab' => 'machines'])->links('vendor.pagination.luxury') }} - + @elseif($tab === 'permissions') + + + + + + + {{ __('Account Info') }} + + {{ __('Company Name') }} + + {{ __('Authorized Machines') }} + + {{ __('Action') }} + + + + @forelse($users_list as $user) + + + + + + + + + + {{ + $user->name }} + {{ + $user->username }} + + + + + + {{ $user->company->name ?? __('System') }} + + + + + @forelse($user->machines as $m) + + {{ + $m->name }} + {{ + $m->serial_no }} + + @empty + + -- + {{ __('None') }} -- + + @endforelse + + + + $user->id, "name" => $user->name]) }})' + class="inline-flex items-center gap-2 px-4 py-2 rounded-xl bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-500 hover:text-white transition-all duration-300 text-xs font-black uppercase tracking-widest shadow-sm shadow-cyan-500/5 group/auth"> + + + + {{ __('Authorize') }} + + + + @empty + + + + + + + {{ __('No + accounts found') }} + + + + @endforelse + + + + + @if($users_list) + {{ $users_list->appends(['tab' => 'permissions'])->links('vendor.pagination.luxury') }} + @endif + @else @@ -1024,6 +1216,165 @@ :confirm-text="__('Yes, regenerate')" /> + + + + + + + + + + + + + + {{ __('Authorized Machines Management') }} + + {{ + __('Account') }}: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ + __('Syncing Permissions...') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ __('Selection') }}: / {{ __('Devices') }} + + + + {{ __('Cancel') }} + + {{ __('Update Authorization') }} + + + + + + + +
{{ __('No + accounts found') }}
+ {{ __('Selection') }}: / {{ __('Devices') }} +