362 lines
13 KiB
PHP
362 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\SupportTicket;
|
|
use App\Models\SupportTicketReply;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class SupportTicketController extends Controller
|
|
{
|
|
// ==========================================
|
|
// WORKER ENDPOINTS
|
|
// ==========================================
|
|
|
|
public function getTicketsForWorker(Request $request)
|
|
{
|
|
$worker = $request->attributes->get('worker');
|
|
if (!$worker) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$tickets = SupportTicket::with('reason')->where('worker_id', $worker->id)
|
|
->orderBy('created_at', 'desc')
|
|
->get()
|
|
->map(function ($ticket) {
|
|
return [
|
|
'id' => $ticket->id,
|
|
'ticket_number' => $ticket->ticket_number,
|
|
'subject' => $ticket->subject,
|
|
'description' => $ticket->description,
|
|
'reason_id' => $ticket->reason_id,
|
|
'reason_name' => $ticket->reason ? $ticket->reason->reason : null,
|
|
'voice_note_url' => $ticket->voice_note_path ? asset('storage/' . $ticket->voice_note_path) : null,
|
|
'status' => $ticket->status,
|
|
'priority' => $ticket->priority,
|
|
'created_at' => $ticket->created_at->toIso8601String(),
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'tickets' => $tickets
|
|
]);
|
|
}
|
|
|
|
public function createTicketFromWorker(Request $request)
|
|
{
|
|
$worker = $request->attributes->get('worker');
|
|
if (!$worker) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'subject' => 'required|string|max:255',
|
|
'description' => 'required|string',
|
|
'priority' => 'nullable|string|in:low,medium,high',
|
|
'reason_id' => 'nullable|exists:report_reasons,id',
|
|
'voice_note' => 'nullable|file|mimes:mp3,wav,m4a,ogg,webm,aac,3gp,amr|max:10240', // 10MB limit
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$voiceNotePath = null;
|
|
if ($request->hasFile('voice_note')) {
|
|
$voiceNotePath = $request->file('voice_note')->store('support_voice_notes', 'public');
|
|
}
|
|
|
|
$ticket = SupportTicket::create([
|
|
'ticket_number' => 'TKT-' . rand(100000, 999999),
|
|
'worker_id' => $worker->id,
|
|
'reason_id' => $request->reason_id,
|
|
'subject' => $request->subject,
|
|
'description' => $request->description,
|
|
'voice_note_path' => $voiceNotePath,
|
|
'priority' => $request->priority ?? 'medium',
|
|
'status' => 'open',
|
|
]);
|
|
|
|
$ticket->load('reason');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Ticket created successfully.',
|
|
'ticket' => [
|
|
'id' => $ticket->id,
|
|
'ticket_number' => $ticket->ticket_number,
|
|
'subject' => $ticket->subject,
|
|
'description' => $ticket->description,
|
|
'reason_id' => $ticket->reason_id,
|
|
'reason_name' => $ticket->reason ? $ticket->reason->reason : null,
|
|
'voice_note_url' => $ticket->voice_note_path ? asset('storage/' . $ticket->voice_note_path) : null,
|
|
'status' => $ticket->status,
|
|
'priority' => $ticket->priority,
|
|
'created_at' => $ticket->created_at->toIso8601String(),
|
|
]
|
|
], 201);
|
|
}
|
|
|
|
public function replyToTicketFromWorker(Request $request, $id)
|
|
{
|
|
$worker = $request->attributes->get('worker');
|
|
if (!$worker) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$ticket = SupportTicket::where('worker_id', $worker->id)->find($id);
|
|
if (!$ticket) {
|
|
return response()->json(['success' => false, 'message' => 'Ticket not found.'], 404);
|
|
}
|
|
|
|
if ($ticket->status === 'closed') {
|
|
return response()->json(['success' => false, 'message' => 'Cannot reply to a closed ticket.'], 422);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'message' => 'required_without:voice_note|nullable|string',
|
|
'voice_note' => 'nullable|file|mimes:mp3,wav,m4a,ogg,webm,aac,3gp,amr|max:10240', // 10MB limit
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$voiceNotePath = null;
|
|
if ($request->hasFile('voice_note')) {
|
|
$voiceNotePath = $request->file('voice_note')->store('support_voice_notes', 'public');
|
|
}
|
|
|
|
$reply = SupportTicketReply::create([
|
|
'support_ticket_id' => $ticket->id,
|
|
'worker_id' => $worker->id,
|
|
'message' => $request->message,
|
|
'voice_note_path' => $voiceNotePath,
|
|
]);
|
|
|
|
if ($ticket->status === 'resolved') {
|
|
$ticket->update(['status' => 'open']);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Reply posted successfully.',
|
|
'reply' => [
|
|
'id' => $reply->id,
|
|
'message' => $reply->message,
|
|
'sender_name' => $reply->sender_name,
|
|
'voice_note_url' => $reply->voice_note_path ? asset('storage/' . $reply->voice_note_path) : null,
|
|
'created_at' => $reply->created_at->toIso8601String(),
|
|
]
|
|
], 201);
|
|
}
|
|
|
|
|
|
// ==========================================
|
|
// EMPLOYER ENDPOINTS
|
|
// ==========================================
|
|
|
|
public function getTicketsForEmployer(Request $request)
|
|
{
|
|
$employer = $request->attributes->get('employer');
|
|
if (!$employer) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$tickets = SupportTicket::where('user_id', $employer->id)
|
|
->orderBy('created_at', 'desc')
|
|
->get()
|
|
->map(function ($ticket) {
|
|
return [
|
|
'id' => $ticket->id,
|
|
'ticket_number' => $ticket->ticket_number,
|
|
'subject' => $ticket->subject,
|
|
'description' => $ticket->description,
|
|
'status' => $ticket->status,
|
|
'priority' => $ticket->priority,
|
|
'created_at' => $ticket->created_at->toIso8601String(),
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'tickets' => $tickets
|
|
]);
|
|
}
|
|
|
|
public function createTicketFromEmployer(Request $request)
|
|
{
|
|
$employer = $request->attributes->get('employer');
|
|
if (!$employer) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'subject' => 'required|string|max:255',
|
|
'description' => 'required|string',
|
|
'priority' => 'nullable|string|in:low,medium,high',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$ticket = SupportTicket::create([
|
|
'ticket_number' => 'TKT-' . rand(100000, 999999),
|
|
'user_id' => $employer->id,
|
|
'subject' => $request->subject,
|
|
'description' => $request->description,
|
|
'priority' => $request->priority ?? 'medium',
|
|
'status' => 'open',
|
|
]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Ticket created successfully.',
|
|
'ticket' => [
|
|
'id' => $ticket->id,
|
|
'ticket_number' => $ticket->ticket_number,
|
|
'subject' => $ticket->subject,
|
|
'description' => $ticket->description,
|
|
'status' => $ticket->status,
|
|
'priority' => $ticket->priority,
|
|
'created_at' => $ticket->created_at->toIso8601String(),
|
|
]
|
|
], 201);
|
|
}
|
|
|
|
public function replyToTicketFromEmployer(Request $request, $id)
|
|
{
|
|
$employer = $request->attributes->get('employer');
|
|
if (!$employer) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$ticket = SupportTicket::where('user_id', $employer->id)->find($id);
|
|
if (!$ticket) {
|
|
return response()->json(['success' => false, 'message' => 'Ticket not found.'], 404);
|
|
}
|
|
|
|
if ($ticket->status === 'closed') {
|
|
return response()->json(['success' => false, 'message' => 'Cannot reply to a closed ticket.'], 422);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'message' => 'required_without:voice_note|nullable|string',
|
|
'voice_note' => 'nullable|file|mimes:mp3,wav,m4a,ogg,webm,aac,3gp,amr|max:10240', // 10MB limit
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$voiceNotePath = null;
|
|
if ($request->hasFile('voice_note')) {
|
|
$voiceNotePath = $request->file('voice_note')->store('support_voice_notes', 'public');
|
|
}
|
|
|
|
$reply = SupportTicketReply::create([
|
|
'support_ticket_id' => $ticket->id,
|
|
'user_id' => $employer->id,
|
|
'message' => $request->message,
|
|
'voice_note_path' => $voiceNotePath,
|
|
]);
|
|
|
|
if ($ticket->status === 'resolved') {
|
|
$ticket->update(['status' => 'open']);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Reply posted successfully.',
|
|
'reply' => [
|
|
'id' => $reply->id,
|
|
'message' => $reply->message,
|
|
'sender_name' => $reply->sender_name,
|
|
'voice_note_url' => $reply->voice_note_path ? asset('storage/' . $reply->voice_note_path) : null,
|
|
'created_at' => $reply->created_at->toIso8601String(),
|
|
]
|
|
], 201);
|
|
}
|
|
|
|
|
|
// ==========================================
|
|
// SHARED GET DETAILS
|
|
// ==========================================
|
|
|
|
public function getTicketDetail(Request $request, $id)
|
|
{
|
|
$worker = $request->attributes->get('worker');
|
|
$employer = $request->attributes->get('employer');
|
|
|
|
if (!$worker && !$employer) {
|
|
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
|
}
|
|
|
|
$query = SupportTicket::with('reason')->where('id', $id);
|
|
if ($worker) {
|
|
$query->where('worker_id', $worker->id);
|
|
} else {
|
|
$query->where('user_id', $employer->id);
|
|
}
|
|
|
|
$ticket = $query->first();
|
|
if (!$ticket) {
|
|
return response()->json(['success' => false, 'message' => 'Ticket not found.'], 404);
|
|
}
|
|
|
|
$replies = SupportTicketReply::where('support_ticket_id', $ticket->id)
|
|
->with(['user', 'worker'])
|
|
->orderBy('created_at', 'asc')
|
|
->get()
|
|
->map(function ($reply) {
|
|
return [
|
|
'id' => $reply->id,
|
|
'message' => $reply->message,
|
|
'sender_name' => $reply->sender_name,
|
|
'is_admin' => $reply->user && $reply->user->role === 'admin',
|
|
'is_developer_response' => (bool)$reply->is_developer_response,
|
|
'voice_note_url' => $reply->voice_note_path ? asset('storage/' . $reply->voice_note_path) : null,
|
|
'created_at' => $reply->created_at->toIso8601String(),
|
|
];
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'ticket' => [
|
|
'id' => $ticket->id,
|
|
'ticket_number' => $ticket->ticket_number,
|
|
'subject' => $ticket->subject,
|
|
'description' => $ticket->description,
|
|
'reason_id' => $ticket->reason_id,
|
|
'reason_name' => $ticket->reason ? $ticket->reason->reason : null,
|
|
'voice_note_url' => $ticket->voice_note_path ? asset('storage/' . $ticket->voice_note_path) : null,
|
|
'status' => $ticket->status,
|
|
'priority' => $ticket->priority,
|
|
'created_at' => $ticket->created_at->toIso8601String(),
|
|
],
|
|
'replies' => $replies
|
|
]);
|
|
}
|
|
}
|