238 lines
9.0 KiB
JavaScript
238 lines
9.0 KiB
JavaScript
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const suggestionForm = document.getElementById('suggestion-form');
|
|
const queryInput = document.getElementById('query');
|
|
const personaSelect = document.getElementById('persona');
|
|
const loadingIndicator = document.getElementById('loading');
|
|
const errorMessage = document.getElementById('error-message');
|
|
const errorText = document.getElementById('error-text');
|
|
const chatContainer = document.getElementById('chat-container');
|
|
const luckyButton = document.getElementById('lucky-button');
|
|
const funFact = document.getElementById('fun-fact');
|
|
|
|
let conversation = [];
|
|
let map;
|
|
let funFactInterval;
|
|
|
|
const funFacts = [
|
|
"Poland is home to the world's largest castle, Malbork Castle.",
|
|
"The name \"Poland\" (Polska) originates from the Polanie tribe, meaning \"people living in open fields.\"",
|
|
"Poland has 17 Nobel Prize winners, including four for Peace and five for Literature.",
|
|
"The Polish alphabet consists of 32 letters.",
|
|
"Marie Curie, the pioneering scientist, was Polish.",
|
|
"Poland is the world's biggest exporter of amber.",
|
|
"The city of Wrocław has a network of over 300 small bronze statues of dwarves (krasnale).",
|
|
"Poland's Białowieża Forest is one of the last and largest remaining parts of the immense primeval forest that once stretched across the European Plain."
|
|
];
|
|
|
|
suggestionForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const query = queryInput.value.trim();
|
|
if (!query) return;
|
|
|
|
const persona = personaSelect.value;
|
|
const userMessage = { role: 'user', content: query };
|
|
conversation.push(userMessage);
|
|
|
|
appendMessage('user', { content: query });
|
|
queryInput.value = '';
|
|
|
|
fetchSuggestions(persona);
|
|
});
|
|
|
|
luckyButton.addEventListener('click', function() {
|
|
const queries = [
|
|
"a hidden gem in Warsaw",
|
|
"the best pierogi in Krakow",
|
|
"a beautiful beach on the Baltic coast",
|
|
"a challenging hike in the Tatra Mountains",
|
|
"a quirky museum in Gdansk"
|
|
];
|
|
const randomQuery = queries[Math.floor(Math.random() * queries.length)];
|
|
queryInput.value = randomQuery;
|
|
suggestionForm.dispatchEvent(new Event('submit'));
|
|
});
|
|
|
|
function showRandomFunFact() {
|
|
const randomIndex = Math.floor(Math.random() * funFacts.length);
|
|
funFact.textContent = funFacts[randomIndex];
|
|
}
|
|
|
|
function startLoading() {
|
|
loadingIndicator.style.display = 'block';
|
|
showRandomFunFact();
|
|
funFactInterval = setInterval(showRandomFunFact, 3000);
|
|
}
|
|
|
|
function stopLoading() {
|
|
loadingIndicator.style.display = 'none';
|
|
clearInterval(funFactInterval);
|
|
}
|
|
|
|
function showError(message) {
|
|
errorText.textContent = message;
|
|
errorMessage.style.display = 'block';
|
|
}
|
|
|
|
function hideError() {
|
|
errorMessage.style.display = 'none';
|
|
}
|
|
|
|
function appendMessage(role, data) {
|
|
const messageWrapper = document.createElement('div');
|
|
messageWrapper.classList.add('message-wrapper', `${role}-wrapper`);
|
|
|
|
const messageBubble = document.createElement('div');
|
|
messageBubble.classList.add('message-bubble', `${role}-bubble`);
|
|
|
|
if (role === 'user') {
|
|
messageBubble.textContent = data.content;
|
|
} else {
|
|
if (data.image) {
|
|
const img = document.createElement('img');
|
|
img.src = data.image;
|
|
img.classList.add('card-img-top');
|
|
messageBubble.appendChild(img);
|
|
}
|
|
|
|
const cardBody = document.createElement('div');
|
|
cardBody.classList.add('card-body');
|
|
|
|
if (data.title) {
|
|
const title = document.createElement('h5');
|
|
title.classList.add('card-title');
|
|
title.textContent = data.title;
|
|
cardBody.appendChild(title);
|
|
}
|
|
|
|
if (data.description) {
|
|
const description = document.createElement('p');
|
|
description.classList.add('card-text');
|
|
description.innerHTML = data.description; // Using innerHTML to render line breaks
|
|
cardBody.appendChild(description);
|
|
}
|
|
|
|
if (data.itinerary && data.itinerary.length > 0) {
|
|
const itinerarySection = document.createElement('div');
|
|
itinerarySection.classList.add('itinerary-section');
|
|
const itineraryTitle = document.createElement('h6');
|
|
itineraryTitle.textContent = 'Itinerary';
|
|
itinerarySection.appendChild(itineraryTitle);
|
|
|
|
const itineraryList = document.createElement('ul');
|
|
itineraryList.classList.add('list-group');
|
|
data.itinerary.forEach(day => {
|
|
const li = document.createElement('li');
|
|
li.classList.add('list-group-item');
|
|
li.innerHTML = `<b>Day ${day.day}:</b> ${day.activities.join(', ')}`;
|
|
itineraryList.appendChild(li);
|
|
});
|
|
itinerarySection.appendChild(itineraryList);
|
|
cardBody.appendChild(itinerarySection);
|
|
}
|
|
if (data.location && data.location.lat && data.location.lng) {
|
|
const mapId = `map-${Date.now()}`;
|
|
const mapDiv = document.createElement('div');
|
|
mapDiv.id = mapId;
|
|
mapDiv.style.height = '300px';
|
|
mapDiv.classList.add('mt-3');
|
|
cardBody.appendChild(mapDiv);
|
|
|
|
// Use a timeout to ensure the div is in the DOM before initializing the map
|
|
setTimeout(() => {
|
|
const map = L.map(mapId).setView([data.location.lat, data.location.lng], 13);
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
}).addTo(map);
|
|
L.marker([data.location.lat, data.location.lng]).addTo(map)
|
|
.bindPopup(data.title || 'Location')
|
|
.openPopup();
|
|
}, 100);
|
|
}
|
|
|
|
|
|
messageBubble.appendChild(cardBody);
|
|
|
|
const saveButton = document.createElement('button');
|
|
saveButton.textContent = 'Save';
|
|
saveButton.classList.add('btn', 'btn-sm', 'btn-outline-primary', 'mt-2');
|
|
saveButton.addEventListener('click', () => saveSuggestion(data));
|
|
cardBody.appendChild(saveButton);
|
|
|
|
}
|
|
|
|
messageWrapper.appendChild(messageBubble);
|
|
chatContainer.appendChild(messageWrapper);
|
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
}
|
|
|
|
function saveSuggestion(suggestionData) {
|
|
fetch('api/index.php?action=save_suggestion', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(suggestionData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Suggestion saved!');
|
|
} else {
|
|
showError(data.error || 'Could not save suggestion.');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
showError('An error occurred while saving the suggestion.');
|
|
});
|
|
}
|
|
|
|
function fetchSuggestions(persona) {
|
|
startLoading();
|
|
hideError();
|
|
|
|
fetch('api/index.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
query: conversation[conversation.length - 1].content,
|
|
persona: persona,
|
|
conversation: conversation.slice(0, -1) // Send previous conversation
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
stopLoading();
|
|
if (data.error) {
|
|
showError(data.error);
|
|
// Do not add error to conversation history
|
|
return;
|
|
}
|
|
|
|
const assistantMessage = { role: 'assistant', content: data };
|
|
conversation.push(assistantMessage);
|
|
appendMessage('assistant', data);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
stopLoading();
|
|
showError('An error occurred while fetching the suggestion. Please try again.');
|
|
});
|
|
}
|
|
|
|
const errorAlert = document.getElementById('error-message');
|
|
if (errorAlert) {
|
|
errorAlert.querySelector('.btn-close').addEventListener('click', function () {
|
|
hideError();
|
|
});
|
|
}
|
|
});
|