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'])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@