diff --git a/app/Http/Controllers/Api/ChatbotController.php b/app/Http/Controllers/Api/ChatbotController.php index d0ea4ba..24637cc 100644 --- a/app/Http/Controllers/Api/ChatbotController.php +++ b/app/Http/Controllers/Api/ChatbotController.php @@ -76,9 +76,29 @@ class ChatbotController extends Controller return response()->json(['response' => ''], 500); } + public function mainGenerateText(Request $request) + { + // Log hanya data yang relevan + info("Received prompt: " . $request->input('prompt')); - private function classifyContent(string $prompt) { - $classifyResponse = $this->openAIService->generateClassifyContent($prompt); - return $classifyResponse; + // Validasi input + $request->validate([ + 'prompt' => 'required|string', + ]); + + try { + // Panggil service untuk generate text + $response = $this->openAIService->mainGenerateText($request->input('prompt')); + + return response()->json(['response' => $response]); + } catch (\Exception $e) { + // Tangani error dan log exception + \Log::error("Error generating text: " . $e->getMessage()); + + return response()->json([ + 'error' => '' + ], 500); + } } + } \ No newline at end of file diff --git a/app/Http/Controllers/ChatbotPimpinan/ChatbotPimpinanController.php b/app/Http/Controllers/ChatbotPimpinan/ChatbotPimpinanController.php new file mode 100644 index 0000000..9590ece --- /dev/null +++ b/app/Http/Controllers/ChatbotPimpinan/ChatbotPimpinanController.php @@ -0,0 +1,17 @@ +client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are an expert assistant with deep knowledge in multiple fields, including programming, data science, and business strategy. Provide clear, concise, and accurate responses." + ], + [ + 'role' => 'user', + 'content' => $prompt + ], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + // 1. Buat fungsi untuk akses data advertisements + // 2. Buat fungsi untuk akses data business_or_industries + // 3. Buat fungsi untuk akses data customers + // 4. Buat fungsi untuk akses data pbg_task + // 5. Buat fungsi untuk akses data pbg_task_retribution + // 6. Buat fungsi untuk akses data spatial_plannings + // 7. Buat fungsi untuk akses data tourisms + // 8. Buat fungsi untuk akses data umkms } diff --git a/public/templates/table_config.json b/public/templates/table_config.json new file mode 100644 index 0000000..b8e65f0 --- /dev/null +++ b/public/templates/table_config.json @@ -0,0 +1,145 @@ +{ + "v_advertisements": [ + "no", + "business_name", + "npwpd", + "advertisement_type", + "advertisement_content", + "business_address", + "advertisement_location", + "village_name", + "district_name", + "length", + "width", + "viewing_angle", + "face", + "area", + "angle", + "contact" + ], + "business_or_industries": [ + "nama_kecamatan", + "nama_kelurahan", + "nop", + "nama_wajib_pajak", + "alamat_wajib_pajak", + "alamat_objek_pajak", + "luas_bumi", + "luas_bangunan", + "njop_bumi", + "njop_bangunan", + "ketetapan", + "tahun_pajak", + "created_at", + "updated_at" + ], + "customers": [ + "nomor_pelanggan", + "kota_pelayanan", + "nama", + "alamat", + "latitude", + "longitude", + "created_at", + "updated_at" + ], + "pbg_task": [ + "uuid", + "name", + "owner_name", + "application_type", + "application_type_name", + "condition", + "registration_number", + "document_number", + "address", + "status_name", + "slf_status_name", + "function_type", + "consultation_type", + "due_date", + "land_certificate_phase", + "created_at", + "updated_at", + "task_created_at" + ], + "v_pbg_task_retributions": [ + "uuid", + "name", + "owner_name", + "application_type", + "application_type_name", + "condition", + "registration_number", + "document_number", + "address", + "status_name", + "slf_status_name", + "consultation_type", + "due_date", + "land_certificate_phase", + "created_at", + "updated_at", + "task_created_at", + "nilai_retribusi_bangunan" + ], + "spatial_plannings": [ + "created_at", + "updated_at", + "name", + "kbli", + "activities", + "area", + "location", + "number", + "date" + ], + "v_tourisms": [ + "project_id", + "project_type_id", + "nib", + "business_name", + "oss_publication_date", + "investment_status_description", + "business_form", + "project_risk", + "project_name", + "business_scale", + "business_address", + "village_name", + "district_name", + "longitude", + "latitude", + "project_submission_date", + "kbli_title", + "supervisory_sector", + "user_name", + "email", + "contact", + "land_area_in_m2", + "investment_amount", + "tki" + ], + "v_umkms": [ + "business_address", + "business_contact", + "business_desc", + "business_form", + "business_id_number", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] +} \ No newline at end of file diff --git a/resources/js/chatbot-pimpinan/index.js b/resources/js/chatbot-pimpinan/index.js new file mode 100644 index 0000000..c6b311c --- /dev/null +++ b/resources/js/chatbot-pimpinan/index.js @@ -0,0 +1,111 @@ +import GlobalConfig from "../global-config.js"; + +document.addEventListener("DOMContentLoaded", function () { + const textarea = document.getElementById("user-message"); + const sendButton = document.getElementById("send"); + const conversationArea = document.querySelector(".row.flex-grow"); + + // Fungsi untuk mengirim pesan + async function sendMessage() { + const userText = textarea.value.trim(); + if (userText !== "") { + // Kosongkan textarea setelah mengirim + textarea.value = ""; + + // Tambahkan pesan user ke UI + addMessage(userText, "user"); + + // Tambahkan pesan bot sementara dengan "Loading..." + const botMessageElement = addMessage('
', "bot"); + + // Panggil API untuk mendapatkan response dari bot + const botResponse = await getBotResponse(userText); + + // Perbarui pesan bot dengan respons yang sebenarnya + botMessageElement.innerHTML = botResponse;; + } + } + + // Event listener untuk klik tombol + sendButton.addEventListener("click", sendMessage); + + // Event listener untuk menekan Enter di textarea + textarea.addEventListener("keydown", function (event) { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); // Mencegah newline di textarea + sendMessage(); // Panggil fungsi kirim pesan + } + }); + + function addMessage(text, sender) { + const messageRow = document.createElement("div"); + messageRow.classList.add("row", "flex-grow", "overflow-auto"); + + const messageCol = document.createElement("div"); + messageCol.classList.add("w-auto", "d-inline-block"); // Menyesuaikan lebar konten + + if (sender === "user") { + messageCol.classList.add("ms-auto", "max-w-50"); // Rata kanan, max 50% (setara col-6) + } else { + messageCol.classList.add("max-w-75"); // Max 75% (setara col-9) + + // Tambahkan avatar hanya untuk bot + const avatarSpan = document.createElement("span"); + avatarSpan.classList.add("d-flex", "align-items-center", "mb-1"); + + const avatarImg = document.createElement("img"); + avatarImg.classList.add("rounded-circle"); + avatarImg.width = 32; + avatarImg.src = "/images/iconchatbot.jpeg"; + avatarImg.alt = "bot-avatar"; + + avatarSpan.appendChild(avatarImg); + messageCol.appendChild(avatarSpan); + } + + const messageDiv = document.createElement("div"); + messageDiv.classList.add("p-2", "rounded", "mb-2"); + + if (sender === "user") { + messageDiv.classList.add("user-response", "bg-primary", "text-white"); + } else { + messageDiv.classList.add("bot-response", "bg-light"); + } + + // Menyisipkan konten HTML langsung (bisa berupa teks atau loader) + messageDiv.innerHTML = text; + messageCol.appendChild(messageDiv); + messageRow.appendChild(messageCol); + + // Tambahkan ke area percakapan + conversationArea.appendChild(messageRow); + + // Scroll otomatis ke bawah + conversationArea.scrollTop = conversationArea.scrollHeight; + + return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti + } + + // Fungsi untuk memanggil API + async function getBotResponse(userText) { + try { + const url = `${GlobalConfig.apiHost}/api/main-generate-text`; + const response = await fetch(url, { + method: "POST", + body: JSON.stringify({prompt: userText }), + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + return data.response || "Maaf, saya tidak mengerti."; + } catch (error) { + console.error("Error fetching bot response:", error); + return "Terjadi kesalahan, coba lagi nanti."; + } + } +}); diff --git a/resources/js/chatbot/index.js b/resources/js/chatbot/index.js index 6a432e8..01076cf 100644 --- a/resources/js/chatbot/index.js +++ b/resources/js/chatbot/index.js @@ -1,6 +1,32 @@ import GlobalConfig from "../global-config.js"; document.addEventListener("DOMContentLoaded", function () { + const tabs = document.querySelectorAll(".nav-link"); + const timeElements = document.querySelectorAll(".sending-message-time p"); + + timeElements.forEach((element) => { + element.textContent = getCurrentTime(); + }); + + function activateTab(tab) { + tabs.forEach(btn => { + btn.classList.remove("border-3", "bg-primary", "text-white"); // Reset semua tab + }); + tab.classList.add("border-3", "bg-primary", "text-white"); // Tambahkan warna pada tab aktif + } + + tabs.forEach(tab => { + tab.addEventListener("click", function () { + activateTab(this); + }); + }); + + // Set warna awal untuk tab aktif (jika ada) + const initialActiveTab = document.querySelector(".nav-link.active"); + if (initialActiveTab) { + activateTab(initialActiveTab); + } + document.querySelectorAll(".nav-link").forEach(tab => { tab.addEventListener("click", function () { setTimeout(() => { @@ -28,13 +54,20 @@ document.addEventListener("DOMContentLoaded", function () { addMessage(userText, "user"); // Tambahkan pesan bot sementara dengan "Loading..." - const botMessageElement = addMessage('
', "bot"); + const botMessageElement = addMessage('
...
', "bot"); - // Panggil API untuk mendapatkan response dari bot + const messageTextContainer = botMessageElement.querySelector(".bot-message-text"); + if (messageTextContainer) { + messageTextContainer.innerHTML = '
'; + } + + // Panggil API untuk mendapatkan respons dari bot const botResponse = await getBotResponse(currentTab, userText); // Perbarui pesan bot dengan respons yang sebenarnya - botMessageElement.innerHTML = botResponse;; + if (messageTextContainer) { + messageTextContainer.innerHTML = botResponse; + } } } @@ -49,102 +82,136 @@ document.addEventListener("DOMContentLoaded", function () { } }); - function addMessage(text, sender) { - const messageRow = document.createElement("div"); - messageRow.classList.add("row", "flex-grow", "overflow-auto"); - - const messageCol = document.createElement("div"); - messageCol.classList.add("w-auto", "d-inline-block"); // Menyesuaikan lebar konten - - if (sender === "user") { - messageCol.classList.add("ms-auto", "max-w-50"); // Rata kanan, max 50% (setara col-6) - } else { - messageCol.classList.add("max-w-75"); // Max 75% (setara col-9) - - // Tambahkan avatar hanya untuk bot - const avatarSpan = document.createElement("span"); - avatarSpan.classList.add("d-flex", "align-items-center", "mb-1"); - - const avatarImg = document.createElement("img"); - avatarImg.classList.add("rounded-circle"); - avatarImg.width = 32; - avatarImg.src = "/images/iconchatbot.jpeg"; - avatarImg.alt = "bot-avatar"; - - avatarSpan.appendChild(avatarImg); - messageCol.appendChild(avatarSpan); - } - - const messageDiv = document.createElement("div"); - messageDiv.classList.add("p-2", "rounded", "mb-2"); - - if (sender === "user") { - messageDiv.classList.add("user-response", "bg-primary", "text-white"); - } else { - messageDiv.classList.add("bot-response", "bg-light"); - } - - // Menyisipkan konten HTML langsung (bisa berupa teks atau loader) - messageDiv.innerHTML = text; - messageCol.appendChild(messageDiv); - messageRow.appendChild(messageCol); - - // Tambahkan ke area percakapan - conversationArea.appendChild(messageRow); - - // Scroll otomatis ke bawah - conversationArea.scrollTop = conversationArea.scrollHeight; - - return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti + function getCurrentTime() { + const now = new Date(); + return now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0"); } - // function addMessage(text, sender) { // const messageRow = document.createElement("div"); - // messageRow.classList.add("row", "flex-grow", "overflow-auto"); + // // Atur posisi berdasarkan sender (user -> end, bot -> start) + // messageRow.classList.add("row", "flex-grow", "overflow-auto", sender === "user" ? "justify-content-end" : "justify-content-start"); // const messageCol = document.createElement("div"); // messageCol.classList.add("col-9", "w-auto"); + + // // Atur lebar maksimum berdasarkan sender + // messageCol.style.maxWidth = sender === "user" ? "50%" : "75%"; + // const messageContainer = document.createElement("div"); + // messageContainer.classList.add("p-2", "rounded", "mb-2", "d-flex", "align-items-center"); + // if (sender === "user") { - // messageCol.classList.add("ms-auto"); // Geser ke kanan untuk user + // messageContainer.classList.add("user-response", "bg-primary", "text-white", "ms-auto"); + // } else { + // messageContainer.classList.add("bot-response", "bg-light"); // } - // const messageDiv = document.createElement("div"); - // messageDiv.classList.add("p-2", "rounded", "mb-2"); - - // if (sender === "user") { - // messageDiv.classList.add("user-response", "bg-primary", "text-white"); - // } else { - // messageDiv.classList.add("bot-response", "bg-light"); - - // // Tambahkan avatar hanya untuk bot + // if (sender !== "user") { // const avatarSpan = document.createElement("span"); - // avatarSpan.classList.add("d-flex", "align-items-center", "mb-1"); - + // avatarSpan.classList.add("d-flex", "align-self-start", "mb-1"); + // const avatarImg = document.createElement("img"); // avatarImg.classList.add("rounded-circle"); - // avatarImg.width = 32; + // avatarImg.width = 45; // avatarImg.src = "/images/iconchatbot.jpeg"; // avatarImg.alt = "bot-avatar"; - + // avatarSpan.appendChild(avatarImg); - // messageCol.appendChild(avatarSpan); + // messageContainer.appendChild(avatarSpan); // } - - // // Menyisipkan konten HTML langsung (bisa berupa teks atau loader) - // messageDiv.innerHTML = text; - // messageCol.appendChild(messageDiv); + + // const messageContentWrapper = document.createElement("div"); + // messageContentWrapper.classList.add("d-flex", "flex-column", "ms-2"); + + // const messageContent = document.createElement("div"); + // messageContent.classList.add("row"); + // messageContent.innerHTML = `

${sender === "user" ? "You" : "Neng Bedas"}

${text}
`; + + // const messageTime = document.createElement("div"); + // messageTime.classList.add("sending-message-time"); + // messageTime.innerHTML = `

${getCurrentTime()}

`; + + + // messageContentWrapper.appendChild(messageContent); + // messageContentWrapper.appendChild(messageTime); + // messageContainer.appendChild(messageContentWrapper); + // messageCol.appendChild(messageContainer); // messageRow.appendChild(messageCol); - - // // Tambahkan ke area percakapan + // conversationArea.appendChild(messageRow); - - // // Scroll otomatis ke bawah // conversationArea.scrollTop = conversationArea.scrollHeight; - - // return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti + + // return messageContainer; // } + + function addMessage(text, sender) { + const messageRow = document.createElement("div"); + // Atur posisi berdasarkan sender (user -> end, bot -> start) + messageRow.classList.add("row", "flex-grow", "overflow-auto", sender === "user" ? "justify-content-end" : "justify-content-start"); + + const messageCol = document.createElement("div"); + messageCol.classList.add("col-9", "w-auto"); + + // Atur lebar maksimum berdasarkan sender + messageCol.style.maxWidth = sender === "user" ? "50%" : "75%"; + + // Container untuk menyimpan nama dan bubble chat + const messageWrapper = document.createElement("div"); + messageWrapper.classList.add("d-flex", "flex-column"); + + // Tambahkan Nama di luar bubble chat + const messageName = document.createElement("p"); + messageName.classList.add("fw-bolder", sender === "user" ? "text-end" : "text-start", "mb-1"); + messageName.textContent = sender === "user" ? "You" : "Neng Bedas"; + + // Bubble Chat + const messageContainer = document.createElement("div"); + messageContainer.classList.add("p-2", "rounded", "mb-2", "d-inline-block"); + if (sender === "user") { + messageContainer.classList.add("user-response", "bg-primary", "text-white"); + } else { + messageContainer.classList.add("bot-response", "bg-light"); + } + + const messageContent = document.createElement("div"); + messageContent.classList.add("bot-message-text", "mb-0", "text-start"); + messageContent.textContent = text; + + // Waktu di dalam bubble chat + const messageTime = document.createElement("div"); + messageTime.classList.add("sending-message-time", "text-end", "mt-1"); + messageTime.innerHTML = `

${getCurrentTime()}

`; + + messageContainer.appendChild(messageContent); + messageContainer.appendChild(messageTime); + + // Jika pengirim adalah bot, tambahkan avatar + if (sender !== "user") { + const avatarContainer = document.createElement("div"); + avatarContainer.classList.add("col-auto", "pe-0"); + + const avatarImg = document.createElement("img"); + avatarImg.classList.add("rounded-circle"); + avatarImg.width = 45; + avatarImg.src = "/images/iconchatbot.jpeg"; + avatarImg.alt = "bot-avatar"; + + avatarContainer.appendChild(avatarImg); + messageRow.appendChild(avatarContainer); + } + + // Masukkan nama dan bubble ke dalam wrapper + messageWrapper.appendChild(messageName); + messageWrapper.appendChild(messageContainer); + messageCol.appendChild(messageWrapper); + messageRow.appendChild(messageCol); + + conversationArea.appendChild(messageRow); + conversationArea.scrollTop = conversationArea.scrollHeight; + + return messageContainer; + } // Fungsi untuk memanggil API async function getBotResponse(tab_active, userText) { diff --git a/resources/views/chatbot-pimpinan/index.blade.php b/resources/views/chatbot-pimpinan/index.blade.php new file mode 100644 index 0000000..7ee646f --- /dev/null +++ b/resources/views/chatbot-pimpinan/index.blade.php @@ -0,0 +1,69 @@ +@extends('layouts.vertical', ['subtitle' => 'Main Chatbot']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) + +@endsection + +@section('content') +@include('layouts.partials/page-title', ['title' => 'Main Chatbot', 'subtitle' => 'Main Chatbot']) + +
+
+ + + +
+
+ + avatar-3 + +
+

Halo! Ada yang bisa saya bantu?

+
+
+
+ + +
+
+ + +
+ {{--
+ +
--}} +
+
+ +
+ + +@endsection + +@section('scripts') +@vite(['resources/js/chatbot-pimpinan/index.js']) +@endsection \ No newline at end of file diff --git a/resources/views/chatbot/index.blade.php b/resources/views/chatbot/index.blade.php index 20a897c..5c9a4a5 100644 --- a/resources/views/chatbot/index.blade.php +++ b/resources/views/chatbot/index.blade.php @@ -30,58 +30,85 @@ @include('layouts.partials/page-title', ['title' => 'Chatbot', 'subtitle' => 'Chatbot'])
-
- -
+ + {{--
+
--}}
- - + + {{--
- - avatar-3 - -
-

Halo! Ada yang bisa saya bantu?

+
+ + avatar-3 + +
+
+

Neng Bedas

+

Halo! Ada yang bisa saya bantu?

+
+
+

Now

+
+
+
+
+
--}} + + +
+ +
+ avatar-3 +
+ + +
+ +

Neng Bedas

+ + +
+

Halo! Ada yang bisa saya bantu?

+ + +
+

Now

+
+
-
- {{--
- -
--}}
-
-
diff --git a/resources/views/layouts/vertical.blade.php b/resources/views/layouts/vertical.blade.php index 3328b33..ad57d41 100755 --- a/resources/views/layouts/vertical.blade.php +++ b/resources/views/layouts/vertical.blade.php @@ -61,7 +61,7 @@ {{--
--}} - @if (!Request::is('chatbot')) + @if (!Request::is('chatbot') && !Request::is('main-chatbot')) diff --git a/routes/api.php b/routes/api.php index ebed7b2..fb42143 100644 --- a/routes/api.php +++ b/routes/api.php @@ -26,6 +26,7 @@ use Illuminate\Support\Facades\Route; Route::post('/login', [UsersController::class, 'login'])->name('api.user.login'); Route::post('/generate-text', [ChatbotController::class, 'generateText']); +Route::post('/main-generate-text', [ChatbotController::class, 'mainGenerateText']); Route::group(['middleware' => 'auth:sanctum'], function (){ // users Route::controller(UsersController::class)->group(function(){ diff --git a/routes/web.php b/routes/web.php index 911b8ed..af26549 100755 --- a/routes/web.php +++ b/routes/web.php @@ -21,6 +21,7 @@ use App\Http\Controllers\Data\SpatialPlanningController; use App\Http\Controllers\Report\ReportTourismController; use App\Http\Controllers\SpatialPlanningsController; use App\Http\Controllers\Chatbot\ChatbotController; +use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController; use Illuminate\Support\Facades\Route; require __DIR__ . '/auth.php'; @@ -63,6 +64,9 @@ Route::group(['middleware' => 'auth'], function(){ // chatbot Route::resource('/chatbot', ChatbotController::class); + // chatbot - pimpinan + Route::resource('/main-chatbot', ChatbotPimpinanController::class); + // roles Route::resource('/roles', RolesController::class); Route::group(['prefix' => '/roles'], function (){