From b52e67c42873d868d4218bfcddd7aac12385d123 Mon Sep 17 00:00:00 2001 From: Wian Drs Date: Mon, 22 Jun 2026 13:34:49 +0700 Subject: [PATCH] ticker work proses --- app/Enums/TicketStatus.php | 17 + .../Ticket/TicketWorkflowController.php | 247 +++++++++++++ .../Requests/Ticket/ApproveTicketRequest.php | 29 ++ .../Requests/Ticket/AssignTicketRequest.php | 30 ++ .../Requests/Ticket/CancelTicketRequest.php | 29 ++ .../Requests/Ticket/CloseTicketRequest.php | 29 ++ .../Requests/Ticket/EscalateTicketRequest.php | 37 ++ .../Requests/Ticket/PendingTicketRequest.php | 29 ++ .../Requests/Ticket/ReassignTicketRequest.php | 40 ++ .../Requests/Ticket/RejectTicketRequest.php | 29 ++ .../Requests/Ticket/ResolveTicketRequest.php | 29 ++ .../Requests/Ticket/StartTicketRequest.php | 31 ++ app/Models/Ticket.php | 22 ++ app/Models/TicketApproval.php | 11 +- app/Models/TicketAssignment.php | 11 +- app/Models/TicketType.php | 26 +- app/Services/Ticket/TicketWorkflowService.php | 341 ++++++++++++++++++ .../Ticket/TicketWorkflowValidator.php | 22 ++ ...06_19_044044_create_ticket_types_table.php | 5 + database/seeders/DatabaseSeeder.php | 3 + database/seeders/TicketTypeSeeder.php | 145 ++++++++ routes/api.php | 47 ++- 22 files changed, 1193 insertions(+), 16 deletions(-) create mode 100644 app/Enums/TicketStatus.php create mode 100644 app/Http/Controllers/Ticket/TicketWorkflowController.php create mode 100644 app/Http/Requests/Ticket/ApproveTicketRequest.php create mode 100644 app/Http/Requests/Ticket/AssignTicketRequest.php create mode 100644 app/Http/Requests/Ticket/CancelTicketRequest.php create mode 100644 app/Http/Requests/Ticket/CloseTicketRequest.php create mode 100644 app/Http/Requests/Ticket/EscalateTicketRequest.php create mode 100644 app/Http/Requests/Ticket/PendingTicketRequest.php create mode 100644 app/Http/Requests/Ticket/ReassignTicketRequest.php create mode 100644 app/Http/Requests/Ticket/RejectTicketRequest.php create mode 100644 app/Http/Requests/Ticket/ResolveTicketRequest.php create mode 100644 app/Http/Requests/Ticket/StartTicketRequest.php create mode 100644 app/Services/Ticket/TicketWorkflowService.php create mode 100644 app/Services/Ticket/TicketWorkflowValidator.php create mode 100644 database/seeders/TicketTypeSeeder.php diff --git a/app/Enums/TicketStatus.php b/app/Enums/TicketStatus.php new file mode 100644 index 0000000..7dba178 --- /dev/null +++ b/app/Enums/TicketStatus.php @@ -0,0 +1,17 @@ +service->history($ticket); + + return $this->success( + $data, + 'Workflow berhasil diambil' + ); + } + + /** + * Assign Teknisi + */ + public function assign( + AssignTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->assign( + $ticket, + $request->technicians, + $request->leader_id, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Teknisi berhasil ditugaskan' + ); + } + + /** + * Reassign Teknisi + */ + public function reassign( + ReassignTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->reassign( + $ticket, + $request->technicians, + $request->leader_id, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Penugasan berhasil diperbarui' + ); + } + + /** + * Approve + */ + public function approve( + ApproveTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->approve( + $ticket, + $request->notes, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil disetujui' + ); + } + + /** + * Reject + */ + public function reject( + RejectTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->reject( + $ticket, + $request->reason, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil ditolak' + ); + } + + /** + * Start Work + */ + public function start( + StartTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->start( + $ticket, + $request->validated(), + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Pekerjaan dimulai' + ); + } + + /** + * Pending + */ + public function pending( + PendingTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->pending( + $ticket, + $request->reason, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket pending' + ); + } + + /** + * Resume + */ + public function resume(Ticket $ticket) + { + $ticket = $this->service->resume( + $ticket, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket dilanjutkan' + ); + } + + /** + * Resolve + */ + public function resolve( + ResolveTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->resolve( + $ticket, + $request->notes, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil diselesaikan' + ); + } + + /** + * Close + */ + public function close( + CloseTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->close( + $ticket, + $request->notes, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil ditutup' + ); + } + + /** + * Cancel + */ + public function cancel( + CancelTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->cancel( + $ticket, + $request->reason, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil dibatalkan' + ); + } + + /** + * Escalate + */ + public function escalate( + EscalateTicketRequest $request, + Ticket $ticket + ) { + $ticket = $this->service->escalate( + $ticket, + $request->reason, + auth()->id() ?? 1 + ); + + return $this->success( + new TicketResource($ticket), + 'Ticket berhasil dieskalasi' + ); + } +} \ No newline at end of file diff --git a/app/Http/Requests/Ticket/ApproveTicketRequest.php b/app/Http/Requests/Ticket/ApproveTicketRequest.php new file mode 100644 index 0000000..f723aa1 --- /dev/null +++ b/app/Http/Requests/Ticket/ApproveTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'notes' => 'nullable|string|max:1000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/AssignTicketRequest.php b/app/Http/Requests/Ticket/AssignTicketRequest.php new file mode 100644 index 0000000..aed707a --- /dev/null +++ b/app/Http/Requests/Ticket/AssignTicketRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array + { + return [ + 'technicians' => 'required|array|min:1', + 'leader_id' => 'required|integer', + ]; + } +} diff --git a/app/Http/Requests/Ticket/CancelTicketRequest.php b/app/Http/Requests/Ticket/CancelTicketRequest.php new file mode 100644 index 0000000..fd9ca4b --- /dev/null +++ b/app/Http/Requests/Ticket/CancelTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'reason' => 'required|string|max:2000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/CloseTicketRequest.php b/app/Http/Requests/Ticket/CloseTicketRequest.php new file mode 100644 index 0000000..29bfad2 --- /dev/null +++ b/app/Http/Requests/Ticket/CloseTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'notes' => 'nullable|string|max:2000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/EscalateTicketRequest.php b/app/Http/Requests/Ticket/EscalateTicketRequest.php new file mode 100644 index 0000000..11c381f --- /dev/null +++ b/app/Http/Requests/Ticket/EscalateTicketRequest.php @@ -0,0 +1,37 @@ +|string> + */ + public function rules(): array + { + return [ + 'reason' => 'required|string|max:2000', + 'level' => 'nullable|in:L1,L2,L3,NOC' + ]; + } + + public function messages(): array + { + return [ + 'reason.required' => 'Alasan eskalasi wajib diisi' + ]; + } +} diff --git a/app/Http/Requests/Ticket/PendingTicketRequest.php b/app/Http/Requests/Ticket/PendingTicketRequest.php new file mode 100644 index 0000000..eed1563 --- /dev/null +++ b/app/Http/Requests/Ticket/PendingTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'reason' => 'required|string|max:1000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/ReassignTicketRequest.php b/app/Http/Requests/Ticket/ReassignTicketRequest.php new file mode 100644 index 0000000..03e1fb9 --- /dev/null +++ b/app/Http/Requests/Ticket/ReassignTicketRequest.php @@ -0,0 +1,40 @@ +|string> + */ + public function rules(): array + { + return [ + 'technicians' => 'required|array|min:1', + 'technicians.*' => 'integer', + 'leader_id' => 'required|integer' + ]; + } + + public function messages(): array + { + return [ + 'technicians.required' => 'Daftar teknisi wajib diisi', + 'technicians.array' => 'Format teknisi harus array', + 'leader_id.required' => 'Leader teknisi wajib dipilih' + ]; + } +} diff --git a/app/Http/Requests/Ticket/RejectTicketRequest.php b/app/Http/Requests/Ticket/RejectTicketRequest.php new file mode 100644 index 0000000..4908c0e --- /dev/null +++ b/app/Http/Requests/Ticket/RejectTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'reason' => 'required|string|max:1000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/ResolveTicketRequest.php b/app/Http/Requests/Ticket/ResolveTicketRequest.php new file mode 100644 index 0000000..b5140c8 --- /dev/null +++ b/app/Http/Requests/Ticket/ResolveTicketRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'notes' => 'required|string|max:2000' + ]; + } +} diff --git a/app/Http/Requests/Ticket/StartTicketRequest.php b/app/Http/Requests/Ticket/StartTicketRequest.php new file mode 100644 index 0000000..3e75dae --- /dev/null +++ b/app/Http/Requests/Ticket/StartTicketRequest.php @@ -0,0 +1,31 @@ +|string> + */ + public function rules(): array + { + return [ + 'latitude' => 'nullable|numeric', + 'longitude' => 'nullable|numeric', + 'notes' => 'nullable|string|max:1000' + ]; + } +} diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index a5fc0a7..c76ec8f 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -15,5 +15,27 @@ class Ticket extends Model 'priority', 'status', 'created_by', + 'assigned_at', + 'approved_by', + 'approved_at', + 'rejected_by', + 'rejected_at', + 'rejection_reason', + 'resolved_at', + 'closed_at', ]; + + /* + |-------------------------------------------------------------------------- + | Relationships + |-------------------------------------------------------------------------- + */ + + public function ticketType() + { + return $this->belongsTo( + TicketType::class, + 'ticket_type_id' + ); + } } diff --git a/app/Models/TicketApproval.php b/app/Models/TicketApproval.php index 9bdb1dd..3d2ac2e 100644 --- a/app/Models/TicketApproval.php +++ b/app/Models/TicketApproval.php @@ -6,5 +6,14 @@ use Illuminate\Database\Eloquent\Model; class TicketApproval extends Model { - // + public $timestamps = false; + + protected $fillable = [ + 'ticket_id', + 'approver_id', + 'status', + 'notes', + 'approved_at', + 'created_at' + ]; } diff --git a/app/Models/TicketAssignment.php b/app/Models/TicketAssignment.php index f71f8cd..1f086bd 100644 --- a/app/Models/TicketAssignment.php +++ b/app/Models/TicketAssignment.php @@ -6,5 +6,14 @@ use Illuminate\Database\Eloquent\Model; class TicketAssignment extends Model { - // + public $timestamps = false; + + protected $fillable = [ + 'ticket_id', + 'user_id', + 'is_leader', + 'assigned_by', + 'assigned_at', + 'status' + ]; } diff --git a/app/Models/TicketType.php b/app/Models/TicketType.php index 6a4a458..c5b1ddc 100644 --- a/app/Models/TicketType.php +++ b/app/Models/TicketType.php @@ -12,10 +12,30 @@ class TicketType extends Model 'code', 'name', 'need_approval', - 'sla_minutes' + 'sla_minutes', + 'require_photo', + 'require_material', + 'need_customer', ]; protected $casts = [ - 'need_approval' => 'boolean', + 'need_approval' => 'boolean', + 'require_photo' => 'boolean', + 'require_material' => 'boolean', + 'need_customer' => 'boolean', ]; -} + + /* + |-------------------------------------------------------------------------- + | Relationships + |-------------------------------------------------------------------------- + */ + + public function tickets() + { + return $this->hasMany( + Ticket::class, + 'ticket_type_id' + ); + } +} \ No newline at end of file diff --git a/app/Services/Ticket/TicketWorkflowService.php b/app/Services/Ticket/TicketWorkflowService.php new file mode 100644 index 0000000..a00473e --- /dev/null +++ b/app/Services/Ticket/TicketWorkflowService.php @@ -0,0 +1,341 @@ +id) + ->orderByDesc('id') + ->get(); + } + + /** + * ========================= + * VALIDATION RULE ENGINE + * ========================= + */ + + private function type(Ticket $ticket) + { + return $ticket->ticketType; + } + + private function ensureRequiredAssets(Ticket $ticket, array $payload = []) + { + $type = $this->type($ticket); + + // PHOTO VALIDATION + if ($type?->require_photo) { + $photoCount = $payload['photo_count'] ?? 0; + + if ($photoCount <= 0) { + throw ValidationException::withMessages([ + 'photo' => 'Foto wajib diupload untuk ticket ini' + ]); + } + } + + // MATERIAL VALIDATION + if ($type?->require_material) { + $materialCount = $payload['material_count'] ?? 0; + + if ($materialCount <= 0) { + throw ValidationException::withMessages([ + 'material' => 'Material wajib diinput untuk ticket ini' + ]); + } + } + } + + private function ensureCustomer(Ticket $ticket) + { + $type = $this->type($ticket); + + if ($type?->need_customer && empty($ticket->customer_id)) { + throw ValidationException::withMessages([ + 'customer_id' => 'Customer wajib ada untuk ticket ini' + ]); + } + } + + /** + * ========================= + * ASSIGN / REASSIGN + * ========================= + */ + + public function assign( + Ticket $ticket, + array $technicians, + int $leaderId, + int $userId + ): Ticket { + + TicketWorkflowValidator::allow($ticket, ['approved']); + + DB::transaction(function () use ( + $ticket, + $technicians, + $leaderId, + $userId + ) { + + foreach ($technicians as $technicianId) { + TicketAssignment::create([ + 'ticket_id' => $ticket->id, + 'user_id' => $technicianId, + 'is_leader' => ($technicianId == $leaderId), + 'assigned_by' => $userId, + 'assigned_at' => now(), + 'status' => 'assigned' + ]); + } + + $ticket->update([ + 'status' => 'assigned', + 'assigned_at' => now() + ]); + + $this->log($ticket->id, $userId, 'Assign', 'Teknisi ditugaskan'); + }); + + return $ticket->fresh(); + } + + public function reassign( + Ticket $ticket, + array $technicians, + int $leaderId, + int $userId + ): Ticket { + + DB::transaction(function () use ( + $ticket, + $technicians, + $leaderId, + $userId + ) { + + TicketAssignment::where('ticket_id', $ticket->id)->delete(); + + foreach ($technicians as $technicianId) { + TicketAssignment::create([ + 'ticket_id' => $ticket->id, + 'user_id' => $technicianId, + 'is_leader' => ($technicianId == $leaderId), + 'assigned_by' => $userId, + 'assigned_at' => now(), + 'status' => 'assigned' + ]); + } + + $this->log($ticket->id, $userId, 'Reassign', 'Penugasan diperbarui'); + }); + + return $ticket->fresh(); + } + + /** + * ========================= + * APPROVAL FLOW + * ========================= + */ + + public function approve( + Ticket $ticket, + ?string $notes, + int $userId + ): Ticket { + + TicketWorkflowValidator::allow($ticket, ['open']); + + DB::transaction(function () use ($ticket, $notes, $userId) { + + TicketApproval::create([ + 'ticket_id' => $ticket->id, + 'approver_id' => $userId, + 'status' => 'approved', + 'notes' => $notes, + 'approved_at' => now(), + 'created_at' => now() + ]); + + $ticket->update([ + 'status' => 'approved', + 'approved_by' => $userId, + 'approved_at' => now() + ]); + + $this->log($ticket->id, $userId, 'Approved', $notes); + }); + + return $ticket->fresh(); + } + + public function reject(Ticket $ticket, string $reason, int $userId): Ticket + { + DB::transaction(function () use ($ticket, $reason, $userId) { + + TicketApproval::create([ + 'ticket_id' => $ticket->id, + 'approver_id' => $userId, + 'status' => 'rejected', + 'notes' => $reason, + 'created_at' => now() + ]); + + $ticket->update([ + 'status' => 'rejected', + 'rejected_by' => $userId, + 'rejected_at' => now(), + 'rejection_reason' => $reason + ]); + + $this->log($ticket->id, $userId, 'Rejected', $reason); + }); + + return $ticket->fresh(); + } + + /** + * ========================= + * WORKFLOW START + * ========================= + */ + + public function start(Ticket $ticket, array $location, int $userId): Ticket + { + TicketWorkflowValidator::allow($ticket, ['assigned']); + + DB::transaction(function () use ($ticket, $location, $userId) { + + $ticket->update([ + 'status' => 'progress' + ]); + + $this->log( + $ticket->id, + $userId, + 'Start Work', + 'Pekerjaan dimulai' + ); + }); + + return $ticket->fresh(); + } + + public function pending(Ticket $ticket, string $reason, int $userId): Ticket + { + $ticket->update(['status' => 'pending']); + + $this->log($ticket->id, $userId, 'Pending', $reason); + + return $ticket->fresh(); + } + + public function resume(Ticket $ticket, int $userId): Ticket + { + $ticket->update(['status' => 'progress']); + + $this->log($ticket->id, $userId, 'Resume', 'Dilanjutkan'); + + return $ticket->fresh(); + } + + /** + * ========================= + * RESOLVE (VALIDATION POINT) + * ========================= + */ + + public function resolve( + Ticket $ticket, + string $notes, + int $userId, + array $payload = [] + ): Ticket { + + TicketWorkflowValidator::allow($ticket, ['progress']); + + // 🔥 VALIDASI MATERIAL + PHOTO + CUSTOMER + $this->ensureRequiredAssets($ticket, $payload); + $this->ensureCustomer($ticket); + + DB::transaction(function () use ($ticket, $notes, $userId) { + + $ticket->update([ + 'status' => 'resolved', + 'resolved_at' => now() + ]); + + $this->log($ticket->id, $userId, 'Resolved', $notes); + }); + + return $ticket->fresh(); + } + + public function close(Ticket $ticket, ?string $notes, int $userId): Ticket + { + TicketWorkflowValidator::allow($ticket, ['resolved']); + + $ticket->update([ + 'status' => 'closed', + 'closed_at' => now() + ]); + + $this->log($ticket->id, $userId, 'Closed', $notes); + + return $ticket->fresh(); + } + + public function cancel(Ticket $ticket, string $reason, int $userId): Ticket + { + $ticket->update(['status' => 'cancelled']); + + $this->log($ticket->id, $userId, 'Cancelled', $reason); + + return $ticket->fresh(); + } + + public function escalate(Ticket $ticket, string $reason, int $userId): Ticket + { + $this->log($ticket->id, $userId, 'Escalated', $reason); + + return $ticket->fresh(); + } + + /** + * ========================= + * LOG SYSTEM + * ========================= + */ + + private function log( + int $ticketId, + int $userId, + string $activity, + ?string $notes = null + ): void { + + TicketLog::create([ + 'ticket_id' => $ticketId, + 'user_id' => $userId, + 'activity' => $activity, + 'notes' => $notes, + 'created_at'=> now() + ]); + } +} \ No newline at end of file diff --git a/app/Services/Ticket/TicketWorkflowValidator.php b/app/Services/Ticket/TicketWorkflowValidator.php new file mode 100644 index 0000000..c461fd7 --- /dev/null +++ b/app/Services/Ticket/TicketWorkflowValidator.php @@ -0,0 +1,22 @@ +status, $allowedStatuses)) { + + throw new Exception( + "Ticket status '{$ticket->status}' tidak valid untuk proses ini" + ); + } + } +} \ No newline at end of file diff --git a/database/migrations/2026_06_19_044044_create_ticket_types_table.php b/database/migrations/2026_06_19_044044_create_ticket_types_table.php index 17d6b13..e16cc09 100644 --- a/database/migrations/2026_06_19_044044_create_ticket_types_table.php +++ b/database/migrations/2026_06_19_044044_create_ticket_types_table.php @@ -17,6 +17,11 @@ return new class extends Migration $table->string('name', 100); $table->boolean('need_approval')->default(false); $table->integer('sla_minutes')->nullable(); + + // Requirement Ticket + $table->boolean('require_photo')->default(false); + $table->boolean('require_material')->default(false); + $table->boolean('need_customer')->default(false); $table->timestamps(); }); } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d01a0ef..cb1dd0e 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -14,6 +14,9 @@ class DatabaseSeeder extends Seeder public function run(): void { // User::factory(10)->create(); + $this->call([ + TicketTypeSeeder::class, + ]); User::factory()->create([ 'name' => 'Test User', diff --git a/database/seeders/TicketTypeSeeder.php b/database/seeders/TicketTypeSeeder.php new file mode 100644 index 0000000..42c9c5c --- /dev/null +++ b/database/seeders/TicketTypeSeeder.php @@ -0,0 +1,145 @@ + 'GGN', + 'name' => 'Gangguan Internet', + 'need_approval' => false, + 'sla_minutes' => 240, + 'require_photo' => true, + 'require_material' => false, + 'need_customer' => true, + ], + + [ + 'code' => 'INS', + 'name' => 'Instalasi Baru', + 'need_approval' => true, + 'sla_minutes' => 1440, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => true, + ], + + [ + 'code' => 'ONU', + 'name' => 'Ganti ONU', + 'need_approval' => false, + 'sla_minutes' => 240, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => true, + ], + + [ + 'code' => 'CBL', + 'name' => 'Tarik Kabel', + 'need_approval' => false, + 'sla_minutes' => 480, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => true, + ], + + [ + 'code' => 'SVY', + 'name' => 'Survey Lokasi', + 'need_approval' => false, + 'sla_minutes' => 480, + 'require_photo' => true, + 'require_material' => false, + 'need_customer' => false, + ], + + [ + 'code' => 'MNT', + 'name' => 'Maintenance', + 'need_approval' => true, + 'sla_minutes' => 1440, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => false, + ], + + [ + 'code' => 'ADM', + 'name' => 'Administrasi', + 'need_approval' => false, + 'sla_minutes' => null, + 'require_photo' => false, + 'require_material' => false, + 'need_customer' => false, + ], + + [ + 'code' => 'REL', + 'name' => 'Relokasi Pelanggan', + 'need_approval' => true, + 'sla_minutes' => 1440, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => true, + ], + + [ + 'code' => 'UPG', + 'name' => 'Upgrade Paket', + 'need_approval' => true, + 'sla_minutes' => 240, + 'require_photo' => false, + 'require_material' => false, + 'need_customer' => true, + ], + + [ + 'code' => 'DWG', + 'name' => 'Downgrade Paket', + 'need_approval' => true, + 'sla_minutes' => 240, + 'require_photo' => false, + 'require_material' => false, + 'need_customer' => true, + ], + + [ + 'code' => 'BTS', + 'name' => 'Pekerjaan BTS/POP', + 'need_approval' => true, + 'sla_minutes' => 2880, + 'require_photo' => true, + 'require_material' => true, + 'need_customer' => false, + ], + + [ + 'code' => 'NOC', + 'name' => 'Pekerjaan NOC', + 'need_approval' => true, + 'sla_minutes' => 1440, + 'require_photo' => false, + 'require_material' => false, + 'need_customer' => false, + ], + + ]; + + foreach ($types as $type) { + + TicketType::updateOrCreate( + ['code' => $type['code']], + $type + ); + } + } +} diff --git a/routes/api.php b/routes/api.php index e9f173a..38bb28e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\AuthController; use App\Http\Controllers\Ticket\TicketController; use App\Http\Controllers\Ticket\TicketTypeController; +use App\Http\Controllers\Ticket\TicketWorkflowController; Route::post('/login', [AuthController::class, 'login']); @@ -13,22 +14,46 @@ Route::post('/login', [AuthController::class, 'login']); // Route::get('/', [TicketController::class, 'index']); // Route::post('/', [TicketController::class, 'store']); // }); +Route::get('/generate-token', function () { + + $user = \App\Models\User::find(1); + + return $user->createToken( + 'master-token' + )->plainTextToken; + +}); Route::middleware('auth:sanctum')->group(function () { Route::apiResource('tickets', TicketController::class); + Route::apiResource('ticket-types', TicketTypeController::class); + Route::prefix('tickets/{ticket}') + ->controller(TicketWorkflowController::class) + ->group(function () { + + Route::get('/workflow', 'history'); + + Route::post('/assign', 'assign'); + Route::post('/reassign', 'reassign'); + + Route::post('/approve', 'approve'); + Route::post('/reject', 'reject'); + + Route::post('/start', 'start'); + + Route::post('/pending', 'pending'); + Route::post('/resume', 'resume'); + + Route::post('/resolve', 'resolve'); + + Route::post('/close', 'close'); + + Route::post('/cancel', 'cancel'); + + Route::post('/escalate', 'escalate'); + }); }); - - -// Route::get('/generate-token', function () { - -// $user = \App\Models\User::find(1); - -// return $user->createToken( -// 'master-token' -// )->plainTextToken; - -// });