Created
April 26, 2024 20:03
-
-
Save paulwababu/480a9027e0957054ce69e2d701c5f0de to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Ask Friendly</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="icon" href="https://res.cloudinary.com/prometheusapi/image/upload/v1710534811/image_3_uqxyey.png" type="image/x-icon"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> | |
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" /> | |
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> | |
<style> | |
#map { height: 350px; } | |
.leaflet-container { border-radius: 8px; } | |
.chat-container { max-height: 300px; overflow-y: auto; } | |
.chat-container.expanded { padding-top: 1.5rem; } | |
.user-message { background-color: #f1f3dd; padding: 0.75rem 1rem; border-radius: 0.5rem; margin-bottom: 0.5rem; } | |
.ai-message { background-color: #F2F2F2; color: rgb(0, 0, 0); padding: 0.75rem 1rem; border-radius: 0.5rem; margin-bottom: 0.5rem; text-align: left; } | |
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap'); | |
body { font-family: 'Open Sans', sans-serif; } | |
html, body { height: 100%; margin: 0; } | |
.container { height: 100vh; } | |
.sidebar { background-color: #f3f4f6; padding: 1rem; border-right: 1px solid #e5e7eb; overflow-y: auto; transition: transform 0.3s ease-in-out; } | |
.sidebar.collapsed { transform: translateX(-100%); } | |
.sidebar-header { font-size: 1.125rem; font-weight: 600; margin-bottom: 1rem; } | |
.sidebar-item { display: flex; align-items: center; padding: 0.5rem; border-radius: 0.375rem; margin-bottom: 0.5rem; cursor: pointer; } | |
.sidebar-item:hover { background-color: #e5e7eb; } | |
.sidebar-item-icon { width: 2rem; height: 2rem; border-radius: 50%; margin-right: 0.75rem; } | |
.sidebar-item-text { font-size: 0.875rem; } | |
.sidebar-divider { border-top: 1px solid #e5e7eb; margin: 1rem 0; } | |
.user-icon { width: 2rem; height: 2rem; border-radius: 50%; background-color: #8b5cf6; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; margin-right: 0.75rem; } | |
.logged-in-user { display: flex; align-items: center; padding: 0.5rem; border-radius: 0.375rem; cursor: pointer; } | |
.logged-in-user:hover { background-color: #e5e7eb; } | |
.toggle-sidebar-btn { position: absolute; top: 10px; right: 10px; background-color: #fff; border: 1px solid #e5e7eb; border-radius: 0.375rem; padding: 0.5rem; cursor: pointer; z-index: 1000; } | |
</style> | |
<script> | |
// Function to fetch conversations from the server | |
async function fetchConversations() { | |
try { | |
const sessionID = sessionStorage.getItem('sessionID'); | |
const response = await fetch(`/chat-history/${sessionID}`); | |
const conversations = await response.json(); | |
updateConversationList(conversations); | |
} catch (error) { | |
console.error('Error fetching conversations:', error); | |
} | |
} | |
// Function to update the conversation list UI with the fetched data | |
function updateConversationList(conversations) { | |
const conversationList = document.getElementById('conversation-list'); | |
conversationList.innerHTML = ''; | |
conversations.forEach(conversation => { | |
const conversationElement = document.createElement('div'); | |
conversationElement.className = 'sidebar-item conversation'; | |
conversationElement.textContent = conversation.latest_message; | |
conversationElement.setAttribute('data-conversation-id', conversation.conversation_id); | |
conversationElement.addEventListener('click', () => { | |
fetchConversationMessages(conversation.conversation_id); | |
}); | |
conversationList.appendChild(conversationElement); | |
}); | |
} | |
// Function to fetch conversation messages from the server | |
async function fetchConversationMessages(conversationId) { | |
try { | |
const sessionID = sessionStorage.getItem('sessionID'); | |
const response = await fetch(`/chat-conversation/${sessionID}/${conversationId}`); | |
const messages = await response.json(); | |
displayConversationMessages(messages); | |
} catch (error) { | |
console.error('Error fetching conversation messages:', error); | |
} | |
} | |
// Function to display conversation messages in the chat container | |
function displayConversationMessages(messages) { | |
const chatContainer = document.getElementById('chat-container'); | |
chatContainer.innerHTML = ''; | |
messages.forEach(message => { | |
const messageElement = document.createElement('div'); | |
messageElement.className = message.role === 'user' ? 'user-message' : 'ai-message'; | |
messageElement.textContent = message.content; | |
chatContainer.appendChild(messageElement); | |
}); | |
} | |
// Load conversations when the page is loaded | |
window.addEventListener('load', fetchConversations); | |
</script> | |
</head> | |
<body class="bg-white-100"> | |
<div class="container flex"> | |
<div class="w-1/5 sidebar collapsed flex flex-col"> | |
<div class="flex-grow overflow-y-auto"> | |
<div class="sidebar-header"> | |
<div class="text-lg font-semibold flex items-center"> | |
<img src="https://res.cloudinary.com/prometheusapi/image/upload/v1710534811/image_3_uqxyey.png" alt="ChatBot Icon" class="rounded-full h-12 w-12"> | |
<span class="ml-3" style="font-size:1.1em;">Ask Friendly</span> | |
</div> | |
</div> | |
<div class="sidebar-divider"></div> | |
<div class="sidebar-header">Conversations</div> | |
<div id="conversation-list"></div> | |
</div> | |
<div class="flex items-center justify-center mt-4"> | |
<button id="dark-mode-toggle" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"> | |
<i class="fas fa-moon mr-2"></i> Dark Mode | |
</button> | |
</div> | |
<div class="logged-in-user"> | |
<div class="user-icon" id="user-initials"></div> | |
<span class="sidebar-item-text" id="user-name"></span> | |
</div> | |
</div> | |
<div class="w-4/6 flex flex-col"> | |
<button id="show-map-btn" class="text-gray-400 hover:text-gray-600" style="display: none;"> | |
<i class="fas fa-map-marker-alt fa-lg"></i> | |
</button> | |
<script> | |
$('#dark-mode-toggle').on('click', function() { | |
$('body').toggleClass('dark-mode'); | |
// Save the user's preference in local storage | |
if ($('body').hasClass('dark-mode')) { | |
localStorage.setItem('darkMode', 'true'); | |
} else { | |
localStorage.setItem('darkMode', 'false'); | |
} | |
}); | |
// Load the user's preference from local storage on page load | |
const darkModeEnabled = localStorage.getItem('darkMode') === 'true'; | |
if (darkModeEnabled) { | |
$('body').addClass('dark-mode'); | |
} | |
</script> | |
<style> | |
/* Dark mode styles for the form area */ | |
body.dark-mode .bg-white { | |
background-color: #212121; | |
} | |
body.dark-mode input, | |
body.dark-mode textarea, | |
body.dark-mode button { | |
border-color: #4a4a4a; | |
} | |
/* Dark mode styles for input form background color */ | |
body.dark-mode input, | |
body.dark-mode textarea { | |
background-color: #3e3e3e; | |
color: #fff; | |
} | |
body.dark-mode input::placeholder, | |
body.dark-mode textarea::placeholder { | |
color: #888; | |
} | |
body.dark-mode button { | |
background-color: #4a4a4a; | |
} | |
body.dark-mode button:hover { | |
background-color: #5c5c5c; | |
} | |
/* Dark mode styles */ | |
body.dark-mode { | |
background-color: #212121; | |
color: #fff; | |
} | |
body.dark-mode .sidebar { | |
background-color: #171717; | |
} | |
body.dark-mode .chat-container { | |
background-color: #212121; | |
} | |
body.dark-mode .user-message { | |
background-color: #3e3e3e; | |
} | |
body.dark-mode .ai-message { | |
background-color: #4a4a4a; | |
} | |
/* You may need to adjust other colors for text, icons, etc. to fit the dark mode theme */ | |
#show-map-btn { | |
background-color: transparent; | |
border: none; | |
cursor: pointer; | |
padding: 0; | |
margin: 0 10px; | |
} | |
</style> | |
<script> | |
$('#show-map-btn').on('click', function() { | |
$('#map-container').show(); | |
}); | |
</script> | |
<div id="map-container" class="relative mb-4"> | |
<div id="toggle-sidebar-btn-container"> | |
<button class="toggle-sidebar-btn" id="toggle-sidebar-btn"> | |
<i class="fas fa-bars"></i> | |
</button> | |
</div> | |
<div class="flex justify-between items-center border-b-2 border-gray-200 pb-4"> | |
<!-- Removed the toggle-sidebar-btn from here --> | |
<div class="flex-grow"></div> | |
<div class="flex justify-end items-center"> | |
<button id="cancel-map-btn" class="text-gray-400 hover:text-gray-600"> | |
<i class="fas fa-times fa-lg"></i> | |
</button> | |
</div> | |
</div> | |
<div id="map" class="h-96 rounded-lg"></div> | |
</div> | |
<img id="ask-friendly-logo" src="https://res.cloudinary.com/prometheusapi/image/upload/v1710961905/7682900d-2e64-408d-9d24-d01e50ee979b_yhnkuk.png" alt="Ask Friendly Logo"> | |
<style> | |
#ask-friendly-logo { | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
z-index: -1; | |
width: 200px; /* Adjust the width as needed */ | |
height: auto; /* Maintain the aspect ratio */ | |
pointer-events: none; /* Ignore mouse events to allow interaction with content above the logo */ | |
} | |
#toggle-sidebar-btn-container { | |
position: absolute; | |
top: -10px; /* Keep this the same if the vertical position is good */ | |
left: 45px; /* Increase if you need it to move right, or decrease to move left */ | |
z-index: 1001; /* Keep it above Leaflet's controls */ | |
} | |
</style> | |
<div id="chat-container" class="flex-grow overflow-y-auto"> | |
<div class="user-message">Hi, what's happening with the wildlife in Maui?</div> | |
<div class="ai-message">There's a significant event involving wildlife. We're gathering more information and will update shortly.</div> | |
</div> | |
<div class="text-xs text-gray-500 text-right"> | |
Call 911 for life-threatening emergencies. Assistant is not perfect and can make mistakes or misunderstand information. | |
</div> | |
<div class="bg-white shadow rounded-lg p-4"> | |
<div class="flex items-center justify-between"> | |
<input id="message" type="text" placeholder="What can I do to assist you? 😊" class="w-full p-2 border border-gray-300 rounded-lg focus:outline-none" /> | |
<button id="submit-button" class="ml-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> | |
Submit | |
</button> | |
</div> | |
<div class="flex items-center justify-between mt-4"> | |
<div class="flex space-x-2"> | |
<button class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-plus"></i> | |
</button> | |
<button class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-font"></i> | |
</button> | |
<button class="text-gray-500 hover:text-gray-700"> | |
<i class="far fa-smile"></i> | |
</button> | |
<button class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-at"></i> | |
</button> | |
<button class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-video"></i> | |
</button> | |
</div> | |
<div class="flex-grow text-center"> | |
<button class="text-blue-500 hover:text-blue-700"> | |
<i class="fas fa-microphone"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Confirmation Modal --> | |
<div id="confirmation-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden justify-center items-center" style="z-index: 1000;"> | |
<div class="bg-white rounded-lg p-4 max-w-sm mx-auto"> | |
<div class="flex flex-col items-center justify-center"> | |
<img src="https://res.cloudinary.com/prometheusapi/image/upload/v1710534811/image_3_uqxyey.png" alt="ChatBot Icon" class="rounded-full h-12 w-12 mb-4"> | |
<p class="text-gray-800 font-semibold">Are you sure you want to clear the chat?</p> | |
<div class="mt-4 flex justify-center space-x-4"> | |
<button id="confirm-clear" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded inline-flex items-center"> | |
<i class="fas fa-trash mr-2"></i> Confirm | |
</button> | |
<button id="cancel-clear" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"> | |
<i class="fas fa-times mr-2"></i> Cancel | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
$(document).ready(function() { | |
// Function to handle successful location retrieval | |
function handleLocationSuccess(position) { | |
const latitude = position.coords.latitude; | |
const longitude = position.coords.longitude; | |
// Store location information for later use | |
window.userLocation = `Latitude: ${latitude}, Longitude: ${longitude}`; | |
getLocationName(latitude, longitude) | |
.then(locationName => { | |
userLocationName = locationName; | |
console.log(userLocationName) | |
}); | |
} | |
function getLocationName(latitude, longitude) { | |
const apiKey = '93e4e817d64d43b1966d7b22059698b5'; | |
const url = `https://api.opencagedata.com/geocode/v1/json?q=${latitude}+${longitude}&key=${apiKey}`; | |
return fetch(url) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.status.code === 200 && data.results.length > 0) { | |
return data.results[0].formatted; | |
} else { | |
return 'Unknown location'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
return 'Unknown location'; | |
}); | |
} | |
// Function to handle location retrieval errors | |
function handleLocationError(error) { | |
console.warn(`ERROR(${error.code}): ${error.message}`); | |
window.userLocation = 'Location not available'; | |
} | |
// Request user's location | |
if (navigator.geolocation) { | |
navigator.geolocation.getCurrentPosition(handleLocationSuccess, handleLocationError); | |
} else { | |
console.log("Geolocation is not supported by this browser."); | |
window.userLocation = 'Location not supported'; | |
} | |
$('#message').on('focus', function() { | |
const text = $(this).val(); | |
if (text.length === 0) { | |
$('#suggestions').show(); // Show suggestions if text area is empty | |
} | |
}); | |
$('#message').on('input', function() { | |
if ($(this).val().length === 0) { | |
$('#suggestions').show(); // Show suggestions if text area is emptied | |
} else { | |
$('#suggestions').hide(); // Hide suggestions if text area is not empty | |
} | |
}); | |
$('#message').on('blur', function() { | |
// Hide suggestions when the text area loses focus | |
// Use a timeout to allow click event to process first | |
setTimeout(function() { | |
$('#suggestions').hide(); | |
}, 100); | |
}); | |
$('.suggestion').on('click', function() { | |
const suggestion = $(this).text(); | |
$('#message').val(suggestion).focus(); // Set the clicked suggestion text to the text area and focus | |
$('#suggestions').hide(); // Hide the suggestions | |
}); | |
// Toggle sidebar on button click | |
$('#toggle-sidebar-btn').on('click', function() { | |
$('.sidebar').toggleClass('collapsed'); | |
}); | |
// Cancel map on button click | |
$('#cancel-map-btn').on('click', function() { | |
$('#map-container').hide(); | |
$('#show-map-btn').show(); | |
}); | |
}); | |
</script> | |
<script> | |
window.onload = function() { | |
// Extract the entire query string from the URL | |
const fullUrl = window.location.href; | |
// Find the start of the 'creator' parameter and extract the substring from there | |
const creatorParamStart = fullUrl.indexOf("?creator=") + "?creator=".length; | |
let creatorJson = fullUrl.substring(creatorParamStart); | |
// Assume that 'creator' is the last parameter in the URL | |
// Replace any '&' beyond the first occurrence in the JSON string (caused by incorrect URL parsing) | |
creatorJson = creatorJson.replace(/&(?=[^&]*$)/, '%26'); | |
// Decode the URI component to handle URL encoding | |
creatorJson = decodeURIComponent(creatorJson); | |
try { | |
// Parse the JSON string to an object | |
const creator = JSON.parse(creatorJson); | |
// Extract the 'firstName' and 'lastName' properties | |
const firstName = creator.firstName; | |
const lastName = creator.lastName; | |
// Step 3: Update the user initials and name in the HTML | |
const userInitials = document.getElementById('user-initials'); | |
const userName = document.getElementById('user-name'); | |
userInitials.textContent = `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase(); | |
userName.textContent = `${firstName} ${lastName}`; | |
// Now you can use firstName and lastName as needed | |
console.log('First Name:', firstName); | |
console.log('Last Name:', lastName); | |
} catch (error) { | |
console.error('Error parsing JSON from URL:', error); | |
} | |
}; | |
</script> | |
<script> | |
var map = L.map('map').setView([20.798363, -156.331925], 10); // Default location | |
$(document).ready(function() { | |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
maxZoom: 19, | |
attribution: '© OpenStreetMap contributors' | |
}).addTo(map); | |
// Fetch data and add markers | |
function loadMarkers() { | |
fetch('http://localhost:3003/data') // Adjust URL as needed | |
.then(response => response.json()) | |
.then(data => { | |
console.log(data); // Log the data to see what's actually being returned | |
// Assuming the data is an object with a "data" key which is an array | |
data.forEach(item => { | |
// Each item is an object where the key is unknown and the value is the actual data | |
for (const key in item) { | |
const entry = item[key]; | |
const lat = entry.lat || entry.location.latitude; | |
const lng = entry.lng || entry.location.longitude; | |
if (!lat || !lng) { | |
console.error('Missing latitude or longitude:', entry); | |
} else { | |
var marker = L.marker([lat, lng]).addTo(map); | |
marker.bindPopup(`<b>${entry.name}</b><br>${entry.description || 'No description available'}`); | |
} | |
} | |
}); | |
}) | |
.catch(error => console.error('Error loading markers:', error)); | |
} | |
loadMarkers(); // Call function to load markers | |
// Function to parse the creator data from the URL | |
function parseCreatorData() { | |
const fullUrl = window.location.href; | |
const creatorParamStart = fullUrl.indexOf("?creator=") + "?creator=".length; | |
let creatorJson = fullUrl.substring(creatorParamStart); | |
creatorJson = creatorJson.replace(/&(?=[^&]*$)/, '%26'); | |
creatorJson = decodeURIComponent(creatorJson); | |
try { | |
return JSON.parse(creatorJson); | |
} catch (error) { | |
console.error('Error parsing JSON from URL:', error); | |
return null; // Return null or a default object | |
} | |
} | |
const creatorData = parseCreatorData(); | |
// Function to create an SVG icon with the user's initials | |
function createUserIcon(firstName, lastName) { | |
const initials = `${firstName.charAt(0)}${lastName.charAt(0)}`; | |
const svgIcon = encodeURI(`data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='50' height='50'><circle cx='25' cy='25' r='20' fill='#5679C0' /><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='16' font-family='Arial, sans-serif'>${initials}</text></svg>`).replace('#', '%23'); | |
return L.icon({ | |
iconUrl: svgIcon, | |
iconSize: [50, 50], // Size of the icon | |
iconAnchor: [25, 50], // Point of the icon which will correspond to marker's location | |
popupAnchor: [0, -50] // Point from which the popup should open relative to the iconAnchor | |
}); | |
} | |
if (creatorData) { | |
var userIcon = createUserIcon(creatorData.firstName, creatorData.lastName); | |
function handleLocationSuccess(position) { | |
const latitude = position.coords.latitude; | |
const longitude = position.coords.longitude; | |
// Fly to the user's location once | |
map.flyTo(new L.LatLng(latitude, longitude), 13, { | |
animate: true, | |
duration: 5 // Duration in seconds for the flying effect | |
}); | |
setTimeout(function() { | |
var marker = L.marker([latitude, longitude], {icon: userIcon}).addTo(map); | |
marker.bindPopup(`<b>${creatorData.firstName} ${creatorData.lastName} is here!</b>`).openPopup(); | |
// Add bouncing effect | |
bounceMarker(marker); | |
}, 3500); // Delay the marker appearance slightly after the map flies to the location | |
} | |
function handleLocationError(error) { | |
console.warn(`ERROR(${error.code}): ${error.message}`); | |
} | |
// Function to add a bouncing effect to the marker | |
function bounceMarker(marker) { | |
var originalLatLng = marker.getLatLng(); | |
var bounceHeight = 0.0001; | |
var bounceRate = 400; // Bounce every 400ms | |
setInterval(function() { | |
var newLatLng = [ | |
originalLatLng.lat + (Math.random() - 0.5) * bounceHeight, | |
originalLatLng.lng + (Math.random() - 0.5) * bounceHeight | |
]; | |
marker.setLatLng(newLatLng); | |
}, bounceRate); | |
} | |
if (navigator.geolocation) { | |
navigator.geolocation.getCurrentPosition(handleLocationSuccess, handleLocationError); | |
} else { | |
console.log("Geolocation is not supported by this browser."); | |
} | |
} else { | |
console.log("Failed to parse creator data from URL."); | |
} | |
}); | |
</script> | |
<!-- Integrate the script into the HTML code --> | |
<script> | |
// Your script code here | |
const messageInput = document.getElementById('message'); | |
const submitButton = document.getElementById('submit-button'); | |
const chatContainer = document.getElementById('chat-container'); | |
const retryButton = document.getElementById('retry-button'); | |
const undoButton = document.getElementById('undo-button'); | |
const clearButton = document.getElementById('clear-button'); | |
const confirmationModal = document.getElementById('confirmation-modal'); | |
const confirmClearButton = document.getElementById('confirm-clear'); | |
const cancelClearButton = document.getElementById('cancel-clear'); | |
let lastUserMessage; | |
let lastAIResponseElement; | |
let mapIdCounter = 0; // Counter to assign unique IDs for map elements | |
messageInput.addEventListener('input', () => { | |
messageInput.style.height = 'auto'; | |
messageInput.style.height = `${messageInput.scrollHeight}px`; | |
}); | |
messageInput.addEventListener('keydown', (event) => { | |
if (event.key === 'Enter' && !event.shiftKey) { | |
event.preventDefault(); | |
submitButton.click(); | |
} | |
}); | |
async function typeResponse(element, text) { | |
console.log("Hello here is the text:", text); | |
let index = 0; | |
while (index < text.length) { | |
if (text.charAt(index) === '\n') { | |
element.innerHTML += '<br>'; | |
} else { | |
element.innerHTML += text.charAt(index); | |
} | |
index++; | |
await new Promise(resolve => setTimeout(resolve, 50)); | |
chatContainer.scrollTop = chatContainer.scrollHeight; | |
} | |
} | |
submitButton.addEventListener('click', async () => { | |
let message = messageInput.value.trim(); | |
if (!message) { | |
return; | |
} | |
const userMessageDiv = document.createElement('div'); | |
userMessageDiv.className = 'mb-4 bg-custom-blue p-4 rounded-lg chat-bubble'; | |
userMessageDiv.innerHTML = `<p class="bg-custom-blue">${message}</p>`; | |
chatContainer.appendChild(userMessageDiv); | |
lastUserMessage = message; | |
const aiResponseDiv = document.createElement('div'); | |
aiResponseDiv.className = 'mb-4 bg-gray-100 p-4 rounded-lg flex flex-col items-center'; // Change to flex-col | |
const loaderHTML = ` | |
<div class="loader flex space-x-2"> | |
<div class="animate-pulse w-2 h-2 rounded-full bg-gray-800"></div> | |
<div class="animate-pulse w-2 h-2 rounded-full bg-gray-800"></div> | |
<div class="animate-pulse w-2 h-2 rounded-full bg-gray-800"></div> | |
</div>`; | |
aiResponseDiv.innerHTML = loaderHTML; | |
chatContainer.appendChild(aiResponseDiv); | |
// Function to generate a simple UUID. This is a basic version for demonstration. | |
function generateUUID() { | |
let dt = new Date().getTime(); | |
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
const r = (dt + Math.random()*16)%16 | 0; | |
dt = Math.floor(dt/16); | |
return (c === 'x' ? r : (r&0x3|0x8)).toString(16); | |
}); | |
return uuid; | |
} | |
// Check if session ID exists in sessionStorage | |
let sessionID = sessionStorage.getItem('sessionID'); | |
console.log(sessionID) | |
// If session ID does not exist, generate a new one and store it in sessionStorage | |
if (!sessionID) { | |
sessionID = generateUUID(); | |
sessionStorage.setItem('sessionID', sessionID); | |
} | |
// Set an expiration time for the session ID (5 minutes) | |
const expirationTime = 5 * 60 * 1000; // 5 minutes in milliseconds | |
setTimeout(() => { | |
sessionStorage.removeItem('sessionID'); | |
}, expirationTime); | |
console.log(sessionID); | |
// Define the API URL | |
let apiUrl = '/submit-message'; | |
// Check if the message query includes the word 'map' | |
if (message.toLowerCase().includes('map')) { | |
apiUrl = '/dummy-message'; // Use the dummy endpoint | |
} | |
if (message.toLowerCase().includes('report')) { | |
apiUrl = '/reportinformation'; // Use the dummy endpoint | |
} | |
try { | |
let response; | |
if(apiUrl === '/dummy-message') { | |
// Construct query string | |
const queryString = new URLSearchParams({ question: message }).toString(); | |
const urlWithParams = `${apiUrl}?${queryString}`; | |
// Use GET request for dummy message | |
response = await fetch(urlWithParams, { | |
method: 'GET', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
// Do not use 'params' with fetch; query parameters should be in the URL | |
}); | |
} | |
if (apiUrl === '/submit-message') { | |
let requestBody = { | |
message: message, | |
userLocation: null, | |
userDetails: null | |
}; | |
//const locationKeywords = ['where', 'near', 'around', 'location', 'area']; | |
//if (locationKeywords.some(keyword => message.toLowerCase().includes(keyword))) { | |
//requestBody.userLocation = userLocationName; | |
//} | |
const urlParams = new URLSearchParams(window.location.search); | |
requestBody.userDetails = urlParams.toString(); | |
response = await fetch(apiUrl, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'X-Session-ID': sessionID, | |
}, | |
body: JSON.stringify(requestBody), | |
}); | |
} | |
if(apiUrl === '/reportinformation') { | |
// Use POST request for the actual API | |
response = await fetch(apiUrl, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'X-Session-ID': sessionID, | |
}, | |
body: JSON.stringify({ message: message }), | |
}); | |
} | |
if (!response.ok) { | |
throw new Error('Network response was not ok ' + response.statusText); | |
} | |
const data = await response.json(); | |
// Clearing the loader and setting up the response | |
aiResponseDiv.innerHTML = ''; | |
const aiResponseText = document.createElement('div'); | |
aiResponseText.className = 'response-text mb-2'; | |
aiResponseDiv.appendChild(aiResponseText); | |
console.log(data) | |
// Typing out the AI response | |
await typeResponse(aiResponseText, data.message); | |
if (data.map && typeof data.map.latitude !== 'undefined' && typeof data.map.longitude !== 'undefined') { | |
// Move the main map to the new coordinates | |
map.flyTo([data.map.latitude, data.map.longitude], 13); | |
// Clear existing markers if any | |
map.eachLayer(function(layer) { | |
if (layer instanceof L.Marker) { | |
map.removeLayer(layer); | |
} | |
}); | |
// Define the icon for the new marker | |
const customIcon = L.icon({ | |
iconUrl: data.map.iconUrl, | |
iconSize: [35, 50], | |
iconAnchor: [17, 42] // Adjust if necessary to position the icon correctly | |
}); | |
// Create a marker at the new location with a popup | |
const marker = L.marker([data.map.latitude, data.map.longitude], {icon: customIcon}).addTo(map); | |
marker.bindPopup(`<strong>Event:</strong><br>${data.map.eventType || 'Event'}`, {maxWidth: "auto"}).openPopup(); | |
} else { | |
console.log('No map data available for this response.'); | |
} | |
} catch (error) { | |
console.error('Error:', error); | |
aiResponseDiv.innerHTML = 'Error loading response.'; | |
} | |
messageInput.value = ''; | |
messageInput.style.height = 'auto'; | |
chatContainer.scrollTop = chatContainer.scrollHeight; | |
}); | |
// Handle Retry button click | |
retryButton.addEventListener('click', async () => { | |
if (lastUserMessage) { | |
messageInput.value = lastUserMessage; | |
try { | |
const response = await fetch('/submit-message', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ message: lastUserMessage }), | |
}); | |
const data = await response.json(); | |
const messageText = JSON.stringify(data); | |
console.log("Hello here is the data:", messageText); | |
// Replace the existing AI response with the new response | |
lastAIResponseElement.textContent = ''; | |
await typeResponse(lastAIResponseElement, messageText); | |
} catch (error) { | |
console.error('Error:', error); | |
} | |
} | |
}); | |
// Handle Undo button click | |
undoButton.addEventListener('click', () => { | |
// Remove the last user message and AI response from the chat container | |
const lastUserMessage = chatContainer.lastElementChild; | |
const lastAIResponse = chatContainer.lastElementChild.previousElementSibling; | |
if (lastUserMessage && lastAIResponse) { | |
chatContainer.removeChild(lastUserMessage); | |
chatContainer.removeChild(lastAIResponse); | |
lastUserMessage = null; | |
lastAIResponseElement = null; // Add this line | |
} | |
}); | |
// Modify the Clear button event listener to show the confirmation modal | |
clearButton.addEventListener('click', () => { | |
confirmationModal.classList.remove('hidden'); | |
}); | |
// Clear the chat and hide the modal when Confirm is clicked | |
confirmClearButton.addEventListener('click', () => { | |
chatContainer.innerHTML = ''; | |
lastUserMessage = null; | |
lastAIResponseElement = null; | |
confirmationModal.classList.add('hidden'); | |
}); | |
// Hide the modal without clearing the chat when Cancel is clicked | |
cancelClearButton.addEventListener('click', () => { | |
confirmationModal.classList.add('hidden'); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment