Skip to content

Instantly share code, notes, and snippets.

@paulwababu
Created April 26, 2024 20:03
Show Gist options
  • Save paulwababu/480a9027e0957054ce69e2d701c5f0de to your computer and use it in GitHub Desktop.
Save paulwababu/480a9027e0957054ce69e2d701c5f0de to your computer and use it in GitHub Desktop.
<!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