diff --git a/assets/images/chatbot.svg b/assets/images/chatbot.svg new file mode 100644 index 000000000..f57045c4d --- /dev/null +++ b/assets/images/chatbot.svg @@ -0,0 +1,2 @@ + +chat-bot \ No newline at end of file diff --git a/assets/images/guest.svg b/assets/images/guest.svg new file mode 100644 index 000000000..1ac79d232 --- /dev/null +++ b/assets/images/guest.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/assets/js/openads-chat.js b/assets/js/openads-chat.js new file mode 100644 index 000000000..e5639dd40 --- /dev/null +++ b/assets/js/openads-chat.js @@ -0,0 +1,122 @@ +(function() { + 'use strict'; + + const queryString = new URLSearchParams(window.location.search); + + const isLocalhost = window.location.hostname === 'localhost'; + const endpoint = isLocalhost ? + 'http://localhost:3000/chatbot' : + 'https://masteringjs-backend-production.up.railway.app/chatbot'; + + const widget = document.createElement('div'); + widget.innerHTML = '
Ask me anything about JavaScript
'; + widget.classList.add('openads-widget'); + widget.onclick = function() { + document.querySelector('.allwrapper').classList.add('openads-chat-fixed'); + chatWindow.classList.add('show'); + }; + document.body.appendChild(widget); + + const chatWindow = document.querySelector('.openads-chat'); + + const chatExit = document.querySelector('.openads-chat-exit'); + chatExit.onclick = function() { + document.querySelector('.allwrapper').classList.remove('openads-chat-fixed'); + chatWindow.classList.remove('show'); + }; + + const newMessageInput = document.querySelector('.openads-chat-input textarea'); + const newMessageButton = document.querySelector('.openads-chat-submit'); + const chatHistory = document.querySelector('.openads-chat-history'); + + setTimeout(() => { + if (queryString.has('show-chatbot')) { + document.querySelector('.allwrapper').classList.add('openads-chat-fixed'); + chatWindow.classList.add('show'); + } + }, 0); + + window.submitOpenAdsMessage = async function submitOpenAdsMessage() { + const value = newMessageInput.value; + if (!value) { + return; + } + newMessageButton.disabled = true; + newMessageButton.innerHTML = ''; + newMessageInput.disabled = true; + + const newMessage = document.createElement('div'); + newMessage.classList.add('openads-chat-history-message'); + newMessage.innerHTML = ` +
+ + Guest +
+
+ ${value} +
+ `; + chatHistory.appendChild(newMessage); + + let response; + let error = false; + try { + response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ question: value }) + }).then(res => res.json()); + } catch (err) { + + } + + newMessageInput.value = ''; + newMessageInput.disabled = false; + newMessageButton.disabled = false; + newMessageButton.innerHTML = '»'; + + const newResponse = document.createElement('div'); + newResponse.classList.add('openads-chat-history-message'); + let sources; + if (response.sources) { + sources = response.sources.map(source => ` +
+ ${source.title} +
+ `).join('\n'); + } else { + sources = ` +
+ ${response.title} +
+ `; + } + newResponse.innerHTML = ` +
+ + Mastering JS +
+
+ ${marked.parse(response.content)} +
+
+
+
 
+
+ Source +
+
+ ${sources} +
+ `; + chatHistory.appendChild(newResponse); + }; + + document.querySelector('.openads-chat-input textarea').addEventListener('keypress', ev => { + if (ev.code === 'Enter') { + window.submitOpenAdsMessage(); + } + }); +})(); \ No newline at end of file diff --git a/assets/openads-chat.css b/assets/openads-chat.css new file mode 100644 index 000000000..39c3af517 --- /dev/null +++ b/assets/openads-chat.css @@ -0,0 +1,180 @@ +.openads-widget { + position: fixed; + background-color: #f0db4f; + right: 0px; + top: 50vh; + padding: 1em; + cursor: pointer; + width: 180px; + display: flex; + gap: 10px; +} + +.openads-chat-fixed { + position: fixed; +} + +.openads-chat { + position: fixed; + width: 0px; + right: 0px; + height: 100%; + background-color: white; + border-left: 1px solid #ddd; + border-bottom: 1px solid #ddd; + top: 0px; + box-sizing: border-box; + transition: width 0.3s ease; + z-index: 1000000; + overflow: hidden; +} + +.openads-chat.show { + width: 60vw; +} + +.openads-chat-exit { + position: absolute; + right: 2px; + top: 0px; + cursor: pointer; +} + +.openads-chat-wrapper { + position: relative; + display: flex; + flex-direction: column; + padding-top: 1.5em; + padding-bottom: 1em; + box-sizing: border-box; + height: 100%; + gap: 10px; + font-size: 18px; +} + +.openads-chat-history { + flex-grow: 1; + border-bottom: 1px solid #ddd; + padding-left: 1em; + padding-right: 1em; + overflow-y: scroll; + overflow-x: hidden; +} + +.openads-chat-new-message { + display: flex; + width: 100%; + box-sizing: border-box; + padding-left: 1em; + padding-right: 1em; +} + +.openads-chat-message-body p:first-child { + margin-top: 0px; +} + +.openads-chat-input { + flex-grow: 1; +} + +.openads-chat-input textarea { + box-sizing: border-box; + width: 100%; + font-size: 1.1em; + margin-bottom: 0px; + border: 1px solid #eee; + height: 3em; + border-radius: 4px; +} + +.open-ads-chat-button { + flex-grow: 0; +} + +.openads-chat-submit { + border: 0px; + background-color: #208E96; + color: white; + font-size: 1.5em; + box-sizing: border-box; + height: 2.25em; + border-radius: 4px; + padding-top: 0px; + cursor: pointer; + width: 1.25em; +} + +.openads-chat-submit:disabled { + background-color: #ddd; +} + +.openads-chat-submit img { + width: 0.75em; +} + +.openads-chat-message-body { + margin-left: 24px; +} + +.openads-chat-history-message { + margin-bottom: 1em; +} + +.openads-chat-history-message .participant { + color: #666; + font-size: 0.9em; +} + +.openads-chat-history-message .participant img { + height: 16px; + width: 16px; + margin-right: 4px; +} + +.openads-chat-message-source-header { + color: #666; + font-weight: 600; + font-size: 0.9em; + position: relative; +} + +.openads-chat-message-source-header-line { + position: absolute; + border-bottom: 4px solid #666; + width: 100%; + top: 0px; + line-height: 8px; + z-index: -1; +} + +.openads-chat-icon { + flex-grow: 0; + padding-top: 0.5em; +} + +.openads-chat-icon img { + height: 2em; +} + +.openads-widget-text { + flex-grow: 1; +} + +@media (max-width: 1000px) { + .openads-chat.show { + width: 100vw; + } + + .openads-widget { + position: fixed; + background-color: #f0db4f; + top: auto; + right: 10px; + bottom: 10px; + padding: 1em; + border-radius: 4px; + cursor: pointer; + width: 120px; + font-size: 0.9em; + } +} \ No newline at end of file diff --git a/components/layout.js b/components/layout.js index 35361735f..7ac447436 100644 --- a/components/layout.js +++ b/components/layout.js @@ -26,7 +26,7 @@ module.exports = params => ` - + @@ -45,11 +45,45 @@ module.exports = params => ` ${footer()} ${floatAd(params.ad)} + ${openAdsChat} + + + ${carbonAdScript(params.carbonAds)} `; +const openAdsChat = ` +
+
+
×
+
+
+
+ + Mastering JS +
+
+ Hi, I'm a JavaScript programming bot. + Ask me something about JavaScript! +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+`.trim(); + function carbonAdScript(carbonAds) { if (carbonAds === false) { return ''; diff --git a/ebooks/mastering-mongoose-thankyou.html b/ebooks/mastering-mongoose-thankyou.html index 4f398476c..af9473776 100644 --- a/ebooks/mastering-mongoose-thankyou.html +++ b/ebooks/mastering-mongoose-thankyou.html @@ -21,7 +21,7 @@ - + @@ -144,6 +144,36 @@

Resources

+
+
+
×
+
+
+
+ + Mastering JS +
+
+ Hi, I'm a JavaScript programming bot. + Ask me something about JavaScript! +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + +