diff --git a/README.md b/README.md index 60f55e53..e73d0eda 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # Project Name -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +The chatbot will act as a virtual travel guide that helps users plan their next trip or dream about their future adventures. It can suggest destinations, give travel tips, or simply chat about the joys of exploring the world. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +I have only onclick button in this chatbot. If I have more time, I would like to try different input types such as dropdowns button with selection. ## View it live -Have you deployed your project somewhere? Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://travelchatbot.netlify.app diff --git a/code/assets/background.jpg b/code/assets/background.jpg new file mode 100644 index 00000000..ea093a61 Binary files /dev/null and b/code/assets/background.jpg differ diff --git a/code/assets/bot.png b/code/assets/bot.png index 2c894b15..ae898f3e 100644 Binary files a/code/assets/bot.png and b/code/assets/bot.png differ diff --git a/code/assets/bot.wav b/code/assets/bot.wav new file mode 100644 index 00000000..8af1ed83 Binary files /dev/null and b/code/assets/bot.wav differ diff --git a/code/assets/user.png b/code/assets/user.png index 6d95f08f..e72052d5 100644 Binary files a/code/assets/user.png and b/code/assets/user.png differ diff --git a/code/assets/user.wav b/code/assets/user.wav new file mode 100644 index 00000000..de73fbe8 Binary files /dev/null and b/code/assets/user.wav differ diff --git a/code/index.html b/code/index.html index 316eb187..99d904b7 100644 --- a/code/index.html +++ b/code/index.html @@ -1,32 +1,46 @@ - - - - - - Chatbot - + + + + + + Travel Adventure Chatbot + + - -

Welcome to my chatbot!

-
-
-
-
- - - -
+ +
+
+ +
+

WELCOME TO YOUR TRAVEL ADVENTURE CHATBOT!

+

I'll help you to find out your perfect destination.

+ 🏝️ +
+
+ +
+ +

✈️ LET'S START!

+
+
+
+
+ + +
+
-
+
+
- - + + - + \ No newline at end of file diff --git a/code/script.js b/code/script.js index 125d6904..97ab0eab 100644 --- a/code/script.js +++ b/code/script.js @@ -1,14 +1,30 @@ // DOM selectors (variables that point to selected DOM elements) goes here πŸ‘‡ -const chat = document.getElementById('chat') +const chat = document.getElementById("chat-messages") +const inputWrapper = document.getElementById("input-wrapper") // Wrapper for input/buttons +const nameForm = document.getElementById("name-form") // Form to capture the user's name +const nameInput = document.getElementById("name-input") // Input for the name + +// Store user choices +let userName = "" +const userChoices = {} // create an empty object to store several key-values + +// Add audio elements for feedback sounds +const botAudio = new Audio() +botAudio.src = "./assets/bot.wav" + +const userAudio = new Audio() +userAudio.src = "./assets/user.wav" // Functions goes here πŸ‘‡ -// A function that will add a chat bubble in the correct place based on who the sender is -const showMessage = (message, sender) => { - // The if statement checks if the sender is the user and if that's the case it inserts - // an HTML section inside the chat with the posted message from the user - if (sender === 'user') { - chat.innerHTML += ` +// Function to show chat bubbles +const showMessage = (message, sender, delay = 0) => { + setTimeout(() => { + if (sender === "user") { + // Play user sound and show message + userAudio.currentTime = 0 + userAudio.play() + chat.innerHTML += `

${message}

