pluck('group_id') ->toArray(); } private function isAgent(int $groupId = null): bool { $query = TicketingAgentAccess::where('user_id', Auth::id()); if ($groupId) { $query->where('group_id', $groupId); } return $query->exists(); } private function isManager(int $groupId): bool { return TicketingAgentAccess::where('user_id', Auth::id()) ->where('group_id', $groupId) ->where('role', 'manager') ->exists(); } public function index(Request $request): Response { $user = Auth::user(); $agentGroupIds = $this->agentGroupIds(); $isAgent = count($agentGroupIds) > 0; $query = Ticket::with(['group', 'priority', 'project']); if ($isAgent) { $query->whereIn('group_id', $agentGroupIds); // Filters if ($request->filled('group_id')) { $query->where('group_id', $request->group_id); } if ($request->filled('status')) { $query->where('status', $request->status); } if ($request->filled('priority_id')) { $query->where('priority_id', $request->priority_id); } if ($request->filter === 'mine') { $query->where('assigned_to', $user->id); } elseif ($request->filter === 'unassigned') { $query->whereNull('assigned_to'); } elseif ($request->filter === 'pending') { $query->where('status', 'pending'); } elseif (!$request->filled('status')) { $query->whereNotIn('status', ['resolved', 'closed']); } } else { $query->where('submitter_id', $user->id); } $tickets = $query->latest()->paginate(30)->withQueryString(); // Enrich with submitter name $userIds = $tickets->pluck('submitter_id')->merge($tickets->pluck('assigned_to'))->filter()->unique(); $users = \DB::table('users')->whereIn('id', $userIds)->get(['id', 'name', 'email'])->keyBy('id'); $tickets->getCollection()->transform(function ($ticket) use ($users) { $ticket->submitter = $users[$ticket->submitter_id] ?? null; $ticket->assignee = $ticket->assigned_to ? ($users[$ticket->assigned_to] ?? null) : null; return $ticket; }); $groups = TicketingGroup::when($isAgent, fn($q) => $q->whereIn('id', $agentGroupIds))->get(); $priorities = PriorityLevel::whereNull('group_id') ->orWhenIn('group_id', $agentGroupIds ?? []) ->orderBy('sort_order') ->get(); $projects = TicketingProject::when($isAgent, fn($q) => $q->whereIn('group_id', $agentGroupIds)) ->where('status', 'active') ->get(); return Inertia::render('Ticketing/Index', [ 'tickets' => $tickets, 'groups' => $groups, 'priorities' => $priorities, 'projects' => $projects, 'isAgent' => $isAgent, 'filters' => $request->only(['group_id', 'status', 'priority_id', 'filter']), ]); } public function create(): Response { $user = Auth::user(); $agentGroupIds = $this->agentGroupIds(); $isAgent = count($agentGroupIds) > 0; $groups = TicketingGroup::when($isAgent, fn($q) => $q->whereIn('id', $agentGroupIds))->get(); $priorities = PriorityLevel::orderBy('sort_order')->get(); return Inertia::render('Ticketing/Create', [ 'groups' => $groups, 'priorities' => $priorities, ]); } public function store(Request $request) { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'required|string', 'group_id' => 'required|exists:ticketing_groups,id', 'priority_id' => 'nullable|exists:ticketing_priority_levels,id', 'due_date' => 'nullable|date', ]); $group = TicketingGroup::findOrFail($validated['group_id']); $number = $group->nextTicketNumber(); $ticket = Ticket::create([ ...$validated, 'number' => $number, 'submitter_id' => Auth::id(), 'status' => 'open', ]); return redirect()->route('ticketing.show', $ticket); } public function show(Ticket $ticket): Response { $user = Auth::user(); $isAgent = $this->isAgent($ticket->group_id); $isSubmitter = $ticket->submitter_id === $user->id; if (!$isAgent && !$isSubmitter) { abort(403); } $ticket->load(['group', 'priority', 'project', 'messages', 'attachments']); // Enrich messages with user data $userIds = $ticket->messages->pluck('user_id')->filter()->unique(); $users = \DB::table('users')->whereIn('id', $userIds)->get(['id', 'name', 'email'])->keyBy('id'); $ticket->messages->each(function ($msg) use ($users) { $msg->author = $msg->user_id ? ($users[$msg->user_id] ?? null) : null; }); // Agents for assignment picker $agents = []; if ($isAgent) { $agentIds = TicketingAgentAccess::where('group_id', $ticket->group_id)->pluck('user_id'); $agents = \DB::table('users')->whereIn('id', $agentIds)->get(['id', 'name', 'email']); } $priorities = PriorityLevel::where(fn($q) => $q->whereNull('group_id')->orWhere('group_id', $ticket->group_id)) ->orderBy('sort_order')->get(); return Inertia::render('Ticketing/Show', [ 'ticket' => $ticket, 'isAgent' => $isAgent, 'isManager' => $isAgent && $this->isManager($ticket->group_id), 'agents' => $agents, 'priorities' => $priorities, ]); } public function edit(Ticket $ticket): Response { if (!$this->isAgent($ticket->group_id)) { abort(403); } $ticket->load(['group', 'priority', 'project']); $priorities = PriorityLevel::where(fn($q) => $q->whereNull('group_id')->orWhere('group_id', $ticket->group_id)) ->orderBy('sort_order')->get(); $agentIds = TicketingAgentAccess::where('group_id', $ticket->group_id)->pluck('user_id'); $agents = \DB::table('users')->whereIn('id', $agentIds)->get(['id', 'name', 'email']); $projects = TicketingProject::where('group_id', $ticket->group_id)->get(); return Inertia::render('Ticketing/Edit', [ 'ticket' => $ticket, 'priorities' => $priorities, 'agents' => $agents, 'projects' => $projects, ]); } public function update(Request $request, Ticket $ticket) { if (!$this->isAgent($ticket->group_id)) { abort(403); } $rules = [ 'title' => 'sometimes|required|string|max:255', 'description' => 'sometimes|required|string', 'status' => 'sometimes|in:open,in_progress,pending,resolved,closed', 'priority_id' => 'nullable|exists:ticketing_priority_levels,id', 'due_date' => 'nullable|date', 'project_id' => 'nullable|exists:ticketing_projects,id', ]; // assigned_to only settable by agents if ($this->isAgent($ticket->group_id)) { $rules['assigned_to'] = 'nullable|exists:users,id'; } $ticket->update($request->validate($rules)); return back()->with('success', 'Ticket updated.'); } public function destroy(Ticket $ticket) { if (!$this->isManager($ticket->group_id)) { abort(403); } $ticket->delete(); return redirect()->route('ticketing.index'); } public function myTickets(): Response { $tickets = Ticket::where('submitter_id', Auth::id()) ->with(['group', 'priority']) ->latest() ->paginate(20); return Inertia::render('Ticketing/MyTickets', [ 'tickets' => $tickets, ]); } }