Implement Help button and modal

This commit is contained in:
michael 2025-11-19 11:52:26 -06:00
parent 2676f40309
commit afb151009a
3 changed files with 333 additions and 2 deletions

View file

@ -21,7 +21,8 @@
<div class="sidebar-subtitle">Conversations</div>
<div class="conversations-list" id="conversations-list"></div>
</div>
<div style="color: rgb(255, 255, 255); text-decoration: none; font-weight: 200; position: fixed; bottom: 0; left: 0; height: 50px; width: 250px; background-color: black; z-index: 2000;">
<div style="color: rgb(255, 255, 255); text-decoration: none; font-weight: 200; position: fixed; bottom: 0; left: 0; height: 90px; width: 250px; background-color: black; z-index: 2000;">
<button id="help-btn" class="help-btn">Help</button>
<a href="privacy/" style="color: rgb(255, 255, 255); text-decoration: none; font-weight: 200; position: absolute; bottom: 15px; left: 30px;">View our privacy policy here</a>
</div>
</div>
@ -38,6 +39,20 @@
<a style="color: rgb(255, 255, 255); text-decoration: none; font-weight: 200; position: absolute; bottom: 15px; right: 30px;">Log Out</a>
</div>
</div>
<!-- Help Modal -->
<div id="help-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Help</h2>
<span class="close-btn">&times;</span>
</div>
<div class="modal-body">
<p>Make sure you refresh (CTRL + Shift + R) when encountering an error:</p>
<p>Contact support: <a href="mailto:USCBaisupport@oliver.agency">USCBaisupport@oliver.agency</a></p>
</div>
</div>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script>
<script src="js/variables.js"></script>

View file

