input('tab', 'list'); $per_page = $tab === 'list' ? $request->input('per_page', 10) : $request->input('per_page', 12); $query = Machine::query(); // 搜尋:名稱或序號 if ($search = $request->input('search')) { $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('serial_no', 'like', "%{$search}%"); }); } if ($tab === 'list') { $machines = $query->when($request->status, function ($query, $status) { return $query->where('status', $status); }) ->latest() ->paginate($per_page) ->withQueryString(); return view('admin.machines.index', compact('machines', 'tab')); } else { // 效期管理模式:獲取機台及其貨道統計 $machines = $query->withCount(['slots as total_slots']) ->withCount(['slots as expired_count' => function ($q) { $q->where('expiry_date', '<', now()->toDateString()); }]) ->withCount(['slots as pending_count' => function ($q) { $q->whereNull('expiry_date'); }]) ->withCount(['slots as warning_count' => function ($q) { $q->whereBetween('expiry_date', [now()->toDateString(), now()->addDays(7)->toDateString()]); }]) ->latest() ->paginate($per_page) ->withQueryString(); return view('admin.machines.index', compact('machines', 'tab')); } } /** * 顯示特定機台的日誌與詳細資訊 */ public function show(int $id): View { $machine = Machine::with(['logs' => function ($query) { $query->latest()->limit(50); }])->findOrFail($id); return view('admin.machines.show', compact('machine')); } /** * AJAX: 取得機台抽屜面板所需的歷程日誌 */ public function logsAjax(Request $request, Machine $machine) { $per_page = $request->input('per_page', 20); $startDate = $request->get('start_date', now()->format('Y-m-d')); $endDate = $request->get('end_date', now()->format('Y-m-d')); $logs = $machine->logs() ->when($request->level, function ($query, $level) { return $query->where('level', $level); }) ->whereDate('created_at', '>=', $startDate) ->whereDate('created_at', '<=', $endDate) ->when($request->type, function ($query, $type) { return $query->where('type', $type); }) ->latest() ->paginate($per_page); return response()->json([ 'success' => true, 'data' => $logs->items(), 'pagination' => [ 'total' => $logs->total(), 'current_page' => $logs->currentPage(), 'last_page' => $logs->lastPage(), ] ]); } /** * AJAX: 取得特定帳號的機台分配狀態 */ public function getAccountMachines(\App\Models\System\User $user) { $currentUser = auth()->user(); // 安全檢查:只能操作自己公司的帳號(除非是系統管理員) if (!$currentUser->isSystemAdmin() && $user->company_id !== $currentUser->company_id) { return response()->json(['error' => 'Unauthorized'], 403); } // 取得該公司所有機台 (限定 company_id 以實作資料隔離) $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: 儲存特定帳號的機台分配 */ public function syncAccountMachines(Request $request, \App\Models\System\User $user) { $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() ]); } /** * 機台使用率統計 */ public function utilization(Request $request): View { // 取得當前使用者有權限的所有機台 (已透過 Global Scope 過濾) $machines = Machine::all(); return view('admin.machines.utilization', [ 'machines' => $machines ]); } /** * AJAX: 取得機台所有貨道資訊 (供效期管理視覺化圖表使用) */ public function slotsAjax(Machine $machine) { $slots = $machine->slots()->with('product:id,name,image')->orderByRaw('CAST(slot_no AS UNSIGNED) ASC')->get(); return response()->json([ 'success' => true, 'machine' => $machine->only(['id', 'name', 'serial_no']), 'slots' => $slots ]); } /** * AJAX: 更新貨道效期 */ public function updateSlotExpiry(Request $request, Machine $machine) { $request->validate([ 'slot_no' => 'required|integer', 'expiry_date' => 'nullable|date', 'apply_all_same_product' => 'boolean' ]); $slotNo = $request->slot_no; $expiryDate = $request->expiry_date; $applyAll = $request->apply_all_same_product ?? false; $slot = $machine->slots()->where('slot_no', $slotNo)->firstOrFail(); $slot->update(['expiry_date' => $expiryDate]); if ($applyAll && $slot->product_id) { $machine->slots() ->where('product_id', $slot->product_id) ->update(['expiry_date' => $expiryDate]); } return response()->json([ 'success' => true, 'message' => __('Expiry updated successfully.') ]); } /** * 取得機台統計數據 (AJAX) */ public function utilizationData(int $id, Request $request) { $machine = Machine::findOrFail($id); $date = $request->get('date', now()->toDateString()); $service = app(\App\Services\Machine\MachineService::class); $stats = $service->getUtilizationStats($machine, $date); return response()->json([ 'success' => true, 'data' => $stats ]); } /** * 機台維護紀錄 (開發中) */ public function maintenance(Request $request): View { return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder } }