Created
March 19, 2025 22:46
-
-
Save keon/26f52515ba8d201ba0fa95ec7bbcba80 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Function to create and manage the multi-step guidance and chat | |
function createSupportSystem() { | |
// Add styles for all components | |
const style = document.createElement('style'); | |
style.textContent = ` | |
/* General tooltip/modal styles */ | |
.guidance-tooltip { | |
position: fixed; | |
background-color: #0099CC; | |
color: white; | |
padding: 15px 20px; | |
border-radius: 8px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); | |
font-family: 'Styrene', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
font-size: 14px; | |
z-index: 9999; | |
animation: pulse 2s infinite alternate; | |
width: 280px; | |
} | |
.guidance-tooltip-arrow { | |
position: absolute; | |
width: 0; | |
height: 0; | |
} | |
.guidance-tooltip-title { | |
font-weight: bold; | |
font-size: 16px; | |
margin-bottom: 8px; | |
display: flex; | |
justify-content: space-between; | |
} | |
.guidance-tooltip-close { | |
cursor: pointer; | |
font-size: 18px; | |
} | |
.guidance-tooltip-content { | |
margin-bottom: 12px; | |
line-height: 1.4; | |
} | |
.guidance-tooltip-btn { | |
background-color: white; | |
color: #0099CC; | |
border: none; | |
border-radius: 4px; | |
padding: 8px 16px; | |
font-weight: bold; | |
cursor: pointer; | |
transition: background-color 0.2s; | |
} | |
.guidance-tooltip-btn:hover { | |
background-color: #f0f0f0; | |
} | |
/* Chat interface styles */ | |
#support-chat-button { | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
width: 60px; | |
height: 60px; | |
background-color: #0099CC; | |
border-radius: 50%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | |
z-index: 9998; | |
transition: all 0.3s ease; | |
animation: pulse 2s infinite alternate; | |
} | |
#support-chat-icon { | |
color: white; | |
font-size: 24px; | |
} | |
#support-chat-window { | |
position: fixed; | |
bottom: 90px; | |
right: 20px; | |
width: 320px; | |
height: 400px; | |
background-color: white; | |
border-radius: 12px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
display: none; | |
flex-direction: column; | |
overflow: hidden; | |
z-index: 9997; | |
font-family: 'Styrene', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
} | |
#support-chat-header { | |
background-color: #0099CC; | |
color: white; | |
padding: 12px 16px; | |
font-weight: bold; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
#support-chat-close { | |
cursor: pointer; | |
font-size: 18px; | |
} | |
#support-chat-messages { | |
flex: 1; | |
padding: 12px; | |
overflow-y: auto; | |
display: flex; | |
flex-direction: column; | |
gap: 8px; | |
background-color: #f7f7f7; | |
} | |
.chat-message { | |
max-width: 80%; | |
padding: 8px 12px; | |
border-radius: 16px; | |
margin-bottom: 4px; | |
word-break: break-word; | |
} | |
.user-message { | |
align-self: flex-end; | |
background-color: #0099CC; | |
color: white; | |
border-bottom-right-radius: 4px; | |
} | |
.assistant-message { | |
align-self: flex-start; | |
background-color: #eaeaea; | |
color: #333; | |
border-bottom-left-radius: 4px; | |
} | |
#support-chat-input-container { | |
padding: 12px; | |
border-top: 1px solid #eaeaea; | |
display: flex; | |
gap: 8px; | |
background-color: white; | |
} | |
#support-chat-input { | |
flex: 1; | |
padding: 8px 12px; | |
border: 1px solid #ddd; | |
border-radius: 16px; | |
outline: none; | |
font-family: inherit; | |
} | |
#support-chat-submit { | |
background-color: #0099CC; | |
color: white; | |
border: none; | |
border-radius: 50%; | |
width: 32px; | |
height: 32px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
} | |
/* Highlight styles */ | |
.menu-highlight { | |
background-color: rgba(0, 153, 204, 0.2) !important; | |
border-left: 3px solid #0099CC !important; | |
position: relative; | |
animation: highlight-pulse 2s infinite alternate; | |
} | |
.buy-credits-highlight { | |
position: relative; | |
animation: highlight-button 2s infinite alternate; | |
box-shadow: 0 0 0 4px rgba(0, 153, 204, 0.4) !important; | |
} | |
/* Animation keyframes */ | |
@keyframes pulse { | |
0% { box-shadow: 0 4px 12px rgba(0, 153, 204, 0.4); } | |
100% { box-shadow: 0 4px 20px rgba(0, 153, 204, 0.8); } | |
} | |
@keyframes highlight-pulse { | |
0% { background-color: rgba(0, 153, 204, 0.1) !important; } | |
100% { background-color: rgba(0, 153, 204, 0.3) !important; } | |
} | |
@keyframes highlight-button { | |
0% { transform: scale(1); box-shadow: 0 0 0 2px rgba(0, 153, 204, 0.4) !important; } | |
100% { transform: scale(1.05); box-shadow: 0 0 0 4px rgba(0, 153, 204, 0.7) !important; } | |
} | |
/* Overlay styles */ | |
.guidance-overlay { | |
position: fixed; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: rgba(0, 0, 0, 0.5); | |
z-index: 9990; | |
pointer-events: none; | |
} | |
.guidance-spotlight { | |
position: absolute; | |
border-radius: 8px; | |
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7); | |
pointer-events: auto; | |
} | |
`; | |
document.head.appendChild(style); | |
// Create chat interface | |
function createChatInterface() { | |
// Create chat button | |
const chatButton = document.createElement('div'); | |
chatButton.id = 'support-chat-button'; | |
chatButton.innerHTML = `<div id="support-chat-icon">💬</div>`; | |
document.body.appendChild(chatButton); | |
// Create chat window | |
const chatWindow = document.createElement('div'); | |
chatWindow.id = 'support-chat-window'; | |
chatWindow.innerHTML = ` | |
<div id="support-chat-header"> | |
<div>Support Chat</div> | |
<div id="support-chat-close">×</div> | |
</div> | |
<div id="support-chat-messages"> | |
<div class="chat-message assistant-message">Hello! I'm your support assistant. How can I help you today?</div> | |
</div> | |
<div id="support-chat-input-container"> | |
<input type="text" id="support-chat-input" placeholder="Type your message..."> | |
<button id="support-chat-submit">➤</button> | |
</div> | |
`; | |
document.body.appendChild(chatWindow); | |
// Toggle chat window | |
chatButton.addEventListener('click', () => { | |
const isVisible = chatWindow.style.display === 'flex'; | |
chatWindow.style.display = isVisible ? 'none' : 'flex'; | |
}); | |
// Close chat window | |
document.getElementById('support-chat-close').addEventListener('click', () => { | |
chatWindow.style.display = 'none'; | |
}); | |
// Send message on submit button click | |
document.getElementById('support-chat-submit').addEventListener('click', handleChatSubmit); | |
// Send message on Enter key | |
document.getElementById('support-chat-input').addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
handleChatSubmit(); | |
} | |
}); | |
} | |
// Handle chat message submission | |
function handleChatSubmit() { | |
const inputField = document.getElementById('support-chat-input'); | |
const message = inputField.value.trim(); | |
if (message) { | |
// Add user message to chat | |
addChatMessage(message, true); | |
// Clear input field | |
inputField.value = ''; | |
// Process the message | |
processUserMessage(message); | |
} | |
} | |
// Add a message to the chat window | |
function addChatMessage(content, isUser) { | |
const messagesContainer = document.getElementById('support-chat-messages'); | |
const messageDiv = document.createElement('div'); | |
messageDiv.className = `chat-message ${isUser ? 'user-message' : 'assistant-message'}`; | |
messageDiv.textContent = content; | |
messagesContainer.appendChild(messageDiv); | |
// Scroll to the bottom | |
messagesContainer.scrollTop = messagesContainer.scrollHeight; | |
} | |
// Process user message and determine response | |
function processUserMessage(message) { | |
// Check if message contains the trigger phrase | |
if (message.toLowerCase().includes('my api stopped working')) { | |
setTimeout(() => { | |
addChatMessage("I found the issue. Your API access is likely suspended due to low credit balance. Let me help you fix this.", false); | |
setTimeout(() => { | |
addChatMessage("I'll guide you to the Billing page where you can add more credits to resume your API access.", false); | |
// Start the guidance process after a short delay | |
setTimeout(startGuidance, 1000); | |
}, 1000); | |
}, 1000); | |
} else { | |
// Generic responses for other messages | |
setTimeout(() => { | |
addChatMessage("I'm here to help. If you're experiencing issues with your API, please let me know by saying 'my API stopped working'.", false); | |
}, 500); | |
} | |
} | |
// Create an overlay with a spotlight | |
function createOverlay(targetElement) { | |
// Remove any existing overlay | |
const existingOverlay = document.getElementById('guidance-overlay'); | |
if (existingOverlay) existingOverlay.remove(); | |
// Create new overlay | |
const overlay = document.createElement('div'); | |
overlay.id = 'guidance-overlay'; | |
overlay.className = 'guidance-overlay'; | |
document.body.appendChild(overlay); | |
if (targetElement) { | |
const rect = targetElement.getBoundingClientRect(); | |
// Create spotlight | |
const spotlight = document.createElement('div'); | |
spotlight.className = 'guidance-spotlight'; | |
spotlight.style.top = `${rect.top - 5}px`; | |
spotlight.style.left = `${rect.left - 5}px`; | |
spotlight.style.width = `${rect.width + 10}px`; | |
spotlight.style.height = `${rect.height + 10}px`; | |
overlay.appendChild(spotlight); | |
} | |
return overlay; | |
} | |
// Create and show a tooltip | |
function showTooltip(targetElement, position, title, content, buttonText, buttonAction) { | |
// Remove any existing tooltip | |
const existingTooltip = document.getElementById('guidance-tooltip'); | |
if (existingTooltip) existingTooltip.remove(); | |
// Create tooltip | |
const tooltip = document.createElement('div'); | |
tooltip.id = 'guidance-tooltip'; | |
tooltip.className = 'guidance-tooltip'; | |
tooltip.innerHTML = ` | |
<div class="guidance-tooltip-title"> | |
<span>${title}</span> | |
<span class="guidance-tooltip-close">×</span> | |
</div> | |
<div class="guidance-tooltip-content">${content}</div> | |
${buttonText ? `<button class="guidance-tooltip-btn">${buttonText}</button>` : ''} | |
`; | |
document.body.appendChild(tooltip); | |
// Position the tooltip relative to the target | |
const rect = targetElement.getBoundingClientRect(); | |
const arrow = document.createElement('div'); | |
arrow.className = 'guidance-tooltip-arrow'; | |
tooltip.appendChild(arrow); | |
switch (position) { | |
case 'right': | |
tooltip.style.left = `${rect.right + 15}px`; | |
tooltip.style.top = `${rect.top + rect.height/2 - tooltip.offsetHeight/2}px`; | |
arrow.style.left = '-8px'; | |
arrow.style.top = '50%'; | |
arrow.style.transform = 'translateY(-50%)'; | |
arrow.style.borderTop = '8px solid transparent'; | |
arrow.style.borderBottom = '8px solid transparent'; | |
arrow.style.borderRight = '8px solid #0099CC'; | |
break; | |
case 'bottom': | |
tooltip.style.left = `${rect.left + rect.width/2 - tooltip.offsetWidth/2}px`; | |
tooltip.style.top = `${rect.bottom + 15}px`; | |
arrow.style.top = '-8px'; | |
arrow.style.left = '50%'; | |
arrow.style.transform = 'translateX(-50%)'; | |
arrow.style.borderLeft = '8px solid transparent'; | |
arrow.style.borderRight = '8px solid transparent'; | |
arrow.style.borderBottom = '8px solid #0099CC'; | |
break; | |
case 'top': | |
tooltip.style.left = `${rect.left + rect.width/2 - tooltip.offsetWidth/2}px`; | |
tooltip.style.top = `${rect.top - tooltip.offsetHeight - 15}px`; | |
arrow.style.bottom = '-8px'; | |
arrow.style.left = '50%'; | |
arrow.style.transform = 'translateX(-50%)'; | |
arrow.style.borderLeft = '8px solid transparent'; | |
arrow.style.borderRight = '8px solid transparent'; | |
arrow.style.borderTop = '8px solid #0099CC'; | |
break; | |
case 'left': | |
tooltip.style.left = `${rect.left - tooltip.offsetWidth - 15}px`; | |
tooltip.style.top = `${rect.top + rect.height/2 - tooltip.offsetHeight/2}px`; | |
arrow.style.right = '-8px'; | |
arrow.style.top = '50%'; | |
arrow.style.transform = 'translateY(-50%)'; | |
arrow.style.borderTop = '8px solid transparent'; | |
arrow.style.borderBottom = '8px solid transparent'; | |
arrow.style.borderLeft = '8px solid #0099CC'; | |
break; | |
} | |
// Add event listeners | |
tooltip.querySelector('.guidance-tooltip-close').addEventListener('click', () => { | |
tooltip.remove(); | |
const overlay = document.getElementById('guidance-overlay'); | |
if (overlay) overlay.remove(); | |
// Remove any highlights | |
document.querySelectorAll('.menu-highlight').forEach(el => { | |
el.classList.remove('menu-highlight'); | |
}); | |
document.querySelectorAll('.buy-credits-highlight').forEach(el => { | |
el.classList.remove('buy-credits-highlight'); | |
}); | |
}); | |
if (buttonText) { | |
tooltip.querySelector('.guidance-tooltip-btn').addEventListener('click', () => { | |
buttonAction(); | |
}); | |
} | |
return tooltip; | |
} | |
// Highlight the Billing menu item | |
function highlightBillingMenu() { | |
const billingMenuItems = Array.from(document.querySelectorAll('a')) | |
.filter(a => a.textContent.trim() === 'Billing'); | |
if (billingMenuItems.length > 0) { | |
const billingMenuItem = billingMenuItems[0]; | |
// Highlight the menu item | |
billingMenuItem.classList.add('menu-highlight'); | |
// Create overlay with spotlight on the menu item | |
createOverlay(billingMenuItem); | |
// Show tooltip next to the menu item | |
showTooltip( | |
billingMenuItem, | |
'right', | |
'Low Credit Balance', | |
'Your API has stopped working because your credit balance is too low. Click on Billing to add more credits.', | |
'Continue', | |
() => { | |
// Simulate click on billing menu | |
billingMenuItem.click(); | |
// Wait for page to load, then proceed to next step | |
setTimeout(highlightBuyCreditsButton, 1000); | |
} | |
); | |
// Scroll menu into view if needed | |
billingMenuItem.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
} | |
} | |
// Highlight the Buy Credits button | |
function highlightBuyCreditsButton() { | |
// Remove old overlay and highlights | |
const overlay = document.getElementById('guidance-overlay'); | |
if (overlay) overlay.remove(); | |
document.querySelectorAll('.menu-highlight').forEach(el => { | |
el.classList.remove('menu-highlight'); | |
}); | |
// Find the Buy Credits button | |
setTimeout(() => { | |
const buyCreditsButton = Array.from(document.querySelectorAll('button')) | |
.find(btn => btn.textContent.trim() === 'Buy credits'); | |
if (buyCreditsButton) { | |
// Highlight the button | |
buyCreditsButton.classList.add('buy-credits-highlight'); | |
// Create overlay with spotlight on the button | |
createOverlay(buyCreditsButton); | |
// Show tooltip next to the button | |
showTooltip( | |
buyCreditsButton, | |
'bottom', | |
'Purchase More Credits', | |
'Your current balance is only <strong>$17.16</strong>. You need to add more credits to reactivate your API access.', | |
'Got it', | |
() => { | |
// Remove the tooltip and overlay | |
const tooltip = document.getElementById('guidance-tooltip'); | |
if (tooltip) tooltip.remove(); | |
const overlay = document.getElementById('guidance-overlay'); | |
if (overlay) overlay.remove(); | |
// Remove highlights | |
buyCreditsButton.classList.remove('buy-credits-highlight'); | |
// Add a final message to the chat | |
addChatMessage("Once you've added more credits, your API access will be restored automatically. Let me know if you need any further assistance!", false); | |
} | |
); | |
// Scroll button into view if needed | |
buyCreditsButton.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
} | |
}, 500); | |
} | |
// Start the guidance process | |
function startGuidance() { | |
// Check if we're already on the billing page | |
const isBillingPage = window.location.pathname.includes('/settings/billing'); | |
if (isBillingPage) { | |
// Skip directly to highlighting the buy credits button | |
highlightBuyCreditsButton(); | |
} else { | |
// Start with highlighting the billing menu | |
highlightBillingMenu(); | |
} | |
} | |
// Create the chat interface | |
createChatInterface(); | |
} | |
// Run the function | |
(function() { | |
// Try several times to make sure it runs after everything is loaded | |
setTimeout(createSupportSystem, 1000); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment