ticker work proses

This commit is contained in:
Wian Drs
2026-06-22 13:34:49 +07:00
parent 844b8a0ff0
commit b52e67c428
22 changed files with 1193 additions and 16 deletions
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Enums;
enum TicketStatus:string
{
case DRAFT = 'draft';
case OPEN = 'open';
case APPROVED = 'approved';
case ASSIGNED = 'assigned';
case PROGRESS = 'progress';
case PENDING = 'pending';
case RESOLVED = 'resolved';
case CLOSED = 'closed';
case CANCELLED = 'cancelled';
case REJECTED = 'rejected';
}
@@ -0,0 +1,247 @@
<?php
namespace App\Http\Controllers\Ticket;
use App\Http\Controllers\ApiController;
use App\Http\Resources\Ticket\TicketResource;
use App\Models\Ticket;
use App\Services\Ticket\TicketWorkflowService;
use App\Http\Requests\Ticket\AssignTicketRequest;
use App\Http\Requests\Ticket\ReassignTicketRequest;
use App\Http\Requests\Ticket\ApproveTicketRequest;
use App\Http\Requests\Ticket\RejectTicketRequest;
use App\Http\Requests\Ticket\PendingTicketRequest;
use App\Http\Requests\Ticket\ResolveTicketRequest;
use App\Http\Requests\Ticket\CloseTicketRequest;
use App\Http\Requests\Ticket\CancelTicketRequest;
use App\Http\Requests\Ticket\EscalateTicketRequest;
use App\Http\Requests\Ticket\StartTicketRequest;
class TicketWorkflowController extends ApiController
{
public function __construct(
protected TicketWorkflowService $service
) {}
/**
* History Workflow
*/
public function history(Ticket $ticket)
{
$data = $this->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'
);
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class ApproveTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'notes' => 'nullable|string|max:1000'
];
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class AssignTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'technicians' => 'required|array|min:1',
'leader_id' => 'required|integer',
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class CancelTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'reason' => 'required|string|max:2000'
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class CloseTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'notes' => 'nullable|string|max:2000'
];
}
}
@@ -0,0 +1,37 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class EscalateTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|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'
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class PendingTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'reason' => 'required|string|max:1000'
];
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class ReassignTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|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'
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class RejectTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'reason' => 'required|string|max:1000'
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class ResolveTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'notes' => 'required|string|max:2000'
];
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Ticket;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class StartTicketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'latitude' => 'nullable|numeric',
'longitude' => 'nullable|numeric',
'notes' => 'nullable|string|max:1000'
];
}
}
+22
View File
@@ -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'
);
}
}
+10 -1
View File
@@ -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'
];
}
+10 -1
View File
@@ -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'
];
}
+23 -3
View File
@@ -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'
);
}
}
@@ -0,0 +1,341 @@
<?php
namespace App\Services\Ticket;
use App\Models\Ticket;
use App\Models\TicketLog;
use App\Models\TicketApproval;
use App\Models\TicketAssignment;
use App\Services\Ticket\TicketWorkflowValidator;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class TicketWorkflowService
{
/**
* Workflow History
*/
public function history(Ticket $ticket)
{
return TicketLog::where('ticket_id', $ticket->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()
]);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Services\Ticket;
use App\Models\Ticket;
use Exception;
class TicketWorkflowValidator
{
public static function allow(
Ticket $ticket,
array $allowedStatuses
): void {
if (! in_array($ticket->status, $allowedStatuses)) {
throw new Exception(
"Ticket status '{$ticket->status}' tidak valid untuk proses ini"
);
}
}
}
@@ -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();
});
}
+3
View File
@@ -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',
+145
View File
@@ -0,0 +1,145 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\TicketType;
class TicketTypeSeeder extends Seeder
{
public function run(): void
{
$types = [
[
'code' => '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
);
}
}
}
+36 -11
View File
@@ -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;
// });