authorizeAbility($request, 'admin.members.view'); $organization = $this->organization($request); $owner = $this->ownerRef($request); $assignments = StaffAssignment::owned($owner) ->where('organization_id', $organization->id) ->unitPlacements() ->with(['member.branch', 'department.branch', 'careUnit']) ->orderByDesc('starts_on') ->orderByDesc('id') ->get(); $heroStats = [ 'total' => $assignments->count(), 'active' => $assignments->where('status', 'active')->filter(fn (StaffAssignment $a) => $a->isEffectiveOn())->count(), 'temporary' => $assignments->where('kind', 'temporary')->count(), 'primary' => $assignments->where('kind', 'primary')->count(), ]; return view('care.admin.staff-assignments.index', [ 'assignments' => $assignments, 'organization' => $organization, 'kinds' => config('care.staff_assignment_kinds'), 'statuses' => config('care.staff_assignment_statuses'), 'assignmentRoles' => config('care.staff_assignment_roles'), 'memberLabels' => $this->memberLabels($assignments->pluck('member')->filter()), 'heroStats' => $heroStats, ]); } public function create(Request $request): View { $this->authorizeAbility($request, 'admin.members.manage'); return view('care.admin.staff-assignments.create', $this->formData($request)); } public function store(Request $request): RedirectResponse { $this->authorizeAbility($request, 'admin.members.manage'); $organization = $this->organization($request); $owner = $this->ownerRef($request); $validated = $this->validated($request, $organization->id, $owner); $assignment = StaffAssignment::create([ 'owner_ref' => $owner, 'organization_id' => $organization->id, ...$validated, ]); AuditLogger::record($owner, 'staff_assignment.created', $organization->id, $owner, StaffAssignment::class, $assignment->id); return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment created.'); } public function edit(Request $request, StaffAssignment $staffAssignment): View { $this->authorizeAbility($request, 'admin.members.manage'); $this->authorizeOwner($request, $staffAssignment); return view('care.admin.staff-assignments.edit', [ ...$this->formData($request), 'assignment' => $staffAssignment, ]); } public function update(Request $request, StaffAssignment $staffAssignment): RedirectResponse { $this->authorizeAbility($request, 'admin.members.manage'); $this->authorizeOwner($request, $staffAssignment); $organization = $this->organization($request); $owner = $this->ownerRef($request); $validated = $this->validated($request, $organization->id, $owner); $staffAssignment->update($validated); AuditLogger::record($owner, 'staff_assignment.updated', $organization->id, $owner, StaffAssignment::class, $staffAssignment->id); return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment updated.'); } public function destroy(Request $request, StaffAssignment $staffAssignment): RedirectResponse { $this->authorizeAbility($request, 'admin.members.manage'); $this->authorizeOwner($request, $staffAssignment); $staffAssignment->delete(); AuditLogger::record( $this->ownerRef($request), 'staff_assignment.deleted', $staffAssignment->organization_id, $this->ownerRef($request), StaffAssignment::class, $staffAssignment->id, ); return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment removed.'); } /** * @return array */ protected function formData(Request $request): array { $organization = $this->organization($request); $owner = $this->ownerRef($request); $members = Member::owned($owner) ->where('organization_id', $organization->id) ->with('branch') ->orderBy('role') ->get(); $departments = Department::owned($owner) ->whereHas('branch', fn ($q) => $q->where('organization_id', $organization->id)) ->with('branch') ->where('is_active', true) ->orderBy('name') ->get(); $units = CareUnit::owned($owner) ->where('organization_id', $organization->id) ->where('is_active', true) ->with('department') ->orderBy('name') ->get(); return [ 'organization' => $organization, 'members' => $members, 'memberLabels' => $this->memberLabels($members), 'departments' => $departments, 'units' => $units, 'kinds' => config('care.staff_assignment_kinds'), 'statuses' => config('care.staff_assignment_statuses'), 'assignmentRoles' => config('care.staff_assignment_roles'), 'rbacRoles' => config('care.roles'), 'selectedMemberId' => $request->integer('member_id') ?: null, ]; } /** * @return array */ protected function validated(Request $request, int $organizationId, string $owner): array { $validated = $request->validate([ 'member_id' => ['required', 'integer', 'exists:care_members,id'], 'department_id' => ['nullable', 'integer', 'exists:care_departments,id'], 'care_unit_id' => [ 'nullable', 'integer', 'exists:care_care_units,id', Rule::requiredIf(fn () => $request->input('kind') === 'temporary'), ], 'kind' => ['required', 'string', Rule::in(array_keys(config('care.staff_assignment_kinds')))], 'assignment_role' => ['nullable', 'string', Rule::in(array_keys(config('care.staff_assignment_roles')))], 'starts_on' => ['required', 'date'], 'ends_on' => ['nullable', 'date', 'after_or_equal:starts_on'], 'status' => ['required', 'string', Rule::in(array_keys(config('care.staff_assignment_statuses')))], 'notes' => ['nullable', 'string', 'max:2000'], ], [ 'care_unit_id.required' => 'Temporary unit assignments require a care unit.', ]); $member = Member::owned($owner)->findOrFail($validated['member_id']); abort_unless((int) $member->organization_id === $organizationId, 404); if (! empty($validated['department_id'])) { $department = Department::owned($owner)->with('branch')->findOrFail($validated['department_id']); abort_unless((int) $department->branch?->organization_id === $organizationId, 404); } else { $validated['department_id'] = null; } if (! empty($validated['care_unit_id'])) { $unit = CareUnit::owned($owner)->findOrFail($validated['care_unit_id']); abort_unless((int) $unit->organization_id === $organizationId, 404); if (! empty($validated['department_id']) && (int) $unit->department_id !== (int) $validated['department_id']) { throw \Illuminate\Validation\ValidationException::withMessages([ 'care_unit_id' => 'Care unit must belong to the selected department.', ]); } if (empty($validated['department_id'])) { $validated['department_id'] = $unit->department_id; } } else { $validated['care_unit_id'] = null; } return [ 'member_id' => (int) $validated['member_id'], 'department_id' => $validated['department_id'] ? (int) $validated['department_id'] : null, 'care_unit_id' => $validated['care_unit_id'] ? (int) $validated['care_unit_id'] : null, 'kind' => $validated['kind'], 'assignment_role' => $validated['assignment_role'] ?? null, 'shift_code' => null, 'starts_on' => $validated['starts_on'], 'ends_on' => $validated['ends_on'] ?? null, 'status' => $validated['status'], 'notes' => $validated['notes'] ?? null, ]; } /** * @param iterable $members * @return array */ protected function memberLabels(iterable $members): array { $list = collect($members)->filter()->unique('id')->values(); $refs = $list->pluck('user_ref')->all(); $emails = User::query()->whereIn('public_id', $refs)->pluck('email', 'public_id'); $roles = config('care.roles'); $labels = []; foreach ($list as $member) { $display = str_contains((string) $member->user_ref, '@') ? $member->user_ref : ($emails[$member->user_ref] ?? $member->user_ref); $roleLabel = $roles[$member->role] ?? $member->role; $labels[(int) $member->id] = $display.' ยท '.$roleLabel; } return $labels; } }