diff --git a/api/ai_chat.php b/api/ai_chat.php new file mode 100644 index 0000000..ede17fe --- /dev/null +++ b/api/ai_chat.php @@ -0,0 +1,67 @@ + 'Message is empty.']); + exit; +} + +$aiReply = 'Sorry, I could not process your request at the moment.'; +$responseSuccess = false; + +try { + $systemPrompt = 'You are a friendly and helpful AI assistant for IBMR Festverse, a college event management platform. Your goal is to answer questions about events, help users discover fests, and provide information about the platform. Keep your answers concise and cheerful.'; + + // Create the payload for the AI API + $payload = [ + 'input' => [ + ['role' => 'system', 'content' => $systemPrompt], + ['role' => 'user', 'content' => $userMessage], + ], + ]; + + // Options for the API call + $options = [ + 'poll_interval' => 5, // seconds + 'poll_timeout' => 120 // seconds + ]; + + // Call the AI service + $resp = LocalAIApi::createResponse($payload, $options); + + if (!empty($resp['success'])) { + $text = LocalAIApi::extractText($resp); + if ($text !== '') { + $aiReply = $text; + $responseSuccess = true; + } else { + // Handle cases where AI gives a non-text response or empty text + $aiReply = 'I received a response, but it was empty. Can you try rephrasing?'; + } + } else { + error_log('AI API Error: ' . ($resp['error'] ?? 'Unknown error')); + $aiReply = 'There was an issue connecting to the AI service. Please try again later.'; + } + +} catch (Exception $e) { + error_log('AI Chat Exception: ' . $e->getMessage()); + $aiReply = 'A server error occurred. We are looking into it.'; +} + +if ($responseSuccess) { + echo json_encode(['reply' => $aiReply]); +} else { + // In case of an error, we still return a JSON response with a user-friendly message + // The specific error is logged on the server for debugging + echo json_encode(['reply' => $aiReply]); +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..39393e5 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,200 @@ +:root { + --bg-color: #0D1117; + --surface-color: #161B22; + --border-color: #30363d; + --primary-gradient-start: #38BDF8; + --primary-gradient-end: #818CF8; + --accent-color: #F59E0B; + --text-primary: #E6EDF3; + --text-secondary: #7D8590; + --font-family-sans-serif: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +} + +body { + background-color: var(--bg-color); + color: var(--text-primary); + font-family: var(--font-family-sans-serif); + min-height: 100vh; +} + +.navbar { + background-color: transparent; + padding: 1rem 0; +} + +.navbar-brand { + font-weight: 700; + font-size: 1.5rem; + background: linear-gradient(45deg, var(--primary-gradient-start), var(--primary-gradient-end)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + color: transparent; +} + +.nav-link { + color: var(--text-secondary); + font-weight: 500; + transition: color 0.2s ease-in-out; +} + +.nav-link:hover, .nav-link.active { + color: var(--text-primary); +} + +.navbar-toggler { + border: none; + color: var(--text-primary); + font-size: 1.5rem; +} +.navbar-toggler:focus { + box-shadow: none; +} + +.hero-section { + padding: 8rem 0; + animation: fadeIn 1s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.hero-title { + font-weight: 700; + color: var(--text-primary); + letter-spacing: -1.5px; +} + +.hero-subtitle { + color: var(--text-secondary); + max-width: 600px; + margin: 1.5rem auto; +} + +.btn-primary { + background: linear-gradient(45deg, var(--primary-gradient-start), var(--primary-gradient-end)); + border: none; + padding: 0.75rem 2rem; + font-weight: 500; + transition: transform 0.2s ease-out, box-shadow 0.2s ease-out; +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(0,0,0,0.2); +} + +/* Chat Widget */ +#chat-widget-container { + position: fixed; + bottom: 2rem; + right: 2rem; + z-index: 1050; +} + +#chat-toggle-button { + width: 60px; + height: 60px; + font-size: 1.8rem; + background: linear-gradient(45deg, var(--primary-gradient-start), var(--primary-gradient-end)); + color: white; + border: none; + display: flex; + justify-content: center; + align-items: center; +} + +#chat-window { + display: none; + width: 370px; + height: 70vh; + max-height: 600px; + flex-direction: column; + background-color: var(--surface-color); + border: 1px solid var(--border-color); + color: var(--text-primary); + position: absolute; + bottom: 0; + right: 0; +} + +#chat-window.open { + display: flex; + animation: slideUp 0.3s ease-out; +} + +@keyframes slideUp { + from { transform: translateY(30px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +#chat-window .card-header { + background-color: var(--surface-color); + border-bottom: 1px solid var(--border-color); +} +#chat-window .card-header .ai-avatar { + width: 32px; + height: 32px; + border-radius: 50%; + background: linear-gradient(45deg, var(--primary-gradient-start), var(--primary-gradient-end)); + color: white; + display: flex; + align-items: center; + justify-content: center; +} + +#chat-window .card-footer { + background-color: var(--surface-color); + border-top: 1px solid var(--border-color); +} + +#chat-messages { + flex-grow: 1; + overflow-y: auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.message { + max-width: 85%; + padding: 0.5rem 1rem; + border-radius: 1.25rem; + display: inline-block; +} + +.message.received { + background-color: #21262d; + align-self: flex-start; + border-bottom-left-radius: 0.25rem; +} +.message.sent { + background: linear-gradient(45deg, var(--primary-gradient-start), var(--primary-gradient-end)); + color: white; + align-self: flex-end; + border-bottom-right-radius: 0.25rem; +} +.message .message-content { + margin: 0; + word-wrap: break-word; +} +.message.typing { + color: var(--text-secondary); + font-style: italic; + align-self: flex-start; +} + +#chat-input { + background-color: #0D1117; + border: 1px solid var(--border-color); + color: var(--text-primary); +} +#chat-input:focus { + background-color: #0D1117; + color: var(--text-primary); + box-shadow: none; + border-color: var(--primary-gradient-end); +} \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..cc27483 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,87 @@ +document.addEventListener('DOMContentLoaded', function () { + const chatWindow = document.getElementById('chat-window'); + const chatToggleButton = document.getElementById('chat-toggle-button'); + const chatCloseButton = document.getElementById('chat-close-button'); + const chatForm = document.getElementById('chat-form'); + const chatInput = document.getElementById('chat-input'); + const chatMessages = document.getElementById('chat-messages'); + + const toggleChat = (forceState) => { + const isOpen = chatWindow.classList.contains('open'); + const show = forceState !== undefined ? forceState : !isOpen; + + if (show) { + chatWindow.classList.add('open'); + chatToggleButton.style.display = 'none'; + } else { + chatWindow.classList.remove('open'); + chatToggleButton.style.display = 'flex'; + } + }; + + chatToggleButton.addEventListener('click', () => toggleChat(true)); + chatCloseButton.addEventListener('click', () => toggleChat(false)); + + const addMessage = (text, type, isTyping = false) => { + const messageDiv = document.createElement('div'); + messageDiv.classList.add('message', type); + + const contentDiv = document.createElement('div'); + contentDiv.classList.add('message-content'); + contentDiv.textContent = text; + messageDiv.appendChild(contentDiv); + + if (isTyping) { + messageDiv.id = 'typing-indicator'; + messageDiv.classList.add('typing'); + } + + chatMessages.appendChild(messageDiv); + chatMessages.scrollTop = chatMessages.scrollHeight; + return messageDiv; + }; + + chatForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const userInput = chatInput.value.trim(); + if (!userInput) return; + + addMessage(userInput, 'sent'); + chatInput.value = ''; + chatInput.disabled = true; + + const typingIndicator = addMessage('Festverse AI is typing...', 'received', true); + + try { + const response = await fetch('api/ai_chat.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ message: userInput }), + }); + + if (!response.ok) { + throw new Error('Network response was not ok.'); + } + + const data = await response.json(); + + typingIndicator.remove(); + + if (data.reply) { + addMessage(data.reply, 'received'); + } else { + addMessage('Sorry, I encountered an error. Please try again.', 'received'); + } + + } catch (error) { + console.error('Chat error:', error); + typingIndicator.remove(); + addMessage('Sorry, I could not connect to the AI assistant.', 'received'); + } finally { + chatInput.disabled = false; + chatInput.focus(); + } + }); +}); \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..3f15601 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,99 @@ - - - - New Style - - - - - - - - - - - - - - - - - - - + + + IBMR Festverse - Your College Event Universe + + + + + + + + + + + + + + + + + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ + + +
+
+

Discover Your Next College Experience

+

The ultimate platform for college fests, workshops, and events, supercharged with AI.

+ Explore Events +
+
+ + +
+ +
+
+
+
+ +
+
Festverse AI
+
+ +
+
+
+
Hello! How can I help you find the perfect event today?
+
+
+ +
-
- + + + + - + \ No newline at end of file