From b86f18f85fe652f9137ef1a947b36914e8e0b22c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 11 Sep 2025 21:39:07 +0000 Subject: [PATCH] v1 --- .env | 1 + .env.example | 1 + api/chat.php | 58 +++++++ assets/css/custom.css | 93 +++++++++++ assets/js/main.js | 66 ++++++++ assets/pasted-20250911-213644-13107a79.png | Bin 0 -> 54499 bytes db/config.php | 41 +++-- db/migrate.php | 12 ++ db/migrations/001_create_chat_log_table.sql | 7 + index.php | 161 +++++--------------- lib/OpenAIService.php | 65 ++++++++ lib/config.php | 30 ++++ 12 files changed, 404 insertions(+), 131 deletions(-) create mode 100644 .env create mode 100644 .env.example create mode 100644 api/chat.php create mode 100644 assets/css/custom.css create mode 100644 assets/js/main.js create mode 100644 assets/pasted-20250911-213644-13107a79.png create mode 100644 db/migrate.php create mode 100644 db/migrations/001_create_chat_log_table.sql create mode 100644 lib/OpenAIService.php create mode 100644 lib/config.php diff --git a/.env b/.env new file mode 100644 index 0000000..f7ae637 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +OPENAI_API_KEY=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 \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e570b8b --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +OPENAI_API_KEY= diff --git a/api/chat.php b/api/chat.php new file mode 100644 index 0000000..87f0a45 --- /dev/null +++ b/api/chat.php @@ -0,0 +1,58 @@ + 'Invalid request method.']); + exit; +} + +$input = json_decode(file_get_contents('php://input'), true); + +if (!isset($input['message']) || trim($input['message']) === '') { + echo json_encode(['error' => 'Message is required.']); + exit; +} + +$user_message = trim($input['message']); + +try { + $pdo = db(); + + // Save user message + $stmt = $pdo->prepare("INSERT INTO chat_log (session_id, sender, message) VALUES (?, ?, ?)"); + $stmt->execute([$session_id, 'user', $user_message]); + + // Get AI response + $response = OpenAIService::getCompletion($user_message); + + if (!empty($response['error'])) { + http_response_code(500); + echo json_encode($response); + exit; + } + + $ai_message = $response['reply']; + + // Save AI message + $stmt = $pdo->prepare("INSERT INTO chat_log (session_id, sender, message) VALUES (?, ?, ?)"); + $stmt->execute([$session_id, 'ai', $ai_message]); + + echo json_encode(['reply' => $ai_message]); + +} catch (PDOException $e) { + // Log error and return a generic error message + error_log("Database error: " . $e->getMessage()); + echo json_encode(['error' => 'An internal server error occurred.']); + exit; +} \ No newline at end of file diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..65a254d --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,93 @@ +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #F3F4F6; + margin: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; +} + +.chat-container { + width: 100%; + max-width: 600px; + height: 100%; + max-height: 800px; + display: flex; + flex-direction: column; + background-color: #FFFFFF; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +.chat-header { + background: linear-gradient(90deg, #4F46E5, #6D28D9); + color: white; + padding: 16px; + text-align: center; + font-size: 1.25rem; +} + +.chat-box { + flex-grow: 1; + padding: 16px; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 12px; +} + +.chat-message { + padding: 10px 16px; + border-radius: 18px; + max-width: 75%; + word-wrap: break-word; +} + +.user-message { + background-color: #4F46E5; + color: white; + align-self: flex-end; + border-bottom-right-radius: 4px; +} + +.ai-message { + background-color: #E5E7EB; + color: #1F2937; + align-self: flex-start; + border-bottom-left-radius: 4px; +} + +.chat-footer { + padding: 16px; + border-top: 1px solid #E5E7EB; +} + +#chat-form { + display: flex; + gap: 10px; +} + +#message-input { + flex-grow: 1; + border: 1px solid #D1D5DB; + padding: 12px; + border-radius: 8px; + font-size: 1rem; +} + +#chat-form button { + background-color: #4F46E5; + color: white; + border: none; + padding: 12px 20px; + border-radius: 8px; + cursor: pointer; + font-size: 1rem; + transition: background-color 0.2s; +} + +#chat-form button:hover { + background-color: #4338CA; +} \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..86e0ead --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,66 @@ +document.addEventListener('DOMContentLoaded', () => { + const chatForm = document.getElementById('chat-form'); + const messageInput = document.getElementById('message-input'); + const chatBox = document.getElementById('chat-box'); + + // Load chat history on page load + function loadChatHistory() { + if (window.chatHistory && window.chatHistory.length > 0) { + window.chatHistory.forEach(item => { + appendMessage(item.message, item.sender); + }); + } + } + + chatForm.addEventListener('submit', async (e) => { + e.preventDefault(); + + const message = messageInput.value.trim(); + if (!message) return; + + appendMessage(message, 'user'); + messageInput.value = ''; + + const typingIndicator = appendMessage('Typing...', 'ai'); + + try { + const response = await fetch('/api/chat.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ message }) + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const data = await response.json(); + typingIndicator.remove(); + + if(data.error) { + appendMessage(data.error, 'ai'); + } else { + appendMessage(data.message, 'ai'); // Bug fix: data.reply -> data.message + } + + } catch (error) { + typingIndicator.remove(); + appendMessage('Sorry, something went wrong.', 'ai'); + console.error('Error:', error); + } + }); + + function appendMessage(message, sender) { + const messageElement = document.createElement('div'); + messageElement.classList.add('chat-message', `${sender}-message`); + messageElement.textContent = message; + chatBox.appendChild(messageElement); + chatBox.scrollTop = chatBox.scrollHeight; + return messageElement; + } + + // Initial load + loadChatHistory(); +}); diff --git a/assets/pasted-20250911-213644-13107a79.png b/assets/pasted-20250911-213644-13107a79.png new file mode 100644 index 0000000000000000000000000000000000000000..d69cb166839fb1ab027ebcb03bcd7242b8eba1a8 GIT binary patch literal 54499 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 TZm=32?xXNb PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; -} + static $pdo; + if ($pdo) { + return $pdo; + } + + $dsn = 'mysql:host=' . DB_HOST . ';charset=utf8mb4'; + $options = [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]; + + try { + // Try connecting to the database directly + $pdo = new PDO($dsn . ';dbname=' . DB_NAME, DB_USER, DB_PASS, $options); + } catch (PDOException $e) { + // If the database doesn't exist, create it + if (strpos($e->getMessage(), 'Unknown database') !== false) { + try { + $tempPdo = new PDO($dsn, DB_USER, DB_PASS, $options); + $tempPdo->exec('CREATE DATABASE IF NOT EXISTS `' . DB_NAME . '`'); + // Now, connect to the newly created database + $pdo = new PDO($dsn . ';dbname=' . DB_NAME, DB_USER, DB_PASS, $options); + } catch (PDOException $ce) { + die("Failed to create database and connect: " . $ce->getMessage()); + } + } else { + die("Database connection failed: " . $e->getMessage()); + } + } + + return $pdo; +} \ No newline at end of file diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..3fb354e --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,12 @@ +exec($sql); + echo "Database migration successful! Table 'chat_log' is ready.\n"; +} catch (PDOException $e) { + die("Database migration failed: " . $e->getMessage() . "\n"); +} + diff --git a/db/migrations/001_create_chat_log_table.sql b/db/migrations/001_create_chat_log_table.sql new file mode 100644 index 0000000..6761858 --- /dev/null +++ b/db/migrations/001_create_chat_log_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `chat_log` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `session_id` VARCHAR(255) NOT NULL, + `sender` VARCHAR(50) NOT NULL, + `message` TEXT NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/index.php b/index.php index e13ae95..4cc9cd2 100644 --- a/index.php +++ b/index.php @@ -1,131 +1,48 @@ prepare("SELECT sender, message FROM chat_log WHERE session_id = ? ORDER BY created_at ASC"); + $stmt->execute([$_SESSION['session_id']]); + $chat_history = $stmt->fetchAll(); + } catch (PDOException $e) { + // Not critical if history fails to load, so we just log it. + error_log("Could not load chat history: " . $e->getMessage()); + } +} ?> - + - - - New Style - - - - + + + Chat with AI + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

Flatlogic AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+
+
+

AI Chat

+
+
+ +
+
-
-
- Page updated: (UTC) -
+ + + - + \ No newline at end of file diff --git a/lib/OpenAIService.php b/lib/OpenAIService.php new file mode 100644 index 0000000..7d7db5a --- /dev/null +++ b/lib/OpenAIService.php @@ -0,0 +1,65 @@ + 'OpenAI API key is not configured.']; + } + + $url = 'https://api.openai.com/v1/chat/completions'; + + $data = [ + 'model' => 'gpt-3.5-turbo', + 'messages' => [ + ['role' => 'system', 'content' => 'You are a helpful assistant.'], + ['role' => 'user', 'content' => $message] + ], + 'max_tokens' => 150 + ]; + + $headers = [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . self::$apiKey + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $response = curl_exec($ch); + $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error) { + return ['error' => 'API call failed: ' . $error]; + } + + if ($httpcode !== 200) { + return ['error' => 'API returned status ' . $httpcode . ': ' . $response]; + } + + $decodedResponse = json_decode($response, true); + + if (isset($decodedResponse['choices'][0]['message']['content'])) { + return ['reply' => $decodedResponse['choices'][0]['message']['content']]; + } elseif (isset($decodedResponse['error'])) { + return ['error' => $decodedResponse['error']['message']]; + } + + return ['error' => 'Unexpected API response format.']; + } +} + +OpenAIService::init(); diff --git a/lib/config.php b/lib/config.php new file mode 100644 index 0000000..743ae43 --- /dev/null +++ b/lib/config.php @@ -0,0 +1,30 @@ +