| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"/> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| <title>New Age β Internal AI Support</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"> |
| <style> |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| |
| :root { |
| --bg: #F5F4F0; |
| --surface: #FFFFFF; |
| --surface-2: #F0EEE9; |
| --border: #E2E0DA; |
| --border-strong:#C8C6BF; |
| --text: #1A1916; |
| --muted: #6B6A65; |
| --hint: #9E9C97; |
| --accent: #1A1916; |
| --accent-fg: #FFFFFF; |
| --user-bubble: #1A1916; |
| --user-fg: #FFFFFF; |
| --bot-bubble: #FFFFFF; |
| --bot-fg: #1A1916; |
| --radius: 12px; |
| --radius-sm: 8px; |
| --radius-xs: 6px; |
| --shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.06); |
| } |
| |
| body { |
| font-family: 'DM Sans', sans-serif; |
| background: var(--bg); |
| height: 100vh; |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| } |
| |
| |
| .navbar { |
| background: var(--surface); |
| border-bottom: 1px solid var(--border); |
| padding: 0 1.5rem; |
| height: 56px; |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| flex-shrink: 0; |
| z-index: 10; |
| } |
| |
| .navbar-left { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| |
| .logo-mark { |
| width: 32px; |
| height: 32px; |
| background: var(--accent); |
| border-radius: var(--radius-xs); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .logo-mark svg { |
| width: 17px; |
| height: 17px; |
| stroke: #fff; |
| fill: none; |
| stroke-width: 1.8; |
| stroke-linecap: round; |
| stroke-linejoin: round; |
| } |
| |
| .brand-name { |
| font-size: 0.9375rem; |
| font-weight: 600; |
| color: var(--text); |
| letter-spacing: -0.01em; |
| } |
| |
| .brand-tag { |
| font-size: 0.75rem; |
| color: var(--hint); |
| font-weight: 400; |
| margin-left: 2px; |
| } |
| |
| .navbar-right { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .nav-btn { |
| height: 32px; |
| padding: 0 12px; |
| border: 1px solid var(--border); |
| border-radius: var(--radius-xs); |
| background: none; |
| font-family: 'DM Sans', sans-serif; |
| font-size: 0.8125rem; |
| color: var(--muted); |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| transition: border-color 0.15s, color 0.15s; |
| } |
| |
| .nav-btn:hover { border-color: var(--border-strong); color: var(--text); } |
| |
| .nav-btn svg { |
| width: 14px; |
| height: 14px; |
| stroke: currentColor; |
| fill: none; |
| stroke-width: 1.8; |
| stroke-linecap: round; |
| stroke-linejoin: round; |
| } |
| |
| .user-pill { |
| height: 32px; |
| padding: 0 10px; |
| background: var(--surface-2); |
| border: 1px solid var(--border); |
| border-radius: 99px; |
| display: flex; |
| align-items: center; |
| gap: 7px; |
| font-size: 0.8125rem; |
| color: var(--text); |
| font-weight: 500; |
| } |
| |
| .avatar { |
| width: 20px; |
| height: 20px; |
| background: var(--accent); |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 0.625rem; |
| color: #fff; |
| font-weight: 600; |
| } |
| |
| |
| .main { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| max-width: 800px; |
| width: 100%; |
| margin: 0 auto; |
| padding: 0 1rem; |
| overflow: hidden; |
| } |
| |
| |
| .messages { |
| flex: 1; |
| overflow-y: auto; |
| padding: 1.5rem 0; |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| scrollbar-width: thin; |
| scrollbar-color: var(--border) transparent; |
| } |
| |
| .messages::-webkit-scrollbar { width: 4px; } |
| .messages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } |
| |
| |
| .welcome { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| text-align: center; |
| padding: 2rem; |
| gap: 1rem; |
| } |
| |
| .welcome-icon { |
| width: 48px; |
| height: 48px; |
| background: var(--surface); |
| border: 1px solid var(--border); |
| border-radius: var(--radius-sm); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .welcome-icon svg { |
| width: 24px; |
| height: 24px; |
| stroke: var(--muted); |
| fill: none; |
| stroke-width: 1.6; |
| stroke-linecap: round; |
| stroke-linejoin: round; |
| } |
| |
| .welcome h2 { |
| font-size: 1.25rem; |
| font-weight: 600; |
| color: var(--text); |
| letter-spacing: -0.01em; |
| } |
| |
| .welcome p { |
| font-size: 0.875rem; |
| color: var(--muted); |
| max-width: 380px; |
| line-height: 1.6; |
| } |
| |
| |
| .suggestions { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 8px; |
| justify-content: center; |
| margin-top: 0.5rem; |
| } |
| |
| .chip { |
| padding: 7px 14px; |
| background: var(--surface); |
| border: 1px solid var(--border); |
| border-radius: 99px; |
| font-size: 0.8125rem; |
| color: var(--muted); |
| cursor: pointer; |
| font-family: 'DM Sans', sans-serif; |
| transition: border-color 0.15s, color 0.15s, background 0.15s; |
| } |
| |
| .chip:hover { |
| border-color: var(--border-strong); |
| color: var(--text); |
| background: var(--surface-2); |
| } |
| |
| |
| .msg-row { |
| display: flex; |
| gap: 10px; |
| animation: fadeUp 0.2s ease both; |
| } |
| |
| @keyframes fadeUp { |
| from { opacity: 0; transform: translateY(8px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .msg-row.user { flex-direction: row-reverse; } |
| |
| .msg-avatar { |
| width: 28px; |
| height: 28px; |
| border-radius: 50%; |
| flex-shrink: 0; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 0.6875rem; |
| font-weight: 600; |
| margin-top: 2px; |
| } |
| |
| .msg-avatar.bot { |
| background: var(--accent); |
| color: #fff; |
| } |
| |
| .msg-avatar.user-av { |
| background: var(--surface-2); |
| border: 1px solid var(--border); |
| color: var(--muted); |
| } |
| |
| .msg-content { max-width: 72%; display: flex; flex-direction: column; gap: 4px; } |
| .msg-row.user .msg-content { align-items: flex-end; } |
| |
| .msg-sender { |
| font-size: 0.75rem; |
| color: var(--hint); |
| font-weight: 500; |
| padding: 0 4px; |
| } |
| |
| .bubble { |
| padding: 10px 14px; |
| border-radius: var(--radius); |
| font-size: 0.875rem; |
| line-height: 1.6; |
| word-break: break-word; |
| } |
| |
| .bubble.bot { |
| background: var(--bot-bubble); |
| color: var(--bot-fg); |
| border: 1px solid var(--border); |
| border-top-left-radius: 4px; |
| } |
| |
| .bubble.user { |
| background: var(--user-bubble); |
| color: var(--user-fg); |
| border-top-right-radius: 4px; |
| } |
| |
| |
| .bubble img { |
| max-width: 100%; |
| border-radius: var(--radius-xs); |
| display: block; |
| margin-top: 6px; |
| } |
| |
| .bubble .img-only { |
| max-width: 260px; |
| border-radius: var(--radius-sm); |
| } |
| |
| .msg-time { |
| font-size: 0.6875rem; |
| color: var(--hint); |
| padding: 0 4px; |
| } |
| |
| |
| .typing-bubble { |
| padding: 12px 16px; |
| background: var(--bot-bubble); |
| border: 1px solid var(--border); |
| border-radius: var(--radius); |
| border-top-left-radius: 4px; |
| display: flex; |
| gap: 4px; |
| align-items: center; |
| } |
| |
| .typing-bubble span { |
| width: 6px; |
| height: 6px; |
| background: var(--hint); |
| border-radius: 50%; |
| animation: blink 1.2s ease infinite; |
| } |
| |
| .typing-bubble span:nth-child(2) { animation-delay: 0.2s; } |
| .typing-bubble span:nth-child(3) { animation-delay: 0.4s; } |
| |
| @keyframes blink { |
| 0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); } |
| 40% { opacity: 1; transform: scale(1); } |
| } |
| |
| |
| .image-preview-bar { |
| display: none; |
| padding: 8px 0; |
| gap: 8px; |
| flex-wrap: wrap; |
| } |
| |
| .image-preview-bar.visible { display: flex; } |
| |
| .img-thumb { |
| position: relative; |
| width: 60px; |
| height: 60px; |
| } |
| |
| .img-thumb img { |
| width: 60px; |
| height: 60px; |
| object-fit: cover; |
| border-radius: var(--radius-xs); |
| border: 1px solid var(--border); |
| } |
| |
| .img-remove { |
| position: absolute; |
| top: -6px; |
| right: -6px; |
| width: 18px; |
| height: 18px; |
| background: var(--text); |
| color: #fff; |
| border: none; |
| border-radius: 50%; |
| font-size: 10px; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| line-height: 1; |
| } |
| |
| |
| .input-area { |
| padding: 0.75rem 0 1rem; |
| flex-shrink: 0; |
| } |
| |
| .input-box { |
| background: var(--surface); |
| border: 1px solid var(--border); |
| border-radius: var(--radius); |
| box-shadow: var(--shadow); |
| transition: border-color 0.15s; |
| overflow: hidden; |
| } |
| |
| .input-box:focus-within { border-color: var(--border-strong); } |
| |
| .input-top { |
| display: flex; |
| align-items: flex-end; |
| gap: 8px; |
| padding: 10px 12px; |
| } |
| |
| textarea { |
| flex: 1; |
| border: none; |
| outline: none; |
| font-family: 'DM Sans', sans-serif; |
| font-size: 0.875rem; |
| color: var(--text); |
| background: transparent; |
| resize: none; |
| min-height: 24px; |
| max-height: 160px; |
| line-height: 1.6; |
| padding: 0; |
| } |
| |
| textarea::placeholder { color: var(--hint); } |
| |
| .input-bottom { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 6px 12px 10px; |
| border-top: 1px solid var(--border); |
| } |
| |
| .input-actions { display: flex; align-items: center; gap: 4px; } |
| |
| .action-btn { |
| width: 32px; |
| height: 32px; |
| border: none; |
| background: none; |
| border-radius: var(--radius-xs); |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| color: var(--hint); |
| transition: background 0.15s, color 0.15s; |
| } |
| |
| .action-btn:hover { background: var(--surface-2); color: var(--muted); } |
| |
| .action-btn svg { |
| width: 16px; |
| height: 16px; |
| stroke: currentColor; |
| fill: none; |
| stroke-width: 1.8; |
| stroke-linecap: round; |
| stroke-linejoin: round; |
| } |
| |
| .send-btn { |
| height: 32px; |
| padding: 0 14px; |
| background: var(--accent); |
| color: var(--accent-fg); |
| border: none; |
| border-radius: var(--radius-xs); |
| font-family: 'DM Sans', sans-serif; |
| font-size: 0.8125rem; |
| font-weight: 500; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| transition: opacity 0.15s; |
| } |
| |
| .send-btn:hover { opacity: 0.85; } |
| .send-btn:disabled { opacity: 0.4; cursor: not-allowed; } |
| |
| .send-btn svg { |
| width: 14px; |
| height: 14px; |
| stroke: currentColor; |
| fill: none; |
| stroke-width: 2; |
| stroke-linecap: round; |
| stroke-linejoin: round; |
| } |
| |
| .disclaimer { |
| text-align: center; |
| font-size: 0.6875rem; |
| color: var(--hint); |
| margin-top: 8px; |
| } |
| |
| |
| #newChatBtn { display: none; } |
| |
| |
| .conf-badge { |
| display: inline-flex; |
| align-items: center; |
| gap: 4px; |
| font-size: 0.6875rem; |
| color: #B91C1C; |
| background: #FEF2F2; |
| border: 1px solid #FECACA; |
| border-radius: 99px; |
| padding: 2px 8px; |
| margin-top: 4px; |
| } |
| |
| |
| #imageInput { display: none; } |
| |
| |
| .nar-inline-link { |
| display: inline-block; |
| margin-top: 6px; |
| color: #4f8ef7; |
| font-size: 0.875rem; |
| font-weight: 500; |
| text-decoration: none; |
| } |
| .nar-inline-link:hover { text-decoration: underline; } |
| |
| </style> |
| |
| <script type="text/javascript"> |
| (function(c,l,a,r,i,t,y){ |
| c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; |
| t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; |
| y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); |
| })(window, document, "clarity", "script", "x13hxsbvnw"); |
| </script> |
| </head> |
| <body> |
|
|
| |
| <nav class="navbar"> |
| <div class="navbar-left"> |
| <div class="logo-mark" aria-hidden="true"> |
| <svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg> |
| </div> |
| <div> |
| <span class="brand-name">New Age</span> |
| <span class="brand-tag">/ AI Support</span> |
| </div> |
| </div> |
|
|
| <div class="navbar-right"> |
| <button class="nav-btn" id="newChatBtn" onclick="startNewChat()"> |
| <svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> |
| New chat |
| </button> |
| <div class="user-pill"> |
| <div class="avatar" id="userAvatar">A</div> |
| <span id="userName">Admin</span> |
| </div> |
| <button class="nav-btn" id="adminBtn" onclick="window.location.href='/admin'" title="Admin Panel" style="display:none"> |
| <svg viewBox="0 0 24 24"><path d="M12 2a10 10 0 1 0 0 20A10 10 0 0 0 12 2z"/><path d="M12 8v4l3 3"/></svg> |
| Admin |
| </button> |
| <button class="nav-btn" onclick="logout()" title="Logout"> |
| <svg viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg> |
| Logout |
| </button> |
| </div> |
| </nav> |
|
|
| |
| <div class="main"> |
| <div class="messages" id="messages"> |
|
|
| |
| <div class="welcome" id="welcome"> |
| <div class="welcome-icon"> |
| <svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg> |
| </div> |
| <h2>How can I help you today?</h2> |
| <p>Ask me anything about New Age β teams, policies, divisions, contacts, or NAR requests.</p> |
| <div class="suggestions"> |
| <button class="chip" onclick="useChip(this)">What is NAR?</button> |
| <button class="chip" onclick="useChip(this)">Who should I contact for IT issues?</button> |
| <button class="chip" onclick="useChip(this)">What are the office timings?</button> |
| <button class="chip" onclick="useChip(this)">How do I submit a reimbursement?</button> |
| <button class="chip" onclick="useChip(this)">Tell me about New Age divisions</button> |
| </div> |
| </div> |
|
|
| </div> |
|
|
| |
| <div class="image-preview-bar" id="imagePreviewBar"></div> |
|
|
| |
| <div class="input-area"> |
| <div class="input-box"> |
| <div class="input-top"> |
| <textarea |
| id="msgInput" |
| placeholder="Ask anything about New Age..." |
| rows="1" |
| onkeydown="handleKey(event)" |
| oninput="autoResize(this)" |
| ></textarea> |
| </div> |
| <div class="input-bottom"> |
| <div class="input-actions"> |
| |
| <button class="action-btn" title="Attach image" onclick="document.getElementById('imageInput').click()"> |
| <svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg> |
| </button> |
| <input type="file" id="imageInput" accept="image/*" multiple onchange="handleImages(this)"/> |
| <span style="font-size:0.75rem; color:var(--hint); margin-left:4px;">Add image for context</span> |
| </div> |
| <button class="send-btn" id="sendBtn" onclick="sendMessage()" disabled> |
| Send |
| <svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg> |
| </button> |
| </div> |
| </div> |
| <p class="disclaimer">New Age AI will not share confidential information such as salary, revenue, or profit.</p> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const pendingImages = []; |
| let isTyping = false; |
| let msgCount = 0; |
| |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| |
| const savedUser = sessionStorage.getItem('loggedInUser'); |
| if (!savedUser) { |
| |
| window.location.href = '/'; |
| return; |
| } |
| const user = JSON.parse(savedUser); |
| document.getElementById('userName').textContent = user.name; |
| document.getElementById('userAvatar').textContent = user.initials; |
| |
| |
| const adminRoles = ['Manager', 'Admin', 'HR', 'Operations']; |
| if (adminRoles.includes(user.role)) { |
| document.getElementById('adminBtn').style.display = ''; |
| } |
| |
| |
| document.getElementById('msgInput').addEventListener('input', updateSendBtn); |
| }); |
| |
| |
| function updateSendBtn() { |
| const hasText = document.getElementById('msgInput').value.trim().length > 0; |
| const hasImg = pendingImages.length > 0; |
| document.getElementById('sendBtn').disabled = !(hasText || hasImg); |
| } |
| |
| |
| function autoResize(el) { |
| el.style.height = 'auto'; |
| el.style.height = Math.min(el.scrollHeight, 160) + 'px'; |
| updateSendBtn(); |
| } |
| |
| |
| function handleKey(e) { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| if (!document.getElementById('sendBtn').disabled) sendMessage(); |
| } |
| } |
| |
| |
| function handleImages(input) { |
| const files = Array.from(input.files); |
| files.forEach(file => { |
| const reader = new FileReader(); |
| reader.onload = e => { |
| pendingImages.push({ file, dataUrl: e.target.result }); |
| renderImagePreviews(); |
| updateSendBtn(); |
| }; |
| reader.readAsDataURL(file); |
| }); |
| input.value = ''; |
| } |
| |
| function renderImagePreviews() { |
| const bar = document.getElementById('imagePreviewBar'); |
| bar.innerHTML = ''; |
| if (pendingImages.length === 0) { bar.classList.remove('visible'); return; } |
| bar.classList.add('visible'); |
| pendingImages.forEach((img, i) => { |
| const wrap = document.createElement('div'); |
| wrap.className = 'img-thumb'; |
| wrap.innerHTML = `<img src="${img.dataUrl}" alt="Preview"/><button class="img-remove" onclick="removeImage(${i})">β</button>`; |
| bar.appendChild(wrap); |
| }); |
| } |
| |
| function removeImage(i) { |
| pendingImages.splice(i, 1); |
| renderImagePreviews(); |
| updateSendBtn(); |
| } |
| |
| |
| async function sendMessage() { |
| const input = document.getElementById('msgInput'); |
| const text = input.value.trim(); |
| const images = [...pendingImages]; |
| |
| if (!text && images.length === 0) return; |
| if (isTyping) return; |
| |
| |
| if (text && text.length < 3) { |
| alert('Please type a proper question!'); |
| return; |
| } |
| |
| |
| const cacheKey = text.trim().toLowerCase(); |
| if (messageCache[cacheKey]) { |
| document.getElementById('welcome') && (document.getElementById('welcome').style.display = 'none'); |
| document.getElementById('newChatBtn').style.display = 'flex'; |
| addMessage('user', text, images); |
| document.getElementById('msgInput').value = ''; |
| document.getElementById('msgInput').style.height = 'auto'; |
| updateSendBtn(); |
| addMessage('bot', messageCache[cacheKey] + ' *(cached)*'); |
| return; |
| } |
| |
| |
| document.getElementById('welcome').style.display = 'none'; |
| document.getElementById('newChatBtn').style.display = 'flex'; |
| |
| |
| addMessage('user', text, images); |
| |
| |
| input.value = ''; |
| input.style.height = 'auto'; |
| pendingImages.length = 0; |
| renderImagePreviews(); |
| updateSendBtn(); |
| |
| |
| showTyping(); |
| await callGroqAPI(text, images); |
| } |
| |
| |
| function addMessage(role, text, images = []) { |
| msgCount++; |
| const msgs = document.getElementById('messages'); |
| const isUser = role === 'user'; |
| const row = document.createElement('div'); |
| row.className = `msg-row ${isUser ? 'user' : ''}`; |
| |
| const initials = isUser ? (document.getElementById('userAvatar').textContent || 'U') : 'NA'; |
| const now = new Date(); |
| const time = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
| |
| let imagesHtml = ''; |
| if (images.length > 0) { |
| imagesHtml = images.map(img => |
| `<img src="${img.dataUrl}" alt="Attached image" class="${!text ? 'img-only' : ''}"/>` |
| ).join(''); |
| } |
| |
| |
| let narCardHtml = ''; |
| let cleanText = text; |
| if (!isUser) { |
| const narStart = text.indexOf(':::nar-suggestion'); |
| if (narStart !== -1) { |
| const jsonStart = text.indexOf('{', narStart); |
| |
| let narEnd = text.indexOf(':::', narStart + 17); |
| if (narEnd === -1) narEnd = text.length; |
| if (jsonStart !== -1) { |
| |
| const jsonEnd = text.lastIndexOf('}', narEnd) + 1; |
| const jsonStr = text.slice(jsonStart, jsonEnd).trim(); |
| try { |
| narCardHtml = buildNarCard(JSON.parse(jsonStr)); |
| cleanText = text.slice(0, narStart).trim(); |
| } catch(e) { cleanText = text.slice(0, narStart).trim(); } |
| } |
| } |
| } |
| |
| const isConfidential = !isUser && /salary|revenue|profit|confidential/i.test(cleanText); |
| const confidentialBadge = isConfidential |
| ? `<span class="conf-badge">β Confidential info not disclosed</span>` : ''; |
| |
| row.innerHTML = ` |
| <div class="msg-avatar ${isUser ? 'user-av' : 'bot'}">${initials}</div> |
| <div class="msg-content"> |
| <span class="msg-sender">${isUser ? 'You' : 'New Age AI'}</span> |
| <div class="bubble ${isUser ? 'user' : 'bot'}"> |
| ${cleanText ? `<span>${isUser ? escHtml(cleanText) : renderText(cleanText)}</span>` : ''} |
| ${imagesHtml} |
| </div> |
| ${narCardHtml} |
| ${confidentialBadge} |
| <span class="msg-time">${time}</span> |
| </div> |
| `; |
| |
| msgs.appendChild(row); |
| msgs.scrollTop = msgs.scrollHeight; |
| } |
| |
| |
| function buildNarCard(data) { |
| const MONDAY_BOARD_URL = 'https://forms.monday.com/forms/37620e81b30597f2867f3cbdc8aa2e51?r=use1&Request+Type='; |
| return `<a href="${MONDAY_BOARD_URL}" target="_blank" class="nar-inline-link">β Submit ${data.title}</a>`; |
| } |
| |
| |
| function showTyping() { |
| isTyping = true; |
| document.getElementById('sendBtn').disabled = true; |
| const msgs = document.getElementById('messages'); |
| const row = document.createElement('div'); |
| row.className = 'msg-row'; |
| row.id = 'typingRow'; |
| row.innerHTML = ` |
| <div class="msg-avatar bot">NA</div> |
| <div class="msg-content"> |
| <span class="msg-sender">New Age AI</span> |
| <div class="typing-bubble"><span></span><span></span><span></span></div> |
| </div> |
| `; |
| msgs.appendChild(row); |
| msgs.scrollTop = msgs.scrollHeight; |
| } |
| |
| function hideTyping() { |
| isTyping = false; |
| const row = document.getElementById('typingRow'); |
| if (row) row.remove(); |
| updateSendBtn(); |
| } |
| |
| |
| function useChip(btn) { |
| document.getElementById('msgInput').value = btn.textContent; |
| autoResize(document.getElementById('msgInput')); |
| updateSendBtn(); |
| sendMessage(); |
| } |
| |
| |
| function startNewChat() { |
| const msgs = document.getElementById('messages'); |
| msgs.innerHTML = ` |
| <div class="welcome" id="welcome"> |
| <div class="welcome-icon"> |
| <svg viewBox="0 0 24 24" stroke="var(--muted)" fill="none" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" width="24" height="24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg> |
| </div> |
| <h2>How can I help you today?</h2> |
| <p>Ask me anything about New Age β teams, policies, divisions, contacts, or NAR requests.</p> |
| <div class="suggestions"> |
| <button class="chip" onclick="useChip(this)">What is NAR?</button> |
| <button class="chip" onclick="useChip(this)">Who should I contact for IT issues?</button> |
| <button class="chip" onclick="useChip(this)">What are the office timings?</button> |
| <button class="chip" onclick="useChip(this)">How do I submit a reimbursement?</button> |
| <button class="chip" onclick="useChip(this)">Tell me about New Age divisions</button> |
| </div> |
| </div>`; |
| document.getElementById('newChatBtn').style.display = 'none'; |
| msgCount = 0; |
| } |
| |
| |
| function logout() { |
| if (confirm('Are you sure you want to logout?')) { |
| sessionStorage.removeItem('loggedInUser'); |
| window.location.href = '/'; |
| } |
| } |
| |
| |
| function escHtml(str) { |
| return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>'); |
| } |
| |
| function renderText(str) { |
| |
| const narStart = str.indexOf(':::nar-suggestion'); |
| if (narStart !== -1) { |
| str = str.slice(0, narStart).trim(); |
| } |
| str = str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); |
| str = str.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>'); |
| str = str.replace(/\n/g, '<br>'); |
| return str; |
| } |
| |
| |
| |
| |
| |
| const NAR_TYPES = [ |
| { |
| id:1, title:'General Requests', category:'General', |
| keywords:['general','misc','other','help','info'], |
| fields:'β’ Request Title: Short summary of what you need\nβ’ Description: Explain your request in detail\nβ’ Priority: Low / Medium / High\nβ’ Any supporting documents: Attach if relevant' |
| }, |
| { |
| id:2, title:'Access Request', category:'IT', |
| keywords:['access','permission','login','account','tool','software','system','monday','slack','drive','github','credentials'], |
| fields:'β’ Tool / Platform Name: e.g. Monday.com, Slack, GitHub, Google Drive\nβ’ Type of Access Needed: View only / Edit / Admin\nβ’ Reason: Why do you need this access?\nβ’ Urgency: When do you need it by?' |
| }, |
| { |
| id:3, title:'Leave Request', category:'HR', |
| keywords:['leave','vacation','time off','holiday','sick','absence','pto','break'], |
| fields:'β’ Leave Type: Paid / Unpaid / Sick\nβ’ From Date: Start date of leave\nβ’ To Date: End date of leave\nβ’ Total Days: Number of working days\nβ’ Reason: Brief reason for leave\nβ’ Coverage Plan: Who will handle your work while you\'re away?' |
| }, |
| { |
| id:4, title:'Purchase Request', category:'Finance', |
| keywords:['buy','purchase','order','laptop','monitor','hardware','equipment','device','phone','keyboard','mouse','screen'], |
| fields:'β’ Item Name: What do you want to purchase?\nβ’ Quantity: How many units?\nβ’ Estimated Cost: Approximate price in INR\nβ’ Vendor / Link: Where to buy from (Amazon link etc.)\nβ’ Reason / Justification: Why is this needed for your work?\nβ’ Urgency: When do you need it?' |
| }, |
| { |
| id:5, title:'Payment Request', category:'Finance', |
| keywords:['payment','pay','invoice','reimbursement','expense','reimburse','refund','bill','money'], |
| fields:'β’ Payment Type: Reimbursement / Invoice / Vendor Payment\nβ’ Amount: Exact amount in INR\nβ’ Description: What was this expense for?\nβ’ Date of Expense: When was it incurred?\nβ’ Payment Method Used: UPI / Card / Cash\nβ’ Attachment: Upload receipt or invoice (mandatory)' |
| }, |
| { |
| id:6, title:'Contract Change', category:'Legal', |
| keywords:['contract','agreement','change','modify','update','terms','scope','role','rate','hours'], |
| fields:'β’ Current Contract Details: Your current role / rate / hours\nβ’ Requested Change: What exactly needs to change?\nβ’ Reason: Why is this change needed?\nβ’ Effective Date: From when should this apply?\nβ’ Supporting Evidence: Any email threads or agreements to attach?' |
| }, |
| { |
| id:7, title:'Billing / Subscription Concern', category:'Finance', |
| keywords:['billing','subscription','charge','overcharge','plan','renewal','pricing','overage'], |
| fields:'β’ Subscription / Service Name: Which tool or service?\nβ’ Issue Type: Overcharge / Wrong Plan / Unexpected Charge\nβ’ Amount in Question: How much was charged?\nβ’ Date of Charge: When did this appear?\nβ’ Expected Amount: What should it have been?\nβ’ Screenshot: Attach billing screenshot' |
| }, |
| { |
| id:8, title:'Subscription Cancellation Request', category:'Finance', |
| keywords:['cancel','cancellation','unsubscribe','stop subscription','terminate plan'], |
| fields:'β’ Service / Tool Name: What subscription to cancel?\nβ’ Current Plan: Which plan are you on?\nβ’ Reason for Cancellation: Why cancel?\nβ’ Last Required Date: When can it be cancelled from?\nβ’ Alternative: Is there a replacement tool being used?' |
| }, |
| { |
| id:9, title:'Data Report Blocker', category:'Ops', |
| keywords:['report blocked','data stuck','blocker','report issue','data error','dashboard broken','analytics issue'], |
| fields:'β’ Report / Dashboard Name: Which report is blocked?\nβ’ Platform: Monday.com / Google Sheets / Looker / Other\nβ’ Issue Description: What exactly is broken or missing?\nβ’ Since When: When did this blocker start?\nβ’ Impact: What work is being blocked because of this?\nβ’ Screenshot: Attach error or broken state screenshot' |
| }, |
| { |
| id:10, title:'Data Analysis and Reporting Request', category:'Ops', |
| keywords:['data analysis','reporting','analytics','dashboard','metrics','stats','report','insight','kpi'], |
| fields:'β’ Report Name / Title: What should this report be called?\nβ’ Data Source: Where is the data coming from?\nβ’ Metrics Needed: What KPIs or numbers do you need?\nβ’ Time Period: Date range for the report\nβ’ Format: Dashboard / Spreadsheet / PDF\nβ’ Deadline: When do you need this by?' |
| }, |
| { |
| id:11, title:'Grievance / Strategic Alignment Requests', category:'HR', |
| keywords:['grievance','complaint','alignment','strategy','concern','issue with team','unfair','conflict','disagreement'], |
| fields:'β’ Subject: One line summary of the grievance\nβ’ People Involved: Who is this regarding? (names/roles)\nβ’ Incident Date: When did this happen?\nβ’ Detailed Description: Explain the full situation factually\nβ’ What Resolution Are You Seeking?: What outcome do you want?\nβ’ Is this confidential?: Yes / No' |
| }, |
| { |
| id:12, title:'Onboard New Access', category:'IT', |
| keywords:['onboard','new joiner','new employee','new access','setup','new hire','joining','first day','create account'], |
| fields:'β’ New Member Name: Full name of the person joining\nβ’ Role / Designation: Their job title\nβ’ Start Date: When do they join?\nβ’ Tools Required: List all tools they need access to (Slack, Monday, Drive, GitHub etc.)\nβ’ Access Level: For each tool β View / Edit / Admin\nβ’ Reporting Manager: Who will they report to?' |
| }, |
| { |
| id:13, title:'Complaint', category:'HR', |
| keywords:['complaint','problem','unhappy','dissatisfied','bad experience','report problem'], |
| fields:'β’ Complaint Subject: Brief title\nβ’ Against Whom / What: Person, team, or process\nβ’ Date of Incident: When did this happen?\nβ’ Description: Explain clearly what happened\nβ’ Evidence: Any screenshots, messages, or documents?\nβ’ Desired Resolution: What do you want done about it?' |
| }, |
| { |
| id:14, title:'Offer Release Request', category:'HR', |
| keywords:['offer letter','offer release','job offer','send offer','hiring offer','appointment letter'], |
| fields:'β’ Candidate Name: Full name\nβ’ Role Being Offered: Job title\nβ’ Division: Product / Services\nβ’ Agreed Rate: Compensation as discussed\nβ’ Start Date: Expected joining date\nβ’ Contract Type: C1 / C2 / Freelance\nβ’ Approved By: Who approved this hire?' |
| }, |
| { |
| id:15, title:'Contract Termination', category:'Legal', |
| keywords:['terminate','termination','end contract','fire','exit','notice period','last day'], |
| fields:'β’ Person\'s Name: Who is being offboarded?\nβ’ Role: Their designation\nβ’ Last Working Day: Final date\nβ’ Reason for Termination: Performance / Mutual / Resignation / Other\nβ’ Notice Period Served: Yes / No / Waived\nβ’ Access Revocation Required: List tools to revoke (Slack, Monday, GitHub etc.)\nβ’ Final Payment Status: Settled / Pending' |
| }, |
| { |
| id:16, title:'Resignation Letter', category:'HR', |
| keywords:['resign','resignation','quit','leaving','notice','last working day','stepping down'], |
| fields:'β’ Your Name: Full name\nβ’ Current Role: Your designation\nβ’ Notice Period: How many weeks/days notice are you giving?\nβ’ Last Working Day: Your proposed final date\nβ’ Reason (Optional): Brief reason if comfortable sharing\nβ’ Handover Plan: What will you hand over and to whom?' |
| }, |
| { |
| id:17, title:'Job Requisition', category:'HR', |
| keywords:['hire','hiring','job opening','vacancy','recruit','new position','headcount','open role'], |
| fields:'β’ Role Title: What position to hire for?\nβ’ Division: Product / Services\nβ’ Number of Openings: How many people needed?\nβ’ Key Skills Required: Must-have skills\nβ’ Experience Level: Fresher / 1-2 yrs / 3-5 yrs / Senior\nβ’ Expected Start Date: When should they ideally join?\nβ’ Budget Range: Approximate compensation range\nβ’ Reason for Hire: New role / Replacement / Growth' |
| }, |
| { |
| id:18, title:'HR Support', category:'HR', |
| keywords:['hr','human resources','hr help','policy','payroll','hr query','documents','certificate','letter'], |
| fields:'β’ Query Type: Payroll / Document / Policy / Certificate / Other\nβ’ Description: What do you need help with?\nβ’ Document Needed (if any): Experience letter / Salary certificate / NOC etc.\nβ’ Urgency: When do you need this?\nβ’ Any Reference: Previous conversation or ticket number if applicable' |
| }, |
| { |
| id:19, title:'Project Request', category:'General', |
| keywords:['project','new project','start project','initiate','kick off','new work','client project'], |
| fields:'β’ Project Name: What to call this project?\nβ’ Project Type: Internal / Client / R&D\nβ’ Objective: What should this project achieve?\nβ’ Scope: What is in scope and out of scope?\nβ’ Team Required: Roles needed (dev, designer, tester etc.)\nβ’ Timeline: Expected start and end date\nβ’ Budget Estimate: If applicable' |
| }, |
| { |
| id:20, title:'Automation Request', category:'Ops', |
| keywords:['automate','automation','workflow','bot','script','automatic','zapier','make','n8n','monday automation'], |
| fields:'β’ What to Automate: Describe the current manual process\nβ’ Tools Involved: Which apps need to be connected?\nβ’ Trigger: What event should start the automation?\nβ’ Action: What should happen automatically?\nβ’ Expected Output: What\'s the end result?\nβ’ Priority: How critical is this automation?' |
| }, |
| { |
| id:21, title:'Process Change Request', category:'Ops', |
| keywords:['process','change process','improve process','workflow change','sop','procedure','operations'], |
| fields:'β’ Current Process: Describe how it works today\nβ’ Proposed Change: What should change and how?\nβ’ Reason: Why is this change needed?\nβ’ Impact: Who / what will be affected?\nβ’ Implementation Timeline: When should this go live?\nβ’ Owner: Who will be responsible for this new process?' |
| }, |
| { |
| id:22, title:'Root Cause Analysis', category:'Ops', |
| keywords:['root cause','rca','why did','investigation','post mortem','incident','bug cause','what went wrong'], |
| fields:'β’ Incident Title: Short name for what went wrong\nβ’ Date & Time: When did it happen?\nβ’ What Happened: Clear description of the issue\nβ’ Impact: What was affected (users, revenue, features)?\nβ’ Immediate Fix Applied: What was done to resolve it temporarily?\nβ’ Root Cause (if known): What caused it?\nβ’ Permanent Fix: What needs to be done to prevent recurrence?' |
| }, |
| { |
| id:23, title:'Emergency Protocol', category:'General', |
| keywords:['emergency','urgent','critical','asap','immediately','down','outage','crisis','production issue'], |
| fields:'β’ Emergency Type: App Down / Data Loss / Security Breach / Other\nβ’ Severity: P0 (total outage) / P1 (major) / P2 (partial)\nβ’ What Is Affected: Which app, feature, or system?\nβ’ Since When: When did this start?\nβ’ Current Impact: How many users affected?\nβ’ Steps Already Taken: What have you tried so far?\nβ’ Who Knows: Which team members are already aware?' |
| }, |
| { |
| id:24, title:'Growth Team Bug Report', category:'Growth', |
| keywords:['bug','error','crash','broken','not working','fix','glitch','app crash','defect'], |
| fields:'β’ App / Feature Name: Where is the bug?\nβ’ Bug Description: What is happening vs what should happen?\nβ’ Steps to Reproduce: Step-by-step how to trigger the bug\nβ’ Device / Platform: Android / iOS / Web + device model\nβ’ App Version: Which version are you on?\nβ’ Frequency: Always / Sometimes / Rarely\nβ’ Screenshot or Recording: Attach evidence' |
| }, |
| { |
| id:25, title:'Training Required', category:'HR', |
| keywords:['training','learn','course','skill','workshop','upskill','tutorial','mentorship','education'], |
| fields:'β’ Training Topic: What skill or subject?\nβ’ Training Type: Online course / Workshop / Internal session / Mentorship\nβ’ Why Needed: How will this help your work?\nβ’ Preferred Timeline: When do you want to complete it?\nβ’ Estimated Cost (if paid): Amount in INR\nβ’ Platform / Provider: Udemy / Coursera / YouTube / Other' |
| }, |
| { |
| id:26, title:'Meeting Request with CEO', category:'Management', |
| keywords:['ceo','meeting with ceo','talk to ceo','bhargav','leadership','executive','founder'], |
| fields:'β’ Your Name & Role: Who is requesting the meeting?\nβ’ Meeting Purpose: What do you want to discuss? (1-2 lines)\nβ’ Agenda Points: List the specific topics (max 3 recommended)\nβ’ Preferred Duration: 15 min / 30 min / 45 min\nβ’ Preferred Time Slot: Give 2-3 options\nβ’ Is this Urgent?: Yes / No β explain why if yes' |
| }, |
| { |
| id:27, title:'WFH Request', category:'HR', |
| keywords:['wfh','work from home','remote','work remotely','home office','apply for wfh','want to wfh'], |
| fields:'β’ Your Name: Full name\nβ’ WFH Date(s): Which day(s) do you want to WFH?\nβ’ Reason: Why are you requesting WFH?\nβ’ Will you be available on calls?: Yes / No\nβ’ Any impact on deliverables?: Mention if any deadlines are affected\nβ’ Manager Informed?: Yes / No' |
| }, |
| { |
| id:28, title:'Maintenance Task', category:'Ops', |
| keywords:['maintenance','maintain','upkeep','routine task','scheduled','infra','server maintenance'], |
| fields:'β’ Task Title: What needs to be maintained?\nβ’ System / Tool: Which server, app, or infrastructure?\nβ’ Type: Routine / Emergency / Upgrade\nβ’ Scheduled Date & Time: When should this run?\nβ’ Expected Downtime: Will there be any outage? How long?\nβ’ Who Will Execute: Person responsible\nβ’ Rollback Plan: What to do if something goes wrong?' |
| }, |
| ]; |
| |
| |
| function matchNarTypes(query) { |
| const q = query.toLowerCase(); |
| return NAR_TYPES |
| .map(n => ({ ...n, score: n.keywords.reduce((s,kw) => s + (q.includes(kw) ? kw.split(' ').length : 0), 0) })) |
| .filter(n => n.score > 0) |
| .sort((a,b) => b.score - a.score) |
| .slice(0, 3); |
| } |
| |
| |
| |
| |
| const chatHistory = []; |
| const messageCache = {}; |
| |
| async function callGroqAPI(userText, images = []) { |
| try { |
| let userContent = userText || ''; |
| if (images.length > 0) userContent += `\n[User attached ${images.length} image(s)]`; |
| |
| chatHistory.push({ role: 'user', content: userContent }); |
| |
| const response = await fetch('/api/chat', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ messages: chatHistory }) |
| }); |
| |
| const data = await response.json(); |
| |
| if (!data.success) { |
| throw new Error(data.message || 'Something went wrong'); |
| } |
| |
| const reply = data.reply; |
| console.log('RAW REPLY:', reply); |
| chatHistory.push({ role: 'assistant', content: reply }); |
| if (chatHistory.length > 20) chatHistory.splice(0, 2); |
| |
| hideTyping(); |
| addMessage('bot', reply); |
| |
| } catch (err) { |
| hideTyping(); |
| addMessage('bot', `Sorry, I ran into an error: ${err.message}. Please try again.`); |
| console.error('Chat API error:', err); |
| } |
| } |
| |
| </script> |
|
|
| </body> |
| </html> |