Build hybrid ticketing inbox

This commit is contained in:
Joel Wedemire
2026-04-08 17:46:38 -07:00
parent aa6177cf62
commit 615c091f88
2 changed files with 769 additions and 213 deletions

View File

@@ -10,6 +10,7 @@ use Dashboard\Ticketing\Models\TicketingProject;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;
@@ -45,12 +46,35 @@ class TicketController extends Controller
$agentGroupIds = $this->agentGroupIds();
$isAgent = count($agentGroupIds) > 0;
$baseQuery = Ticket::query();
if ($isAgent) {
$baseQuery->whereIn('group_id', $agentGroupIds);
} else {
$baseQuery->where('submitter_id', $user->id);
}
$viewCounts = $isAgent
? [
'all' => (clone $baseQuery)->whereNotIn('status', ['resolved', 'closed'])->count(),
'mine' => (clone $baseQuery)->where('assigned_to', $user->id)->whereNotIn('status', ['resolved', 'closed'])->count(),
'unassigned' => (clone $baseQuery)->whereNull('assigned_to')->whereNotIn('status', ['resolved', 'closed'])->count(),
'pending' => (clone $baseQuery)->where('status', 'pending')->count(),
'resolved' => (clone $baseQuery)->whereIn('status', ['resolved', 'closed'])->count(),
]
: [
'all' => (clone $baseQuery)->count(),
'mine' => 0,
'unassigned' => 0,
'pending' => (clone $baseQuery)->where('status', 'pending')->count(),
'resolved' => (clone $baseQuery)->whereIn('status', ['resolved', 'closed'])->count(),
];
$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);
}
@@ -60,24 +84,36 @@ class TicketController extends Controller
if ($request->filled('priority_id')) {
$query->where('priority_id', $request->priority_id);
}
if ($request->filled('project_id')) {
$query->where('project_id', $request->project_id);
}
if ($request->filter === 'mine') {
$query->where('assigned_to', $user->id);
$query->where('assigned_to', $user->id)->whereNotIn('status', ['resolved', 'closed']);
} elseif ($request->filter === 'unassigned') {
$query->whereNull('assigned_to');
$query->whereNull('assigned_to')->whereNotIn('status', ['resolved', 'closed']);
} elseif ($request->filter === 'pending') {
$query->where('status', 'pending');
} elseif ($request->filter === 'resolved') {
$query->whereIn('status', ['resolved', 'closed']);
} elseif (!$request->filled('status')) {
$query->whereNotIn('status', ['resolved', 'closed']);
}
} else {
$query->where('submitter_id', $user->id);
if ($request->filled('project_id')) {
$query->where('project_id', $request->project_id);
}
if ($request->filter === 'pending') {
$query->where('status', 'pending');
} elseif ($request->filter === 'resolved') {
$query->whereIn('status', ['resolved', 'closed']);
}
}
$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');
$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;
@@ -85,22 +121,76 @@ class TicketController extends Controller
return $ticket;
});
$groups = TicketingGroup::when($isAgent, fn($q) => $q->whereIn('id', $agentGroupIds))->get();
$groups = TicketingGroup::when($isAgent, fn ($q) => $q->whereIn('id', $agentGroupIds))->get();
$priorities = PriorityLevel::whereNull('group_id')
->orWhereIn('group_id', $agentGroupIds ?? [])
->orWhereIn('group_id', $agentGroupIds ?: [0])
->orderBy('sort_order')
->get();
$projects = TicketingProject::when($isAgent, fn($q) => $q->whereIn('group_id', $agentGroupIds))
$projects = TicketingProject::when($isAgent, fn ($q) => $q->whereIn('group_id', $agentGroupIds))
->where('status', 'active')
->get();
$projectCounts = (clone $baseQuery)
->select('project_id', DB::raw('count(*) as aggregate'))
->whereNotNull('project_id')
->groupBy('project_id')
->pluck('aggregate', 'project_id');
$projects->transform(function ($project) use ($projectCounts) {
$project->ticket_count = (int) ($projectCounts[$project->id] ?? 0);
return $project;
});
$ticketDetail = null;
$detailAgents = [];
if ($request->filled('detail')) {
$detailQuery = Ticket::with([
'group',
'priority',
'project',
'messages.attachments',
'attachments',
])->whereKey($request->detail);
if ($isAgent) {
$detailQuery->whereIn('group_id', $agentGroupIds);
} else {
$detailQuery->where('submitter_id', $user->id);
}
$dt = $detailQuery->first();
if ($dt) {
$detailUserIds = collect([$dt->submitter_id, $dt->assigned_to])
->merge($dt->messages->pluck('user_id'))
->filter()
->unique();
$detailUsers = DB::table('users')->whereIn('id', $detailUserIds)->get(['id', 'name', 'email'])->keyBy('id');
$dt->submitter = $detailUsers[$dt->submitter_id] ?? null;
$dt->assignee = $dt->assigned_to ? ($detailUsers[$dt->assigned_to] ?? null) : null;
$dt->messages->each(function ($msg) use ($detailUsers) {
$msg->author = $msg->user_id ? ($detailUsers[$msg->user_id] ?? null) : null;
});
$ticketDetail = $dt;
if ($isAgent) {
$agentIds = TicketingAgentAccess::where('group_id', $dt->group_id)->pluck('user_id');
$detailAgents = DB::table('users')->whereIn('id', $agentIds)->get(['id', 'name', 'email']);
}
}
}
return Inertia::render('Ticketing/Index', [
'tickets' => $tickets,
'groups' => $groups,
'priorities' => $priorities,
'projects' => $projects,
'isAgent' => $isAgent,
'filters' => $request->only(['group_id', 'status', 'priority_id', 'filter']),
'filters' => $request->only(['group_id', 'status', 'priority_id', 'project_id', 'filter', 'detail']),
'ticketDetail' => $ticketDetail,
'detailAgents' => $detailAgents,
'viewCounts' => $viewCounts,
]);
}
@@ -154,19 +244,17 @@ class TicketController extends Controller
$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');
$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']);
$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))
@@ -191,7 +279,7 @@ class TicketController extends Controller
$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']);
$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', [
@@ -217,7 +305,6 @@ class TicketController extends Controller
'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';
}