diff --git a/app/Http/Controllers/Api/ChatbotController.php b/app/Http/Controllers/Api/ChatbotController.php new file mode 100644 index 0000000..d0ea4ba --- /dev/null +++ b/app/Http/Controllers/Api/ChatbotController.php @@ -0,0 +1,84 @@ +openAIService = $openAIService; + } + + public function generateText(Request $request) + { + info($request); + $request->validate([ + 'tab_active' => 'required|string', + 'prompt' => 'required|string', + ]); + + $tab_active = $request->input('tab_active'); + $main_content = match ($tab_active) { + "count-retribusi" => "RETRIBUTION", + "document-validation" => "DOCUMENT VALIDATION", + "data-information" => "DATA SUMMARY", + default => "UNKNOWN", + }; + + if ($main_content === "UNKNOWN") { + return response()->json(['response' => 'Invalid tab_active value.'], 400); + } + + info($main_content); + + // Klasifikasi apakah pertanyaan butuh database atau bisa dijawab langsung + $classifyResponse = $this->openAIService->generateClassifyMainContent($request->input('prompt'), $main_content); + + if ($classifyResponse === "DATABASE") { + $queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content); + if (is_array($queryResponse)) { + info('Query Response is an array: ', $queryResponse); + } else { + info('Query Response is a string: ' . $queryResponse); + } + + // Validasi query dua kali sebelum eksekusi + if ( + $this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" && + $this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" + ) { + info($queryResponse); + $queryResponse = str_replace(['```sql', '```'], '', $queryResponse); + $resultQuery = DB::select($queryResponse); + $formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT); + $nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery); + $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); + return response()->json(['response' => $finalGeneratedText]); + } + + return response()->json(['response' => ''], 400); + } + + if ($classifyResponse === "GENERAL") { + $nlpResult = $this->openAIService->generateGeneralText($request->input('prompt'), $main_content); + $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); + return response()->json(['response' => $finalGeneratedText]); + } + + return response()->json(['response' => ''], 500); + } + + + private function classifyContent(string $prompt) { + $classifyResponse = $this->openAIService->generateClassifyContent($prompt); + return $classifyResponse; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chatbot/ChatbotController.php b/app/Http/Controllers/Chatbot/ChatbotController.php new file mode 100644 index 0000000..dcbfc6a --- /dev/null +++ b/app/Http/Controllers/Chatbot/ChatbotController.php @@ -0,0 +1,17 @@ +client = OpenAI::client(env('OPENAI_API_KEY')); + } + + public function generateGeneralText($prompt, $mainContent) + { + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are an expert assistant. Your task is to generate a concise response based on the provided prompt and main content. + + Guidelines: + - Summarize the key points in exactly 5 bullet points. + - Ensure the response is clear and relevant to the prompt. + - Use simple and professional language." + ], + ['role' => 'user', 'content' => "Prompt: $prompt \nMain Content: $mainContent"], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + public function generateClassifyMainContent($prompt, $mainContent) + { + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are an expert assistant in classifying questions based on whether their answers must be retrieved from a database or can be explained generally. + Your task is to return one of the following two labels: + - \"DATABASE\" → If the question requires specific data that can only be obtained from a database. + - \"GENERAL\" → If the question can be answered without accessing a database. + + Consider the following context: \"$mainContent\" + + Respond with only one of the labels: \"DATABASE\" or \"GENERAL\"." + ], + ['role' => 'user', 'content' => $prompt], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + public function generateQueryBasedMainContent($prompt, $mainContent) + { + // Load file JSON + $jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path + $jsonData = json_decode(file_get_contents($jsonPath), true); + + // Periksa apakah kategori ada dalam JSON + if (!isset($jsonData[$mainContent])) { + return "Template prompt tidak ditemukan."; + } + + // Ambil template berdasarkan kategori + $promptTemplate = $jsonData[$mainContent]['prompt']; + + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + ['role' => 'system', 'content' => $promptTemplate], + ['role' => 'user', 'content' => $prompt], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + + public function validateSyntaxQuery($queryResponse) + { + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are a MariaDB SQL expert. Your task is to validate the syntax of an SQL query to ensure it follows proper MariaDB syntax rules. + + Guidelines: + - Check for any syntax errors, missing keywords, or incorrect clause usage. + - Ensure the query is well-structured and adheres to best practices. + - Verify that all SQL keywords are used correctly and in the right order. + - If the query is valid, respond with: \"VALID\". + - If the query has issues, respond with: \"INVALID\". + + Always respond with either \"VALID\" or \"INVALID\"." + ], + ['role' => 'user', 'content' => $queryResponse], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + public function generateNLPFromQuery($inputUser, $resultQuery) { + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are an expert assistant. Your task is to analyze the database query results and transform them into a human-readable answer based on the user's question. + + Guidelines: + - Understand the user's question and extract the key intent. + - Summarize or format the query results to directly answer the user's question. + - Ensure the response is clear, concise, and relevant. + - If the query result is empty or does not match the question, provide a polite response indicating that no data is available. + + Always provide a well-structured response that makes sense based on the input question." + ], + ['role' => 'user', 'content' => "User's question: $inputUser \nDatabase result: $resultQuery"], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + + public function generateFinalText($nlpResult) { + $response = $this->client->chat()->create([ + 'model' => 'gpt-4o-mini', + 'messages' => [ + [ + 'role' => 'system', + 'content' => "You are an expert text formatter. Your task is to take the given NLP result and format it into a structured, human-readable text suitable for rendering inside an HTML
. + + Guidelines: + - Preserve the meaning and clarity of the content. + - Use proper line breaks for readability. + - If the text contains lists, convert them into bullet points. + - Emphasize important keywords using tags if necessary. + - Ensure the response remains clean and concise without extra explanations." + ], + ['role' => 'user', 'content' => "Here is the NLP result that needs formatting:\n\n$nlpResult"], + ], + ]); + + return trim($response['choices'][0]['message']['content'] ?? 'No response'); + } + +} diff --git a/composer.json b/composer.json index 755703d..8dc9ef8 100755 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "laravel/framework": "^11.31", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", - "maatwebsite/excel": "^3.1" + "maatwebsite/excel": "^3.1", + "openai-php/client": "^0.10.3" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index c784c22..1c20bd1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "52617d098d62b15c6ce8538cc8aea775", + "content-hash": "41bb51871a746904ab745e4095db8b46", "packages": [ { "name": "brick/math", @@ -3296,6 +3296,97 @@ ], "time": "2024-11-21T10:39:51+00:00" }, + { + "name": "openai-php/client", + "version": "v0.10.3", + "source": { + "type": "git", + "url": "https://github.com/openai-php/client.git", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openai-php/client/zipball/4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "php-http/discovery": "^1.20.0", + "php-http/multipart-stream-builder": "^1.4.2", + "psr/http-client": "^1.0.3", + "psr/http-client-implementation": "^1.0.1", + "psr/http-factory-implementation": "*", + "psr/http-message": "^1.1.0|^2.0.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.9.2", + "guzzlehttp/psr7": "^2.7.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "nunomaduro/collision": "^7.11.0|^8.5.0", + "pestphp/pest": "^2.36.0|^3.5.0", + "pestphp/pest-plugin-arch": "^2.7|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", + "phpstan/phpstan": "^1.12.7", + "symfony/var-dumper": "^6.4.11|^7.1.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/OpenAI.php" + ], + "psr-4": { + "OpenAI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri" + } + ], + "description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API", + "keywords": [ + "GPT-3", + "api", + "client", + "codex", + "dall-e", + "language", + "natural", + "openai", + "php", + "processing", + "sdk" + ], + "support": { + "issues": "https://github.com/openai-php/client/issues", + "source": "https://github.com/openai-php/client/tree/v0.10.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-11-12T20:51:16+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v3.0.0", @@ -3413,6 +3504,141 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2" + }, + "time": "2024-09-04T13:22:54+00:00" + }, { "name": "phpoffice/phpspreadsheet", "version": "1.29.10", @@ -7155,74 +7381,6 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "ibex/crud-generator", - "version": "v2.1.2", - "source": { - "type": "git", - "url": "https://github.com/awais-vteams/laravel-crud-generator.git", - "reference": "3906f4a702c91bbe3a84d940c3021d1511834320" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/awais-vteams/laravel-crud-generator/zipball/3906f4a702c91bbe3a84d940c3021d1511834320", - "reference": "3906f4a702c91bbe3a84d940c3021d1511834320", - "shasum": "" - }, - "require": { - "laravel/framework": "^10.30|^11.0", - "php": "^8.2" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Ibex\\CrudGenerator\\CrudServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Ibex\\CrudGenerator\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "M Awais", - "email": "asargodha@gmail.com" - } - ], - "description": "Laravel CRUD Generator", - "keywords": [ - "alpine js", - "bootstrap css", - "crud", - "crud generator", - "laravel", - "laravel crud generator", - "laravel package", - "tailwind css" - ], - "support": { - "issues": "https://github.com/awais-vteams/laravel-crud-generator/issues", - "source": "https://github.com/awais-vteams/laravel-crud-generator/tree/v2.1.2" - }, - "funding": [ - { - "url": "https://github.com/awais-vteams", - "type": "github" - }, - { - "url": "https://ko-fi.com/mawais", - "type": "ko_fi" - } - ], - "time": "2024-12-09T06:01:54+00:00" - }, { "name": "laravel/pail", "version": "v1.2.2", @@ -9316,12 +9474,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/database/migrations/2025_02_27_163347_add_formatted_registration_number_to_pbg_task_google_sheet.php b/database/migrations/2025_02_27_163347_add_formatted_registration_number_to_pbg_task_google_sheet.php new file mode 100644 index 0000000..7d419f7 --- /dev/null +++ b/database/migrations/2025_02_27_163347_add_formatted_registration_number_to_pbg_task_google_sheet.php @@ -0,0 +1,28 @@ +string('formatted_registration_number')->nullable()->after('no_registrasi'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pbg_task_google_sheet', function (Blueprint $table) { + $table->dropColumn('formatted_registration_number'); + }); + } +}; diff --git a/public/images/iconchatbot.jpeg b/public/images/iconchatbot.jpeg new file mode 100644 index 0000000..3225278 Binary files /dev/null and b/public/images/iconchatbot.jpeg differ diff --git a/public/templates/contentTemplatePrompt.json b/public/templates/contentTemplatePrompt.json new file mode 100644 index 0000000..938f381 --- /dev/null +++ b/public/templates/contentTemplatePrompt.json @@ -0,0 +1,11 @@ +{ + "RETRIBUTION": { + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + }, + "DOCUMENT VALIDATION": { + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + }, + "DATA SUMMARY": { + "prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation." + } +} \ No newline at end of file diff --git a/resources/js/chatbot/index.js b/resources/js/chatbot/index.js new file mode 100644 index 0000000..6a432e8 --- /dev/null +++ b/resources/js/chatbot/index.js @@ -0,0 +1,176 @@ +import GlobalConfig from "../global-config.js"; + +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll(".nav-link").forEach(tab => { + tab.addEventListener("click", function () { + setTimeout(() => { + const tab_active = getActiveTabId(); + console.log("Active Tab ID:", tab_active); + }, 100); // Timeout untuk memastikan class `active` sudah diperbarui + }); + }); + + 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 = ""; + + // Ambil tab aktif saat ini + const currentTab = getActiveTabId(); + + // 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(currentTab, 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 + } + + + // 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("col-9", "w-auto"); + + // if (sender === "user") { + // messageCol.classList.add("ms-auto"); // Geser ke kanan untuk user + // } + + // 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 + // 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); + // } + + // // 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(tab_active, userText) { + try { + const url = `${GlobalConfig.apiHost}/api/generate-text`; + const response = await fetch(url, { + method: "POST", + body: JSON.stringify({tab_active:tab_active, 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."; + } + } +}); + +function getActiveTabId() { + const activeTab = document.querySelector(".nav-link.active"); + return activeTab ? activeTab.id : null; +} diff --git a/resources/views/chatbot/index.blade.php b/resources/views/chatbot/index.blade.php new file mode 100644 index 0000000..20a897c --- /dev/null +++ b/resources/views/chatbot/index.blade.php @@ -0,0 +1,92 @@ +@extends('layouts.vertical', ['subtitle' => 'Chatbot']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) + +@endsection + +@section('content') +@include('layouts.partials/page-title', ['title' => 'Chatbot', 'subtitle' => 'Chatbot']) + +
+
+ +
+
+ + + +
+
+ + avatar-3 + +
+

Halo! Ada yang bisa saya bantu?

+
+
+
+ + +
+
+ + +
+ {{--
+ +
--}} +
+
+ +
+
+ +@endsection + +@section('scripts') +@vite(['resources/js/chatbot/index.js']) +@endsection \ No newline at end of file diff --git a/resources/views/layouts/partials/topbar.blade.php b/resources/views/layouts/partials/topbar.blade.php index bbfc197..5f339a3 100755 --- a/resources/views/layouts/partials/topbar.blade.php +++ b/resources/views/layouts/partials/topbar.blade.php @@ -22,14 +22,20 @@
- +
--}} + +
+ + + +