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 0000000..d69cb16 Binary files /dev/null and b/assets/pasted-20250911-213644-13107a79.png differ diff --git a/db/config.php b/db/config.php index d630275..499d974 100644 --- a/db/config.php +++ b/db/config.php @@ -6,12 +6,35 @@ define('DB_USER', 'app_30855'); define('DB_PASS', 'eee81949-37de-47f9-a26f-14ebc8402f7f'); function db() { - static $pdo; - if (!$pdo) { - $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ - PDO::ATTR_ERRMODE => 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

+
+
+ +
+
-
- + + + - + \ 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 @@ +