{"id":5193,"date":"2025-11-13T12:00:34","date_gmt":"2025-11-13T06:30:34","guid":{"rendered":"https:\/\/apicalclasses.com\/?p=5193"},"modified":"2025-12-04T12:56:29","modified_gmt":"2025-12-04T07:26:29","slug":"chat-box","status":"publish","type":"post","link":"https:\/\/apicalclasses.com\/?p=5193","title":{"rendered":"CHAT BOX"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" \/>\n  <title>Chat + Quiz Scoreboard (Combined)<\/title>\n  <style>\n    :root{--bg:#eef2f3;--card:#fff;--accent:#0077cc}\n    body{font-family:Arial,Helvetica,sans-serif;background:var(--bg);margin:0;padding:18px}\n    .container{max-width:1200px;margin:0 auto;display:grid;grid-template-columns:1fr 420px;gap:18px}\n    .card{background:var(--card);padding:14px;border-radius:10px;box-shadow:0 4px 12px rgba(0,0,0,.06)}\n    header{margin-bottom:12px}\n    h1{font-size:20px;margin:0 0 6px}\n    p.lead{margin:0;color:#444;font-size:13px}\n    \/* Chat styles *\/\n    #chat-box{height:420px;overflow:auto;border:1px solid #ddd;padding:10px;border-radius:8px;background:#fafafa}\n    .msg{margin:8px 0;padding:6px;border-radius:6px}\n    .msg .meta{font-size:12px;color:#666;margin-bottom:4px}\n    .msg .text{font-size:15px;color:#111}\n    .translation{font-size:13px;color:#444;margin-top:4px;opacity:.9}\n    .own{background:#e8f4ff;border-left:4px solid var(--accent)}\n    .other{background:#fff;border-left:4px solid #ddd}\n    \/* Input row *\/\n    .row{display:flex;gap:8px;margin-top:10px}\n    input[type=\"text\"], input[type=\"tel\"], textarea{padding:8px;border:1px solid #ccc;border-radius:6px;flex:1}\n    button{background:var(--accent);color:#fff;border:0;padding:9px 12px;border-radius:6px;cursor:pointer}\n    button.secondary{background:#6b7280}\n    \/* Quiz & scoreboard *\/\n    .option{padding:10px;background:#f1f5f9;margin:8px 0;border-radius:6px;cursor:pointer}\n    .option.correct{background:#c8ffc8}\n    .option.wrong{background:#ffc8c8}\n    #scoreList{list-style:none;padding:0;margin:6px 0}\n    #scoreList li{padding:8px;border-bottom:1px solid #eee;font-size:14px}\n    \/* responsive *\/\n    @media(max-width:980px){.container{grid-template-columns:1fr;}}\n    small.hint{color:#666;display:block;margin-top:6px}\n  <\/style>\n<\/head>\n<body>\n  <div class=\"container\">\n    <!-- CHAT COLUMN -->\n    <div class=\"card\" id=\"chatCard\">\n      <header>\n        <h1>\ud83c\udf0d Bilingual Group Chat (English \u2194 Hindi)<\/h1>\n        <p class=\"lead\">Login with your phone (pre-authorized) \u2014 messages auto-translate &#038; show original.<\/p>\n      <\/header>\n\n      <div id=\"login\">\n        <label>Enter your phone number:<\/label><br>\n        <input id=\"phone\" type=\"tel\" placeholder=\"e.g. 9810292863\" \/>\n        <div style=\"margin-top:8px\">\n          <button id=\"loginBtn\">Login<\/button>\n          <button id=\"logoutBtn\" style=\"display:none\" class=\"secondary\">Logout<\/button>\n        <\/div>\n        <small class=\"hint\">Allowed numbers: Heena, Khusboo, Lokesh (demo)<\/small>\n      <\/div>\n\n      <div id=\"chat-area\" style=\"display:none\">\n        <p>Logged in as: <strong id=\"user-name\"><\/strong><\/p>\n        <div id=\"chat-box\" aria-live=\"polite\"><\/div>\n\n        <div class=\"row\">\n          <input id=\"message\" type=\"text\" placeholder=\"Type your message...\" \/>\n          <button id=\"sendBtn\">Send<\/button>\n        <\/div>\n        <small class=\"hint\">Messages are saved to the Firebase Realtime DB.<\/small>\n      <\/div>\n    <\/div>\n\n    <!-- QUIZ + SCOREBOARD COLUMN -->\n    <div class=\"card\" id=\"quizCard\">\n      <header>\n        <h1>\ud83d\udcd8 UPSC MCQ Practice &#038; Scoreboard<\/h1>\n        <p class=\"lead\">Take the quiz, save your score to the same database, and view the live scoreboard.<\/p>\n      <\/header>\n\n      <div id=\"quizBox\"><\/div>\n\n      <div id=\"resultBox\" style=\"display:none;margin-top:12px\">\n        <h3>Your Score: <span id=\"scoreText\"><\/span><\/h3>\n\n        <input id=\"userName\" type=\"text\" placeholder=\"Enter your name to save score\" \/>\n        <button id=\"saveScoreBtn\">Save Score<\/button>\n\n        <hr \/>\n\n        <h3>Check My Saved Score<\/h3>\n        <input id=\"checkName\" type=\"text\" placeholder=\"Enter your name\" \/>\n        <button id=\"checkScoreBtn\">Check Score<\/button>\n        <p id=\"checkResult\"><\/p>\n\n        <hr \/>\n\n        <h3>Live Scoreboard<\/h3>\n        <ul id=\"scoreList\"><\/ul>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <!-- Firebase v10 modules -->\n  <script type=\"module\">\n    import { initializeApp } from \"https:\/\/www.gstatic.com\/firebasejs\/10.12.0\/firebase-app.js\";\n    import { getDatabase, ref, push, set, get, child, onChildAdded, onValue } from \"https:\/\/www.gstatic.com\/firebasejs\/10.12.0\/firebase-database.js\";\n\n    \/\/ ---- Firebase config (chat-box-a5201) ----\n    const firebaseConfig = {\n      apiKey: \"AIzaSyD9kqnlA_-5vKpvDRVPaaSh79SFOidYVZI\",\n      authDomain: \"chat-box-a5201.firebaseapp.com\",\n      databaseURL: \"https:\/\/chat-box-a5201-default-rtdb.asia-southeast1.firebasedatabase.app\",\n      projectId: \"chat-box-a5201\",\n      storageBucket: \"chat-box-a5201.firebasestorage.app\",\n      messagingSenderId: \"369480820811\",\n      appId: \"1:369480820811:web:8cd41bc23646535f7fc5ce\",\n      measurementId: \"G-Y068X2KW0G\"\n    };\n\n    \/\/ Initialize Firebase\n    const app = initializeApp(firebaseConfig);\n    const db = getDatabase(app);\n\n    \/\/ ---------------------------\n    \/\/ AUTHORIZED USERS (demo)\n    \/\/ ---------------------------\n    const allowedUsers = {\n      \"9810292863\": \"Heena\",\n      \"9667324379\": \"Khusboo\",\n      \"9990442782\": \"Lokesh\"\n    };\n\n    \/\/ UI elements\n    const phoneEl = document.getElementById(\"phone\");\n    const loginBtn = document.getElementById(\"loginBtn\");\n    const logoutBtn = document.getElementById(\"logoutBtn\");\n    const loginDiv = document.getElementById(\"login\");\n    const chatArea = document.getElementById(\"chat-area\");\n    const userNameEl = document.getElementById(\"user-name\");\n    const chatBox = document.getElementById(\"chat-box\");\n    const messageEl = document.getElementById(\"message\");\n    const sendBtn = document.getElementById(\"sendBtn\");\n\n    let currentUser = null;\n\n    \/\/ LOGIN \/ LOGOUT\n    loginBtn.addEventListener(\"click\", () => {\n      const phone = phoneEl.value.trim();\n      if (allowedUsers[phone]) {\n        currentUser = allowedUsers[phone];\n        loginDiv.style.display = \"none\";\n        chatArea.style.display = \"block\";\n        userNameEl.textContent = currentUser;\n        logoutBtn.style.display = \"inline-block\";\n        startListeningMessages();\n      } else {\n        alert(\"\u274c Your number is not authorized.\");\n      }\n    });\n\n    logoutBtn.addEventListener(\"click\", () => {\n      currentUser = null;\n      loginDiv.style.display = \"block\";\n      chatArea.style.display = \"none\";\n      chatBox.innerHTML = \"\";\n      logoutBtn.style.display = \"none\";\n    });\n\n    \/\/ SEND MESSAGE (push to \/messages)\n    sendBtn.addEventListener(\"click\", sendMessage);\n    messageEl.addEventListener(\"keypress\", (e) => {\n      if (e.key === \"Enter\") sendMessage();\n    });\n\n    async function sendMessage() {\n      const msg = messageEl.value.trim();\n      if (!msg || !currentUser) return;\n      \/\/ get translation\n      const { translatedText, sourceLang } = await translateSmart(msg);\n\n      const messageData = {\n        user: currentUser,\n        text: msg,\n        translation: translatedText,\n        sourceLang: sourceLang,\n        time: Date.now()\n      };\n\n      \/\/ push to Firebase\n      push(ref(db, \"messages\"), messageData);\n      messageEl.value = \"\";\n    }\n\n    \/\/ LISTEN FOR MESSAGES (realtime)\n    function startListeningMessages() {\n      \/\/ avoid duplicate listeners if called multiple times\n      onChildAdded(ref(db, \"messages\"), (snap) => {\n        const data = snap.val();\n        appendMessage(data);\n      });\n    }\n\n    function appendMessage(data) {\n      const div = document.createElement(\"div\");\n      div.classList.add(\"msg\");\n      div.classList.add(data.user === currentUser ? \"own\" : \"other\");\n\n      let label = \"\";\n      if (data.sourceLang === \"en\") label = \"\ud83c\ude36 Hindi\"; \/\/ original English -> we show Hindi translation label\n      else if (data.sourceLang === \"hi\") label = \"\ud83c\udf10 English\";\n      else label = \"\ud83d\udd24 Translation\";\n\n      const time = new Date(data.time).toLocaleString();\n\n      div.innerHTML = `\n        <div class=\"meta\"><strong>${escapeHtml(data.user)}<\/strong> \u2022 <small>${time}<\/small><\/div>\n        <div class=\"text\">${escapeHtml(data.text)}<\/div>\n        <div class=\"translation\"><em>${label}:<\/em> ${escapeHtml(data.translation)}<\/div>\n      `;\n      chatBox.appendChild(div);\n      chatBox.scrollTop = chatBox.scrollHeight;\n    }\n\n    \/\/ SAFELY escape HTML to prevent injection (good practice)\n    function escapeHtml(s){ return String(s || \"\").replace(\/[&<>\"']\/g, (m)=>({ \"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#39;\"}[m])); }\n\n    \/\/ TRANSLATION: auto-detect source and translate to the other language\n    \/\/ Uses public Google translate endpoint (no API key). Works for demo; may be rate-limited.\n    async function translateSmart(text){\n      try{\n        const res = await fetch(`https:\/\/translate.googleapis.com\/translate_a\/single?client=gtx&sl=auto&tl=en&dt=t&q=${encodeURIComponent(text)}`);\n        const data = await res.json();\n        \/\/ data[2] often contains detected language, but sometimes nested: handle both.\n        let detected = \"auto\";\n        if (Array.isArray(data) && data.length > 2) {\n          detected = data[2] || (data[0] && data[0].length && data[0][0] && data[0][0][2]) || \"auto\";\n        }\n        \/\/ if detected is \"en\", target hi, else target en\n        let target = detected === \"en\" ? \"hi\" : \"en\";\n        \/\/ Get translation to target:\n        const transRes = await fetch(`https:\/\/translate.googleapis.com\/translate_a\/single?client=gtx&sl=${detected === \"auto\" ? \"auto\" : detected}&tl=${target}&dt=t&q=${encodeURIComponent(text)}`);\n        const transData = await transRes.json();\n        const translatedText = (transData && transData[0] && transData[0][0] && transData[0][0][0]) ? transData[0][0][0] : \"(translation error)\";\n        \/\/ Normalize detected to simple codes if possible\n        const detectedLang = (typeof detected === \"string\" && detected !== \"auto\") ? detected : (transData && transData[2]) || \"unknown\";\n        return { translatedText, sourceLang: detectedLang };\n      } catch (err) {\n        console.error(\"Translation error\", err);\n        return { translatedText: \"(Translation error)\", sourceLang: \"unknown\" };\n      }\n    }\n\n    \/\/ ----------------------------\n    \/\/ QUIZ + SCOREBOARD LOGIC\n    \/\/ ----------------------------\n    const questions = [\n      { question: \"1. Consider the following statements regarding TRIFED...\", options: [\"(a) 1,2,4\",\"(b) 2,3\",\"(c) 1,3\",\"(d) All\"], correct: 0 },\n      { question: \"2. TRIFED objectives include?\", options: [\"(a) 1,2,3\",\"(b) 2,4\",\"(c) 1,3\",\"(d) All\"], correct: 0 },\n      { question: \"3. Indian National Army statements?\", options: [\"(a) 1,2,4\",\"(b) 1,3\",\"(c) 2,3\",\"(d) All\"], correct: 0 },\n    ];\n    let qIndex = 0, qScore = 0;\n\n    const quizBox = document.getElementById(\"quizBox\");\n    const resultBox = document.getElementById(\"resultBox\");\n    const scoreText = document.getElementById(\"scoreText\");\n    const userNameInput = document.getElementById(\"userName\");\n    const saveScoreBtn = document.getElementById(\"saveScoreBtn\");\n    const checkNameInput = document.getElementById(\"checkName\");\n    const checkScoreBtn = document.getElementById(\"checkScoreBtn\");\n    const checkResultP = document.getElementById(\"checkResult\");\n    const scoreListEl = document.getElementById(\"scoreList\");\n\n    function loadQuestion(){\n      if (qIndex >= questions.length) return finishQuiz();\n      const q = questions[qIndex];\n      quizBox.innerHTML = `\n        <h3>${q.question}<\/h3>\n        ${q.options.map((opt,i)=>`<div class=\"option\" data-i=\"${i}\">${escapeHtml(opt)}<\/div>`).join('')}\n        <small class=\"hint\">Click an option to answer<\/small>\n      `;\n\n      \/\/ attach listeners\n      document.querySelectorAll(\".option\").forEach(el=>{\n        el.addEventListener(\"click\", ()=> checkAns(Number(el.dataset.i), el));\n      });\n    }\n\n    function checkAns(i, el){\n      const q = questions[qIndex];\n      const optionEls = document.querySelectorAll(\".option\");\n      if (i === q.correct){\n        el.classList.add(\"correct\");\n        qScore++;\n      } else {\n        el.classList.add(\"wrong\");\n        optionEls[q.correct].classList.add(\"correct\");\n      }\n      \/\/ small delay then next question\n      setTimeout(()=>{ qIndex++; loadQuestion(); }, 700);\n    }\n\n    function finishQuiz(){\n      quizBox.style.display = \"none\";\n      resultBox.style.display = \"block\";\n      scoreText.innerText = `${qScore} \/ ${questions.length}`;\n      \/\/ load current scoreboard\n      loadScores();\n    }\n\n    \/\/ SAVE SCORE to \/scores\/<safeName>\n    saveScoreBtn.addEventListener(\"click\", async ()=>{\n      const name = (userNameInput.value || \"\").trim();\n      if (!name) return alert(\"Enter name!\");\n      const safeKey = name.replace(\/[.#$[\\]]\/g, \"_\");\n      await set(ref(db, \"scores\/\" + safeKey), {\n        name: name,\n        score: qScore,\n        max: questions.length,\n        date: new Date().toLocaleString()\n      });\n      alert(\"Score saved!\");\n      loadScores(); \/\/ refresh\n    });\n\n    \/\/ CHECK SCORE by name\n    checkScoreBtn.addEventListener(\"click\", async ()=>{\n      const name = (checkNameInput.value || \"\").trim();\n      if (!name) return alert(\"Enter your name!\");\n      const safeKey = name.replace(\/[.#$[\\]]\/g, \"_\");\n      const snap = await get(child(ref(db), \"scores\/\" + safeKey));\n      if (!snap.exists()) checkResultP.innerText = \"No score found!\";\n      else {\n        const d = snap.val();\n        checkResultP.innerHTML = `<b>${escapeHtml(d.name)}<\/b> \u2014 ${d.score}\/${d.max} (${escapeHtml(d.date)})`;\n      }\n    });\n\n    \/\/ LOAD SCORES (live)\n    function loadScores(){\n      \/\/ use onValue for live updates for all scores\n      onValue(ref(db, \"scores\"), (snap) => {\n        const data = snap.val() || {};\n        const list = Object.values(data)\n          .sort((a,b)=> (b.score - a.score) || (new Date(b.date) - new Date(a.date)))\n          .map(s => `<li><strong>${escapeHtml(s.name)}<\/strong> \u2014 ${s.score}\/${s.max} <small style=\"color:#666\">(${escapeHtml(s.date)})<\/small><\/li>`)\n          .join(\"\");\n        scoreListEl.innerHTML = list || \"<li>No scores yet<\/li>\";\n      });\n    }\n\n    \/\/ Initial load\n    loadQuestion();\n    loadScores();\n\n    \/\/ Also load messages that already exist (so new users see history) - uses onChildAdded above which will emit for existing children as well\n    onChildAdded(ref(db, \"messages\"), (snap)=>{\n      \/\/ handled by startListeningMessages or direct append if not logged in yet\n      const data = snap.val();\n      \/\/ append only if chat area exists (we'll append duplicates if startListeningMessages also added; to keep it simple we rely on onChildAdded only once)\n      \/\/ We'll append here too so messages show even before login; but they will duplicate when logged in because startListeningMessages also adds a listener.\n      \/\/ To avoid duplicate, we won't call startListeningMessages multiple times in this implementation.\n      if (!currentUser) {\n        appendMessage(data);\n      }\n    });\n\n    \/\/ Notes on duplication: startListeningMessages calls onChildAdded again but it's okay - the listener will append messages as they arrive.\n    \/\/ To ensure no duplicates when user logs in, we could clear chatBox before adding messages in startListeningMessages; for simplicity, we clear chatBox on login.\n    \/\/ (We already clear on logout.)\n\n  <\/script>\n<\/body>\n<\/html>\n\n\n\n\n","protected":false},"excerpt":{"rendered":"<p>Chat + Quiz Scoreboard (Combined) \ud83c\udf0d Bilingual Group Chat (English \u2194 Hindi) Login with your phone (pre-authorized) \u2014 messages auto-translate &#038; show original. Enter your phone number: Login Logout Allowed numbers: Heena, Khusboo, Lokesh (demo) Logged in as: Send Messages are saved to the Firebase Realtime DB. \ud83d\udcd8 UPSC MCQ Practice &#038; Scoreboard Take the &#8230; <a title=\"CHAT BOX\" class=\"read-more\" href=\"https:\/\/apicalclasses.com\/?p=5193\" aria-label=\"Read more about CHAT BOX\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-5193","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/posts\/5193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5193"}],"version-history":[{"count":7,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/posts\/5193\/revisions"}],"predecessor-version":[{"id":5218,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=\/wp\/v2\/posts\/5193\/revisions\/5218"}],"wp:attachment":[{"href":"https:\/\/apicalclasses.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/apicalclasses.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}