@@ -16,10 +32,11 @@ const showMessage = (message, sender) => { User
` - // The else if statement checks if the sender is the bot and if that's the case it inserts - // an HTML section inside the chat with the posted message from the bot - } else if (sender === 'bot') { - chat.innerHTML += ` + } else if (sender === "bot") { + // Play bot sound and show message + botAudio.currentTime = 0 + botAudio.play() + chat.innerHTML += `
Bot
@@ -27,27 +44,161 @@ const showMessage = (message, sender) => {
` + } + chat.scrollTop = chat.scrollHeight // Scroll to the latest message + }, delay) +} + +// Function to handle name input and greeting +const saveUsername = (event) => { + event.preventDefault() // Prevent page refresh + userName = nameInput.value.toUpperCase().trim() // Capture the name and trim whitespace + showMessage(`My name is ${userName}!`, "user") // Display user's name as a message + nameInput.value = "" // Clear the input field + setTimeout(() => showMessage(`${userName} is a wonderful name!`, "bot", 1000), 1000) + setTimeout(() => askDestination(), 2000) // Proceed to the next question +} + +// Ask the first question (dream destination) +const askDestination = () => { + showMessage(`What’s your dream destination, ${userName}?`, "bot", 1000) + setTimeout(() => renderOptions(["Paris", "Tokyo", "Dubai"], handleDestinationSelection), 1500) +} + +// Handle the answer to the first question (destination) +const handleDestinationSelection = (answer) => { + userChoices["Dream Destination"] = answer + showMessage(answer, "user") + let response = "" + if (answer === "Paris") response = "Ah, the City of Light! You'd love its history and architecture." + else if (answer === "Tokyo") response = "Tokyo is a vibrant metropolis, known for its cutting-edge technology and deep-rooted traditions. A perfect blend of modern and cultural experiences!" + else response = "Dubai is known for its luxury and modern wonders!" + setTimeout(() => showMessage(response, "bot", 1000), 1000) + setTimeout(() => askPreference(), 2000) // Proceed to the next question +} + +// Ask the second question (travel preference) +const askPreference = () => { + showMessage("Do you prefer mountains, beaches, or cities?", "bot", 1000) + setTimeout(() => renderOptions(["Mountains", "Beaches", "Cities"], handlePreferenceSelection), 1500) +} + +// Handle the answer to the second question (preference) +const handlePreferenceSelection = (answer) => { + userChoices["Preference"] = answer + showMessage(answer, "user") + let response = "" + if (answer === "Mountains") response = "Mountains are breathtaking, consider the Alps or Rockies!" + else if (answer === "Beaches") response = "Beaches are relaxing! You might love the Maldives." + else response = "Cities have so much to offer! Think about visiting New York." + setTimeout(() => showMessage(response, "bot", 1000), 1000) + setTimeout(() => askBudget(), 2000) // Proceed to the third question +} + +// Ask the third question (budget) +const askBudget = () => { + showMessage("What’s your budget for the trip?", "bot", 1000) + setTimeout(() => renderOptions(["Less than $1000", "$1000-$2000", "More than $2000"], handleBudgetSelection), 1500) +} + +// Handle the answer to the third question (budget) +const handleBudgetSelection = (answer) => { + userChoices["Budget"] = answer + showMessage(answer, "user") + let response = "" + if (answer === "Less than $1000") response = "Southeast Asia offers amazing experiences for a low budget!" + else if (answer === "$1000-$2000") response = "With that budget, Europe or South America could be perfect!" + else response = "With a larger budget, luxury awaits you in places like Paris or Dubai!" + setTimeout(() => showMessage(response, "bot", 1000), 1000) + setTimeout(() => askTravelStyle(), 2000) // Proceed to the fourth question +} + +// Ask the fourth question (travel style) +const askTravelStyle = () => { + showMessage("Are you traveling solo or with others?", "bot", 1000) + setTimeout(() => renderOptions(["Solo", "With family", "With friends"], handleTravelStyleSelection), 1500) +} + +// Handle the answer to the fourth question (travel style) +const handleTravelStyleSelection = (answer) => { + userChoices["Travel Style"] = answer + showMessage(answer, "user") + let response = "" + if (answer === "Solo") response = "Solo travel is a great way to discover yourself!" + else if (answer === "With family") response = "Family trips can create unforgettable memories!" + else response = "Trips with friends are always full of fun!" + setTimeout(() => showMessage(response, "bot", 1000), 1000) + setTimeout(() => askTripPriority(), 2000) // Proceed to the fifth question +} + +// Ask the fifth question (trip priority) +const askTripPriority = () => { + showMessage("What's the most important part of your trip: adventure, culture, or relaxation?", "bot", 1000) + setTimeout(() => renderOptions(["Adventure", "Culture", "Relaxation"], handleTripPrioritySelection), 1500) +} + +// Handle the answer to the fifth question (trip priority) +const handleTripPrioritySelection = (answer) => { + userChoices["Trip Priority"] = answer + showMessage(answer, "user") + let response = "" + if (answer === "Adventure") response = "Adventure seekers love places like New Zealand!" + else if (answer === "Culture") response = "Culture enthusiasts enjoy historic cities like Rome." + else response = "Relaxation awaits you at serene beach getaways like the Maldives." + setTimeout(() => showMessage(response, "bot", 1000), 1000) + setTimeout(() => showTripSummary(), 2000) // Proceed to the summary +} + +// Function to show a summary +const showTripSummary = () => { + const { "Dream Destination": destination, Preference: preference, Budget: budget, "Travel Style": travelStyle, "Trip Priority": priority } = userChoices + const summaryMessage = `Here's a summary of your trip: + You're planning to visit ${destination}, where you'll enjoy ${preference.toLowerCase()} activities. You'll be traveling ${travelStyle.toLowerCase()} with a budget of ${budget}, and your main focus is on ${priority.toLowerCase()}.` + showMessage(summaryMessage, "bot", 1000) + setTimeout(() => feedbackRequest(), 1000) + + // setTimeout(() => showMessage(`Thanks for chatting, ${userName}! Enjoy planning your adventure!`, "bot", 2000), 2000) +} + +// Function to ask for feedback +const feedbackRequest = () => { + showMessage(`Thank you ${userName} for asking me about your trip adventure! How did you feel about my response?`, "bot", 1000) + setTimeout(() => renderOptions(["It helps a lotπŸ‘", "Not useful πŸ’”"], handleFeedback), 1500) +} + +// Handle the answer to the feedback (if else) +const handleFeedback = (feedback) => { + // Display the user's feedback as a message + showMessage(feedback, "user") + + // Prepare the bot's response based on the feedback + let response = "" + if (feedback === "It helps a lotπŸ‘") { + response = "Thank you so much, it means a lot to me❀️" + } else { + response = "Sorry to hear that. I'll learn more and try to give better suggestions next time😊" } - // This little thing makes the chat scroll to the last message when there are too many to - // be shown in the chat box - chat.scrollTop = chat.scrollHeight + inputWrapper.innerHTML = "" // Clear the inputWrapper to hide the buttons after the final question + + setTimeout(() => showMessage(response, "bot", 1000), 1000) } -// A function to start the conversation -const greetUser = () => { - // Here we call the function showMessage, that we declared earlier with the argument: - // "Hello there, what's your name?" for message, and the argument "bot" for sender - showMessage("Hello there, what's your name?", 'bot') - // Just to check it out, change 'bot' to 'user' here πŸ‘† and see what happens + +// Helper function to render options as buttons +const renderOptions = (options, callback) => { + inputWrapper.innerHTML = "" // Clear previous options + options.forEach(option => { + const button = document.createElement("button") + button.classList.add("option-btn") + button.innerText = option + button.addEventListener("click", () => callback(option)) + inputWrapper.appendChild(button) + }) //πŸ‘ˆπŸ» A loop that iterates over each item in the options array. For each option, the following steps are executed. } -// Eventlisteners goes here πŸ‘‡ +// Event listener for form submission +nameForm.addEventListener("submit", saveUsername) -// Here we invoke the first function to get the chatbot to ask the first question when -// the website is loaded. Normally we invoke functions like this: greeting() -// To add a little delay to it, we can wrap it in a setTimeout (a built in JavaScript function): -// and pass along two arguments: -// 1.) the function we want to delay, and 2.) the delay in milliseconds -// This means the greeting function will be called one second after the website is loaded. -setTimeout(greetUser, 1000) +// Start the conversation with a greeting after a brief delay +setTimeout(() => showMessage("Welcome to Travel Adventure Chatbot! May I get your name?😊", "bot"), 1000) \ No newline at end of file diff --git a/code/style.css b/code/style.css index a275402f..df82f54e 100644 --- a/code/style.css +++ b/code/style.css @@ -1,76 +1,112 @@ * { box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --white:#fff; + --black:#000; + --green:#5faa6c; + --light-green:#7cad84; + --milk-green: #cbd6cd; } body { - margin: 0; - padding: 0; font-family: 'Montserrat', sans-serif; - background: #0026ff; + height: 100vh; + background-color: var(--light-green); } -h1 { - font-weight: bold; - font-size: 28px; - line-height: 34px; - color: #fff; - text-align: center; +/* Main layout */ +.layout { + display: flex; + height: 100%; /* Full viewport height */ } -h2 { - font-weight: bold; - font-size: 24px; - line-height: 34px; - color: #fff; +/* Left side to take full half of the page */ +.left-side { + width: 40%; /* Take half of the page */ + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; text-align: center; - margin-bottom: 36px; + padding-top: 25%; + background: url("./assets/background.jpg") no-repeat center center/cover; /* Background image full cover */ } -p { - font-size: 18px; - font-weight: 600; - line-height: 28px; - margin: 0; +h1 { + font-weight: 800; + font-size: 2.25rem; + line-height: 3rem; + color: var(--green); + margin: 0 20px 10px 20px; } -input { - box-sizing: border-box; - border: none; - border-radius: 4px 0 0 4px; - background: #e5e9ff; - color: #0026ff; - padding: 16px; - font-size: 16px; - font-family: 'Montserrat'; +h2 { font-weight: 600; - line-height: 26px; - flex: 1; - width: 100%; + font-size: 1.2rem; + line-height: 2rem; + color: var(--light-green); + padding: 0 10px; + margin-bottom: 10px; } -main { - margin: 0 auto; - width: 100%; - max-width: 700px; - height: 600px; - border-radius: 30px; - background: #fff; - padding: 20px 24px; - padding-top: 0; - box-sizing: border-box; +span { + font-size: 3rem; +} + +/* Right side to take the other half */ +.right-side { + width: 60%;; + display: flex; + flex-direction: column; + align-items: center; + padding: 50px 20px; +} + +h3 { + font-weight: 800; + font-size: 2rem; + line-height: 2rem; + color: var(--white); + text-align: center; + margin-bottom: 36px; +} + +.chatbox { display: flex; flex-direction: column; + justify-content: space-between; + height: 80vh; + border-radius: 30px; + background-color: var(--white); + padding: 20px 24px; } +/* Style of the chat-messages box */ .chat { - flex: 1; + flex-grow: 1; display: flex; justify-content: flex-start; - overflow: scroll; + overflow-y: scroll; + max-height: 100%; flex-direction: column; - padding-bottom: 16px; + padding: 16px; + background-color: var(--light-green); + border-radius: 15px; +} + +p { + font-weight: 600; + font-size: 1.2rem; + line-height: 1.5rem; + color: var(--light-green); } +/*bot message*/ .bot-msg { display: flex; margin: 16px 8px 0 0; @@ -91,15 +127,18 @@ main { } .bubble { - background: #e5e9ff; - font-weight: 600; - font-size: 16px; - line-height: 26px; - padding: 16px 24px; - color: #0026ff; - max-width: 40%; + background: var(--white); + padding: 16px; + /* max-width: 100%; Allow chat bubbles to expand fully */ +} + +@media (min-width: 900px) { + .bubble { + max-width: 40%; + } } + .bot-bubble { border-radius: 0px 26px 26px 26px; margin-left: 8px; @@ -113,6 +152,7 @@ main { .input-wrapper { display: flex; justify-content: center; + margin-top: 10px; } .input-wrapper form { @@ -121,25 +161,35 @@ main { align-items: center; } -label { - font-size: 16px; +input, button { font-family: 'Montserrat'; - font-weight: 500; - color: #0026ff; - margin-right: 20px; + font-weight: 600; + font-weight: 600; + font-size: 1.2rem; + line-height: 1.5rem; + padding: 16px 20px; +} + +input { + box-sizing: border-box; + border: none; + border-radius: 10px 0 0 10px; + background: var(--milk-green); + color: var(--black); + flex: 1; + width: 100%; +} + +input [type=text]:focus { + border: 3px solid var(--black); } button { - background-color: #0026ff; - color: white; + background-color: var(--green); + color: var(--white); border: none; - border-radius: 4px; - padding: 16px 20px; + border-radius: 0 4px 4px 0; margin-right: 4px; - font-size: 16px; - line-height: 26px; - font-family: 'Montserrat'; - font-weight: 500; cursor: pointer; transition: all 0.3s ease; } @@ -147,4 +197,38 @@ button { button:hover { opacity: 0.9; transition: all 0.2s ease; -} \ No newline at end of file +} + +/* Responsive Layout for Small Screen */ +@media (min-width: 650px) and (max-width: 768px) { + .left-side { + display: none; + } + + .right-side { + width: 100%; + } + + .bubble { + max-width: 60%; + } +} + +@media (max-width: 650px) { + .left-side { + display: none; + } + + .right-side { + width: 100%; + } + + .bubble { + max-width: 100%; + } + + .input-wrapper { + flex-direction: column; + gap: 5px; + } +} diff --git a/pull_request_template.md b/pull_request_template.md index 70fa177f..ba9f042a 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,3 +1,2 @@ ## Netlify link -Add your Netlify link here. -PS. Don't forget to add it in your readme as well. +https://travelchatbot.netlify.app