@ -607,6 +607,201 @@ const selectTOV = (new_tov_key) => {
document.getElementById("tov-list").classList.add("tov-list-hidden");
};
const onAuthenticated = () => {
goToCreateNewConversationsPage();
getConversations();
getAssistants();
getTOVs();
// Ensure the send button is enabled by default
document.getElementById("message-input").value = "";
document.getElementById("assistant-name-container").classList.remove("assistant-name-container-active");
document.getElementById("assistant-name").classList.remove("assistant-name-placeholder");
document.getElementById("assistants-list").classList.add("assistants-list-hidden");
document.getElementById("tov-name-container").classList.remove("tov-name-container-active");
document.getElementById("tov-name").classList.remove("tov-name-placeholder");
document.getElementById("tov-list").classList.add("tov-list-hidden");
Array.from(document.getElementsByClassName("chat-message-appear"))?.map((el) => el.classList.remove("chat-message-appear"));
document.getElementById("chat").innerHTML += chat_message_user_new.replaceAll("{CONTENT}", md_converter?.makeHtml(message));
// Scroll to Last Message
document.getElementById("chat").scrollTop = document.getElementById("chat").scrollHeight;
setTimeout(() => {
Array.from(document.getElementsByClassName("chat-message-appear"))?.map((el) => el.classList.remove("chat-message-appear"));
document.getElementById("chat").innerHTML += chat_message_assistant_loading_dots;
setTimeout(() => {
// Scroll to Last Message
document.getElementById("chat").scrollTop = document.getElementById("chat").scrollHeight;
}, 5);
}, 2000);
//console.log("URI encoded message: " + encodeURI(message));
const utf8Bytes = new TextEncoder().encode(message);
let binary = '';
for (let i = 0; i < utf8Bytes.length; i++) {
binary += String.fromCharCode(utf8Bytes[i]);
}
const encodedMessage = btoa(binary);
// Function to make the API call (so we can retry it)
const makeAPICall = (retryCount = 0) => {
return gcp_fetch(
make_url +
`?ConversationID=${encodeURI(conversation_id)}&AssistantKey=${encodeURI(new_assistant_key)}&TOV_Key=${encodeURI(
new_tov_key
)}&Message=${encodeURIComponent(encodedMessage)}`
)
.then((res) => {
if (!res.ok) {
throw new Error(`Server responded with status: ${res.status} ${res.statusText}`);
}
return res.json();
})
.then((res) => {
if (sending_message && res?.conversation_id) {
conversation_id = res?.conversation_id;
if (conversations.findIndex((e) => e.id === res?.conversation_id) === -1 && res?.conversation_title) {
conversations = [
{ id: res?.conversation_id, title: res?.conversation_title, assistant_key: new_assistant_key, tov_key: new_tov_key },
].concat(conversations);
document.getElementById("conversations-list").innerHTML = conversations
?.map((conversation) => {
if (conversation?.id === res?.conversation_id)
return conversations_list_item_active
?.replaceAll("{CONVERSATION_ID}", conversation?.id)
?.replaceAll("{CONVERSATION_TITLE}", processConversationTitle(conversation?.title));
return conversations_list_item
?.replaceAll("{CONVERSATION_ID}", conversation?.id)
?.replaceAll("{CONVERSATION_TITLE}", processConversationTitle(conversation?.title));
})
?.join("");
}
}
// Re-enable the send button
document.getElementById("send-button").disabled = false;
sending_message = false;
Array.from(document.getElementsByClassName("chat-message-appear"))?.map((el) => el.classList.remove("chat-message-appear"));
Array.from(document.getElementsByClassName("chat-message-loading-dots"))?.map((el) => el.remove());
// Check if we have a message, otherwise show error
if (res?.message) {
document.getElementById("chat").innerHTML += chat_message_assistant_new.replaceAll("{CONTENT}", md_converter?.makeHtml(removeCitations(res.message)));
} else {
document.getElementById("chat").innerHTML += chat_message_assistant_new.replaceAll("{CONTENT}", "Sorry, there was an issue processing your request. The response was incomplete.");
}
// Scroll to Last Message
document.getElementById("chat").scrollTop = document.getElementById("chat").scrollHeight;
})
.catch((error) => {
console.error(`Error in API call (attempt ${retryCount + 1}):`, error);
// If this is the first failure, retry once
if (retryCount === 0) {
console.log("Retrying request...");
// Keep loading indicator
// Don't remove the loading dots yet
// Wait a moment before retrying (e.g., 1 second)
return new Promise(resolve => setTimeout(resolve, 1000))
.then(() => makeAPICall(retryCount + 1));
}
// If we've already retried or max retries reached, show the error
// Re-enable the send button
document.getElementById("send-button").disabled = false;
sending_message = false;
Array.from(document.getElementsByClassName("chat-message-appear"))?.map((el) => el.classList.remove("chat-message-appear"));
Array.from(document.getElementsByClassName("chat-message-loading-dots"))?.map((el) => el.remove());
// Show error message in chat
document.getElementById("chat").innerHTML += chat_message_assistant_new.replaceAll(
"{CONTENT}",
`<div class="error-message">Sorry, an error occurred: ${error.message}</div>`
);
// Scroll to Last Message
document.getElementById("chat").scrollTop = document.getElementById("chat").scrollHeight;
});
};
// Start the API call with retry functionality
makeAPICall();
};
const toggleAssistantsDropdown = () => {
if (conversation_id !== false || sending_message) return false;
if (Array.from(document.getElementById("assistants-list").classList).includes("assistants-list-hidden")) {
document.getElementById("assistant-name-container").classList.add("assistant-name-container-active");
document.getElementById("assistants-list").classList.remove("assistants-list-hidden");
} else {
document.getElementById("assistant-name-container").classList.remove("assistant-name-container-active");
document.getElementById("assistants-list").classList.add("assistants-list-hidden");
}
};
const selectAssistant = (new_assistant_key) => {
if (conversation_id !== false || sending_message) return false;
const assistant = assistants?.find((e) => e.key === new_assistant_key);
if (!assistant) return false;
assistant_key = new_assistant_key;
document.getElementById("assistant-name").innerHTML = assistants.find((e) => e.key === new_assistant_key)?.name;
document.getElementById("assistant-name-container").classList.remove("assistant-name-container-active");
document.getElementById("assistant-name").classList.remove("assistant-name-placeholder");
document.getElementById("assistants-list").classList.add("assistants-list-hidden");
document.getElementById("chat").innerHTML = ``;
setTimeout(() => {
if (JSON.stringify(assistant_key) === JSON.stringify(new_assistant_key)) {
document.getElementById("chat").innerHTML = chat_message_assistant_new.replaceAll("{CONTENT}", assistant?.initial_message);
}
}, 500);
};
const toggleTOVsDropdown = () => {
if (conversation_id !== false || sending_message) return false;
if (Array.from(document.getElementById("tov-list").classList).includes("tov-list-hidden")) {
document.getElementById("tov-name-container").classList.add("tov-name-container-active");
document.getElementById("tov-list").classList.remove("tov-list-hidden");
} else {
document.getElementById("tov-name-container").classList.remove("tov-name-container-active");
document.getElementById("tov-list").classList.add("tov-list-hidden");
}
};
const selectTOV = (new_tov_key) => {
if (conversation_id !== false || sending_message) return false;
const tov = tone_of_voices?.find((e) => e.key === new_tov_key);
if (!tov) return false;
tov_key = new_tov_key;
document.getElementById("tov-name").innerHTML = tone_of_voices.find((e) => e.key === new_tov_key)?.name;
document.getElementById("tov-name-container").classList.remove("tov-name-container-active");
document.getElementById("tov-name").classList.remove("tov-name-placeholder");
document.getElementById("tov-list").classList.add("tov-list-hidden");
};
const onAuthenticated = () => {
goToCreateNewConversationsPage();
getConversations();
@ -620,4 +815,29 @@ const onAuthenticated = () => {
sendButton.disabled = false;
}
}, 500); // Small delay to ensure DOM is ready
};
};
// Help Modal Functionality
document.addEventListener('DOMContentLoaded', () => {
const modal = document.getElementById("help-modal");
const btn = document.getElementById("help-btn");
const span = document.getElementsByClassName("close-btn")[0];
if (btn) {
btn.onclick = function() {
modal.style.display = "flex";
}
}
if (span) {
span.onclick = function() {
modal.style.display = "none";
}
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
});

View file

@ -652,3 +652,99 @@ button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Help Button */
.help-btn {
position: absolute;
top: 15px;
left: 30px;
background-color: #0079E8;
color: white;
border: none;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: background-color 0.2s;
}
.help-btn:hover {
background-color: #0056b3;
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
z-index: 3000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 0;
border: 1px solid #888;
width: 80%;
max-width: 600px;
border-radius: 8px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
animation-name: animatetop;
animation-duration: 0.4s
}
@keyframes animatetop {
from {top: -300px; opacity: 0}
to {top: 0; opacity: 1}
}
.modal-header {
padding: 15px 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
font-size: 24px;
font-weight: 600;
}
.close-btn {
color: #aaa;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover,
.close-btn:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.modal-body {
padding: 20px;
font-size: 16px;
line-height: 1.5;
}
.modal-body a {
color: #0079E8;
text-decoration: none;
}
.modal-body a:hover {
text-decoration: underline;
}