withTrashed(); if ($request->filled('category_id')) { $query->where('category_id', $request->category_id); } if ($request->filled('item_type')) { $query->where('item_type', $request->item_type); } if ($request->filled('search')) { $query->where(function ($q) use ($request) { $q->where('title', 'like', '%' . $request->search . '%') ->orWhere('content', 'like', '%' . $request->search . '%'); }); } $items = $query->latest()->paginate(20)->withQueryString(); $categories = Category::active()->ordered()->get(); $typeLabels = KnowledgeItem::typeLabels(); return view('admin.knowledge-items.index', compact('items', 'categories', 'typeLabels')); } public function create(Request $request): View { $categories = Category::active()->ordered()->get(); $typeLabels = KnowledgeItem::typeLabels(); $prefillData = $request->only(['category_id', 'item_type', 'title', 'content']); // prefillData berguna bila convert dari feedback return view('admin.knowledge-items.create', compact('categories', 'typeLabels', 'prefillData')); } public function store(StoreKnowledgeItemRequest $request): RedirectResponse { $data = $request->validated(); $data['created_by'] = auth()->id(); $data['updated_by'] = auth()->id(); $item = KnowledgeItem::create($data); $this->auditService->knowledgeItemCreated($item); // Embed secara async ReindexKnowledgeItemJob::dispatch($item->id); return redirect() ->route('admin.knowledge-items.show', $item) ->with('success', "Knowledge item '{$item->title}' berjaya dicipta dan sedang di-embed."); } public function show(KnowledgeItem $knowledgeItem): View { $knowledgeItem->load('category', 'creator'); return view('admin.knowledge-items.show', compact('knowledgeItem')); } public function edit(KnowledgeItem $knowledgeItem): View { $categories = Category::active()->ordered()->get(); $typeLabels = KnowledgeItem::typeLabels(); return view('admin.knowledge-items.edit', compact('knowledgeItem', 'categories', 'typeLabels')); } public function update(StoreKnowledgeItemRequest $request, KnowledgeItem $knowledgeItem): RedirectResponse { $oldValues = $knowledgeItem->only(['title', 'content', 'is_active']); $data = $request->validated(); $data['updated_by'] = auth()->id(); $knowledgeItem->update($data); $this->auditService->knowledgeItemUpdated($knowledgeItem, $oldValues); // Re-embed kerana kandungan mungkin berubah ReindexKnowledgeItemJob::dispatch($knowledgeItem->id); return redirect() ->route('admin.knowledge-items.show', $knowledgeItem) ->with('success', 'Knowledge item berjaya dikemaskini dan sedang di-embed semula.'); } public function toggleStatus(KnowledgeItem $knowledgeItem): RedirectResponse { $newStatus = !$knowledgeItem->is_active; $knowledgeItem->update(['is_active' => $newStatus]); if (!$newStatus && $knowledgeItem->qdrant_point_id) { $this->ingestionService->deactivateKnowledgeItemInQdrant($knowledgeItem); $this->auditService->knowledgeItemDeactivated($knowledgeItem); } elseif ($newStatus) { // Reactivate — update payload Qdrant ReindexKnowledgeItemJob::dispatch($knowledgeItem->id); } $status = $newStatus ? 'diaktifkan' : 'dinyahaktifkan'; return back()->with('success', "Item '{$knowledgeItem->title}' telah {$status}."); } public function reindex(KnowledgeItem $knowledgeItem): RedirectResponse { ReindexKnowledgeItemJob::dispatch($knowledgeItem->id); return back()->with('success', 'Re-embed telah dimulakan.'); } public function destroy(KnowledgeItem $knowledgeItem): RedirectResponse { // Deactivate dalam Qdrant dulu if ($knowledgeItem->qdrant_point_id) { $this->ingestionService->deactivateKnowledgeItemInQdrant($knowledgeItem); } $knowledgeItem->delete(); // SoftDelete return redirect() ->route('admin.knowledge-items.index') ->with('success', 'Knowledge item telah dipadam.'); } }