• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות

    למה אין נתונים לקווים? 🤔

    מתוזמן נעוץ נעול הועבר בקשות לפיתוח
    142 פוסטים 11 כותבים 331 צפיות 5 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • ק מנותק
      קו המוסיקה @קו המוסיקה
      נערך לאחרונה על ידי

      מה ההבדל המעשי בין הקוד הראשוני ש @אa
      לקוד של @kol ?
      אשמח לדעת, כי לא כזה הבנתי כזה ההבדל...

      תגובה 1 תגובה אחרונה תגובה ציטוט 0
      • 1 מנותק
        121244
        נערך לאחרונה על ידי

        @kol הקוד הנ"ל לא מחזיר לי כלל תוצאות אבל לא הגדרתי את מה שאמרת בivr כי אנינ צריך את הנתונים על לפני כן
        אין כזה דבר?

        ק תגובה 1 תגובה אחרונה תגובה ציטוט 0
        • 1 מנותק
          121244 @kol
          נערך לאחרונה על ידי

          @kol כתב בלמה אין נתונים לקווים? 🤔:

          log_playback_play_stop=yes
          log_playback_play_stop_ymgr_to_html=yes
          log_playback_play_stop_year_save_folder=yes
          log_playback_play_stop_year_save_folder_ymgr_to_html=yes
          log_playback_play_stop_month_save_root_log=yes
          log_playback_play_stop_month_save_root_log_ymgr_to_html=yes

          חלק מההגדרות כאן לא קיימות

          תגובה 1 תגובה אחרונה תגובה ציטוט 0
          • ק מנותק
            קו המוסיקה @121244
            נערך לאחרונה על ידי

            @121244 ככל שידוע לי אם לא הגדרת את זה לפני לא תקבל את הנתונים שהיו,
            אם משהו יודע אחרת שיעדכן כי זה רלוונטי בשבילי,
            כי אני הגדרתי את זה לפני שעה ועדיין לא מופיעים לי שום נתונים... (והיו מסתומה מאוד מאזינים בשעה האחרונה)

            1 תגובה 1 תגובה אחרונה תגובה ציטוט 0
            • 1 מנותק
              121244 @קו המוסיקה
              נערך לאחרונה על ידי

              @קו-המוסיקה כפי שאמרתי ההגדרות הנ"ל חלקן לא נכונות
              ולגבי זה שצריך להגדיר לפני כן זה מוזר כי יש לי לוגים במערכת מוגדר אצלי בivr

              save_listening_data=yes
              log_playback_play_stop_month_save_root_log=yes
              listening_mark=yes
              

              וזה מציג בתוצאות 0 בהכל

              תגובה 1 תגובה אחרונה תגובה ציטוט 0
              • מ מנותק
                מוטי מוטי מוטי @אA
                נערך לאחרונה על ידי

                @אA c843e490-4cfb-44bf-a92f-823f829c508a-image.png
                איך זה עובד ההשמעה והנטישה?
                יש 17% האזנה ו 29% נטישה מה זה?
                תודה,
                זה מטורף!
                אשכרה, כבר שנים אין כזה דבר...

                ק א 2 תגובות תגובה אחרונה תגובה ציטוט 0
                • ק מנותק
                  קו המוסיקה @מוטי מוטי מוטי
                  נערך לאחרונה על ידי

                  אוקיי הבנתי מה היה הבעיה שלי,
                  אני הייתי צריך לפתוח טוקן חדש בשביל זה!!
                  והנה זה עובד לי עכשיו זה מטוווורף!!!
                  @אa אין מילים לא האמנתי שכשהעלתי את הנושא פה באמת נגיע למשהו כזה!!!
                  תודה רבה לכל מי שסייע לזה זה פשוט מגניב ומטורף ומאוד מאוד יעיל!!!!
                  (וכמובן אם יהיה לי הערות אני יגיד)

                  1 תגובה 1 תגובה אחרונה תגובה ציטוט 0
                  • 1 מנותק
                    121244 @קו המוסיקה
                    נערך לאחרונה על ידי

                    @קו-המוסיקה למה לי זה לא עובד
                    זה מביא נתונים לא נכונים

                    ק תגובה 1 תגובה אחרונה תגובה ציטוט 0
                    • ק מנותק
                      קו המוסיקה @121244
                      נערך לאחרונה על ידי

                      @121244 כן גם לי הנתונים לא הגיונים בדאשבורד, ומה הסיפור של הגרף הזה,
                      דבר שני אני רואה קבצים מובילים אבל אני רוצה לדעת מאיזה שלוחה הרי יש לי מאות קבצים בקו של קובץ 000
                      Screenshot 2026-04-07 at 15.07.45.png

                      א תגובה 1 תגובה אחרונה תגובה ציטוט 0
                      • א מנותק
                        אA @קו המוסיקה
                        נערך לאחרונה על ידי

                        @קו-המוסיקה @121244 וכל המגיבים.
                        אני בלנד אשב ע"ז לבדוק את הסיפור של הנתונים ואעדכן.

                        תגובה 1 תגובה אחרונה תגובה ציטוט 0
                        • א מנותק
                          אA
                          נערך לאחרונה על ידי

                          אוקי.
                          שיניתי את אופן הבדיקה של השיחות ואני מקווה שזה יהיה יותר אמין.
                          אשמח אם יוכלו להציף שוב את הבאגים שיש כדי שאוכל לתקנם.
                          הקוד המעודכן

                          <!DOCTYPE html>
                          <html lang="he" dir="rtl">
                          <head>
                              <meta charset="UTF-8">
                              <title>ניתוח נתוני מערכות | v26.5</title>
                              <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
                              <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
                              <style>
                                  :root { --bg: #0f172a; --card: #1e293b; --primary: #38bdf8; --accent: #22c55e; --text: #f1f5f9; --border: #334155; --danger: #ef4444; --warning: #f59e0b; }
                                  body { font-family: 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); margin: 0; display: flex; flex-direction: column; height: 100vh; }
                                  header { background: #1e293b; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); }
                                  .logo-box { display: flex; align-items: center; gap: 12px; }
                                  .logo-icon { background: linear-gradient(135deg, var(--primary), #0ea5e9); color: #0f172a; width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
                                  .logo-text { font-size: 1.4rem; font-weight: 800; color: var(--text); }
                                  .config-box { display: flex; gap: 12px; align-items: center; }
                                  input, select { background: #0f172a; border: 1px solid var(--border); color: white; padding: 10px; border-radius: 8px; outline: none; }
                                  /* שינוי ספציפי לצבע לבן בבחירת תאריך */
                                  input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); cursor: pointer; }
                                  .btn-run { background: var(--primary); color: #0f172a; border: none; padding: 10px 25px; border-radius: 8px; font-weight: bold; cursor: pointer; transition: 0.3s; }
                                  .nav-tabs { background: #1e293b; display: flex; padding: 0 20px; border-bottom: 1px solid var(--border); }
                                  .tab { padding: 15px 25px; cursor: pointer; color: #94a3b8; border-bottom: 3px solid transparent; transition: 0.3s; }
                                  .tab.active { color: var(--primary); border-bottom-color: var(--primary); background: rgba(56, 189, 248, 0.07); }
                                  .main-content { flex: 1; padding: 25px; overflow-y: auto; background: #0f172a; }
                                  .card { background: var(--card); padding: 25px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 25px; }
                                  .stat-num { font-size: 2.5rem; font-weight: 800; color: var(--primary); }
                                  .control-bar { display: flex; gap: 20px; align-items: center; background: #334155; padding: 15px 25px; border-radius: 12px 12px 0 0; }
                                  .search-input { background: #0f172a; border: 1px solid var(--border); color: white; padding: 8px 15px; border-radius: 8px; width: 250px; }
                                  table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 0 0 12px 12px; overflow: hidden; }
                                  th { text-align: right; padding: 15px; background: #475569; color: white; font-size: 0.85rem; }
                                  td { padding: 15px; border-bottom: 1px solid var(--border); font-size: 0.95rem; }
                                  tr.clickable { cursor: pointer; }
                                  tr.clickable:hover { background: rgba(56, 189, 248, 0.1); }
                                  .bar-container { width: 100px; background: #0f172a; height: 10px; border-radius: 10px; display: inline-block; overflow: hidden; vertical-align: middle; margin-left: 8px; }
                                  .bar-fill { height: 100%; background: var(--accent); }
                                  .modal-overlay { position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.85); display: none; justify-content: center; align-items: center; z-index: 1000; }
                                  .modal-content { background: var(--card); width: 85%; max-height: 85%; border-radius: 20px; border: 1px solid var(--primary); overflow-y: auto; padding: 30px; position: relative; }
                                  .close-modal { position: absolute; top: 20px; left: 20px; color: white; font-size: 2rem; cursor: pointer; }
                                  .progress-container { width: 100%; background: #1e293b; height: 6px; display: none; }
                                  #progress-fill { height: 100%; background: var(--primary); width: 0%; transition: 0.4s; }
                                  .hidden { display: none; }
                                  .live-status { display: flex; align-items: center; gap: 6px; font-size: 0.75rem; padding: 4px 10px; border-radius: 20px; background: #334155; }
                                  .dot { width: 8px; height: 8px; border-radius: 50%; background: #666; }
                                  .dot.active { background: var(--accent); animation: pulse 2s infinite; }
                                  @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }
                              </style>
                          </head>
                          <body>
                          
                          <header>
                              <div class="logo-box">
                                  <div class="logo-icon"><i class="fas fa-chart-pie"></i></div>
                                  <div class="logo-text">ניתוח נתוני מערכות</div>
                                  <div class="live-status"><div id="live-dot" class="dot"></div> <span id="live-text">OFFLINE</span></div>
                              </div>
                              <div class="config-box">
                                  <input type="password" id="apiToken" placeholder="טוקן API">
                                  <input type="date" id="startDate">
                                  <input type="date" id="endDate">
                                  <button class="btn-run" onclick="manualStart()">הפעל ניתוח</button>
                              </div>
                          </header>
                          
                          <div class="progress-container" id="progBar"><div id="progress-fill"></div></div>
                          
                          <div class="nav-tabs">
                              <div class="tab active" onclick="switchTab(event, 'dash-tab')">דאשבורד</div>
                              <div class="tab" onclick="switchTab(event, 'ext-tab')">שלוחות</div>
                              <div class="tab" onclick="switchTab(event, 'play-tab')">השמעות</div>
                              <div class="tab" onclick="switchTab(event, 'users-tab')">מאזינים</div>
                              <div class="tab" style="color: var(--warning)" onclick="switchTab(event, 'debug-tab')">לוג</div>
                          </div>
                          
                          <div class="main-content">
                              <div id="dash-tab" class="tab-content">
                                  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 25px; margin-bottom: 25px;">
                                      <div class="card"><span style="color: #94a3b8">מאזינים ייחודיים</span><br><span id="stat-users" class="stat-num">0</span></div>
                                      <div class="card"><span style="color: #94a3b8">סה"כ דקות האזנה</span><br><span id="stat-min" class="stat-num">0</span></div>
                                      <div class="card"><span style="color: #94a3b8">שיחות שהושלמו (סוף קובץ)</span><br><span id="stat-comp" class="stat-num">0</span></div>
                                  </div>
                                  <div class="card"><h3>גרף פעילות יומי</h3><canvas id="dailyChart" height="100"></canvas></div>
                              </div>
                          
                              <div id="ext-tab" class="tab-content hidden">
                                  <div class="control-bar">
                                      <select id="extSort" onchange="renderExts()"><option value="count">כמות כניסות</option><option value="sec">זמן שהייה</option></select>
                                      <input type="text" id="extSearch" class="search-input" placeholder="חפש שלוחה..." onkeyup="renderExts()">
                                  </div>
                                  <table id="extTable"><thead><tr><th>שם השלוחה</th><th>תיאור שלוחה</th><th>כניסות</th><th>דקות שהייה</th><th>ממוצע</th></tr></thead><tbody></tbody></table>
                              </div>
                          
                              <div id="play-tab" class="tab-content hidden">
                                  <div class="control-bar">
                                      <select id="playSort" onchange="renderPlays()"><option value="count">פופולריות</option><option value="pct">אחוז השלמה</option></select>
                                      <input type="text" id="playSearch" class="search-input" placeholder="חפש קובץ..." onkeyup="renderPlays()">
                                  </div>
                                  <table id="playTable"><thead><tr><th>שם קובץ / שלוחה</th><th>השמעות</th><th>סה"כ דקות</th><th>אחוז השלמה</th><th>נטישה</th></tr></thead><tbody></tbody></table>
                              </div>
                          
                              <div id="users-tab" class="tab-content hidden">
                                  <div class="control-bar">
                                      <select id="userSort" onchange="renderUsers()"><option value="sec">זמן האזנה כולל</option><option value="calls">מספר שיחות</option></select>
                                      <input type="text" id="userSearch" class="search-input" placeholder="חפש טלפון או שם..." onkeyup="renderUsers()">
                                  </div>
                                  <table id="usersTable"><thead><tr><th>מספר טלפון</th><th>שם מאזין</th><th>דקות האזנה</th><th>מספר שיחות</th><th>סטטוס</th></tr></thead><tbody></tbody></table>
                              </div>
                          
                              <div id="debug-tab" class="tab-content hidden"><div class="card"><div id="debug-console" style="background:#000; color:#22c55e; padding:20px; height:400px; overflow-y:auto; font-family:monospace;"></div></div></div>
                          </div>
                          
                          <div id="userModal" class="modal-overlay" onclick="closeModal()">
                              <div class="modal-content" onclick="event.stopPropagation()">
                                  <span class="close-modal" onclick="closeModal()">&times;</span>
                                  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                                      <h2 id="modalTitle" style="color: var(--primary); margin:0;"></h2>
                                      <select id="modalFilter" onchange="filterModalContent()" style="background: var(--bg); border: 1px solid var(--primary); padding: 5px 15px;">
                                          <option value="all">הכל</option>
                                          <option value="שלוחה">שלוחות בלבד</option>
                                          <option value="השמעה">השמעות בלבד</option>
                                      </select>
                                  </div>
                                  <table id="modalTable">
                                      <thead>
                                          <tr>
                                              <th>סוג פעילות</th>
                                              <th>שלוחה/קובץ</th>
                                              <th>זמן שהייה</th>
                                              <th>תאריך</th>
                                          </tr>
                                      </thead>
                                      <tbody></tbody>
                                  </table>
                              </div>
                          </div>
                          
                          <script>
                              const API = "https://www.call2all.co.il/ym/api/";
                              let dataStore = { exts: {}, plays: {}, users: {}, daily: {}, rawActivity: [], totalSec: 0, completed: 0, names: {} };
                              let currentModalPhone = "";
                              let dailyChart = null;
                              let autoRefreshTimer = null;
                          
                              function addLog(msg, color = '#22c55e') {
                                  const console = document.getElementById('debug-console');
                                  if(!console) return;
                                  const div = document.createElement('div');
                                  div.style.color = color;
                                  div.innerText = `[${new Date().toLocaleTimeString()}] ${msg}`;
                                  console.appendChild(div);
                              }
                          
                              async function fetchNames(token) {
                                  try {
                                      const res = await fetch(`${API}DownloadFile?token=${token}&path=ivr2:/Log/EnterIDValName.ymgr`);
                                      const text = await res.text();
                                      text.split('\n').forEach(line => {
                                          const [phone, name] = line.split('=');
                                          if (phone && name) dataStore.names[phone.trim()] = name.trim();
                                      });
                                      addLog("שמות מקובץ EnterIDValName נטענו.");
                                  } catch(e) {}
                          
                                  try {
                                      const res = await fetch(`${API}GetTemplateEntries?token=${token}&templateId=1`);
                                      const json = await res.json();
                                      if (json.entries) {
                                          json.entries.forEach(e => {
                                              if (e.phone && e.name) dataStore.names[e.phone] = e.name;
                                          });
                                          addLog("שמות מרשימת תפוצה נטענו.");
                                      }
                                  } catch(e) {}
                              }
                          
                              function cleanExt(val) {
                                  if (!val) return "ראשית";
                                  let s = val.toString().trim().replace(/^\ufeff/, "");
                                  if (s === "" || s === "/" || s === "." || s.toLowerCase() === "root" || s.toLowerCase() === "main") return "ראשית";
                                  return s;
                              }
                          
                              function parseSec(t) {
                                  if (!t) return 0;
                                  if (t.toString().includes(':')) {
                                      const p = t.split(':').map(Number);
                                      return p.length === 3 ? (p[0]*3600)+(p[1]*60)+p[2] : (p[0]*60)+p[1];
                                  }
                                  return parseInt(t) || 0;
                              }
                          
                              async function manualStart() {
                                  if(autoRefreshTimer) clearInterval(autoRefreshTimer);
                                  await startFullAnalysis();
                                  document.getElementById('live-dot').classList.add('active');
                                  document.getElementById('live-text').innerText = "LIVE (עדכון אוטומטי)";
                                  autoRefreshTimer = setInterval(startFullAnalysis, 60000); 
                              }
                          
                              async function startFullAnalysis() {
                                  const token = document.getElementById('apiToken').value;
                                  const start = document.getElementById('startDate').value;
                                  const end = document.getElementById('endDate').value;
                                  if (!token) return alert("נא להזין טוקן API");
                          
                                  addLog("בודק הרשאות וטוקן...");
                                  dataStore = { exts: {}, plays: {}, users: {}, daily: {}, rawActivity: [], totalSec: 0, completed: 0, names: {} };
                                  
                                  const check = await fetch(`${API}GetIVR2Dir?token=${token}&path=ivr2:/Log`);
                                  const checkJson = await check.json();
                                  if (checkJson.responseStatus !== "OK") {
                                      addLog("שגיאה: טוקן API לא תקין או שפג תוקפו", "red");
                                      return alert("טוקן API לא תקין!");
                                  }
                          
                                  document.getElementById('progBar').style.display = 'block';
                                  await fetchNames(token);
                          
                                  const monthStr = start.substring(0, 7); 
                                  
                                  try {
                                      const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogFolderEnterExit-${monthStr}.ymgr&convertType=json`);
                                      const json = await res.json();
                                      if (json.data) {
                                          json.data.forEach(row => {
                                              const extName = cleanExt(row["שלוחה"]);
                                              const phone = row["טלפון"] || "חסוי";
                                              const sec = parseInt(row["סה\"כ שניות"]) || 0;
                                              const date = row["תאריך"] || "";
                                              const time = row["התחלה שעה"] || "";
                                              const callId = row["מזהה שיחה"];
                                              
                                              const extTitle = row["כותרת שלוחה"] || row["שם השלוחה"] || row["תיאור"] || "-";
                                              
                                              if (row["שם מזהה"] && !dataStore.names[phone]) dataStore.names[phone] = row["שם מזהה"];
                          
                                              if (!dataStore.exts[extName]) dataStore.exts[extName] = { count: 0, sec: 0, title: extTitle };
                                              dataStore.exts[extName].count++;
                                              dataStore.exts[extName].sec += sec;
                                              
                                              dataStore.totalSec += sec;
                                              if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, callIds: new Set() };
                                              dataStore.users[phone].sec += sec;
                                              if (callId) dataStore.users[phone].callIds.add(callId);
                          
                                              dataStore.rawActivity.push({ 
                                                  phone, 
                                                  type: 'שלוחה', 
                                                  name: extName, 
                                                  sec: sec + " ש'", 
                                                  date: date + " " + time
                                              });
                                          });
                                      }
                                  } catch(e) { addLog("שגיאה בגישה לקובץ LogFolderEnterExit", "red"); }
                          
                                  let dates = [];
                                  for(let d=new Date(start); d<=new Date(end); d.setDate(d.getDate()+1)) dates.push(new Date(d).toISOString().split('T')[0]);
                          
                                  for (let day of dates) {
                                      updateProgress(dates.indexOf(day)+1, dates.length);
                                      try {
                                          const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${day}.ymgr&convertType=json`);
                                          const json = await res.json();
                                          if (json.data) {
                                              if (!dataStore.daily[day]) dataStore.daily[day] = 0;
                                              json.data.forEach(row => {
                                                  const file = row["השמעה"] || "לא ידוע";
                                                  const phone = row["טלפון"] || "חסוי";
                                                  const sec = parseInt(row["סה\"כ שניות"]) || 0;
                                                  const len = parseSec(row["אורך הקובץ"]);
                                                  const isEnd = row["נקודת יציאה"] === "סוף";
                                                  const callId = row["מזהה שיחה"];
                                                  
                                                  dataStore.daily[day]++;
                                                  dataStore.totalSec += sec;
                                                  if (isEnd) dataStore.completed++;
                          
                                                  if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, callIds: new Set() };
                                                  dataStore.users[phone].sec += sec;
                                                  if (callId) dataStore.users[phone].callIds.add(callId);
                          
                                                  if (!dataStore.plays[file]) dataStore.plays[file] = { count: 0, sec: 0, drops: 0, pcts: [] };
                                                  dataStore.plays[file].count++;
                                                  dataStore.plays[file].sec += sec;
                                                  dataStore.plays[file].pcts.push(len > 0 ? Math.min(100, Math.round((sec/len)*100)) : 0);
                                                  if (!isEnd) dataStore.plays[file].drops++;
                          
                                                  dataStore.rawActivity.push({ 
                                                      phone, 
                                                      type: 'השמעה', 
                                                      name: file, 
                                                      sec: sec + " ש'", 
                                                      date: day + " " + (row["שעה"] || "") 
                                                  });
                                              });
                                          }
                                      } catch(e) {}
                                  }
                                  renderUI();
                              }
                          
                              function renderUI() {
                                  document.getElementById('progBar').style.display = 'none';
                                  document.getElementById('stat-users').innerText = Object.keys(dataStore.users).length.toLocaleString();
                                  document.getElementById('stat-min').innerText = Math.floor(dataStore.totalSec / 60).toLocaleString();
                                  document.getElementById('stat-comp').innerText = dataStore.completed.toLocaleString();
                                  renderExts(); renderPlays(); renderUsers(); updateChart();
                                  addLog("הנתונים מעודכנים.");
                              }
                          
                              function renderExts() {
                                  const sort = document.getElementById('extSort').value;
                                  const q = document.getElementById('extSearch').value.toLowerCase();
                                  let items = Object.entries(dataStore.exts).map(([name, d]) => ({name, ...d})).filter(i => i.name.toLowerCase().includes(q));
                                  items.sort((a,b) => b[sort] - a[sort]);
                                  document.querySelector('#extTable tbody').innerHTML = items.map(i => `
                                      <tr><td style="color:var(--primary); font-weight:600">${i.name}</td><td>${i.title}</td><td>${i.count}</td><td>${(i.sec/60).toFixed(1)}</td><td>${i.count ? Math.round(i.sec/i.count) : 0} ש'</td></tr>
                                  `).join('');
                              }
                          
                              function renderPlays() {
                                  const sort = document.getElementById('playSort').value;
                                  const q = document.getElementById('playSearch').value.toLowerCase();
                                  let items = Object.entries(dataStore.plays).map(([name, d]) => {
                                      const avg = d.pcts.length ? Math.round(d.pcts.reduce((a,b)=>a+b,0)/d.pcts.length) : 0;
                                      const drop = Math.round((d.drops/d.count)*100);
                                      return {name, avg, drop, ...d};
                                  }).filter(i => i.name.toLowerCase().includes(q));
                                  items.sort((a,b) => sort === 'count' ? b.count - a.count : b.avg - a.avg);
                                  document.querySelector('#playTable tbody').innerHTML = items.map(i => `
                                      <tr><td>${i.name}</td><td>${i.count}</td><td>${(i.sec/60).toFixed(1)}</td>
                                      <td><div class="bar-container"><div class="bar-fill" style="width:${i.avg}%"></div></div> ${i.avg}%</td>
                                      <td style="color:${i.drop > 50 ? 'var(--danger)' : 'var(--accent)'}">${i.drop}%</td></tr>
                                  `).join('');
                              }
                          
                              function renderUsers() {
                                  const sortKey = document.getElementById('userSort').value;
                                  const q = document.getElementById('userSearch').value;
                                  let items = Object.entries(dataStore.users).map(([phone, d]) => {
                                      const name = dataStore.names[phone] || "לא ידוע";
                                      return {
                                          phone, 
                                          name, 
                                          sec: d.sec, 
                                          calls: d.callIds ? d.callIds.size : 0
                                      };
                                  }).filter(i => i.phone.includes(q) || i.name.includes(q));
                                  
                                  items.sort((a,b) => b[sortKey] - a[sortKey]);
                                  document.querySelector('#usersTable tbody').innerHTML = items.map((i, idx) => `
                                      <tr class="clickable" onclick="openUserDetail('${i.phone}')">
                                          <td style="font-weight:bold">${i.phone}</td>
                                          <td style="color:var(--warning)">${i.name}</td>
                                          <td style="color:var(--primary); font-weight:bold">${(i.sec/60).toFixed(1)}</td>
                                          <td>${i.calls}</td>
                                          <td>${idx < 3 ? '🏆 מוביל' : 'מאזין'}</td>
                                      </tr>
                                  `).join('');
                              }
                          
                              function openUserDetail(phone) {
                                  currentModalPhone = phone;
                                  const name = dataStore.names[phone] || "לא ידוע";
                                  document.getElementById('modalTitle').innerText = `פירוט מאזין: ${phone} (${name})`;
                                  document.getElementById('modalFilter').value = 'all';
                                  filterModalContent();
                                  document.getElementById('userModal').style.display = 'flex';
                              }
                          
                              function filterModalContent() {
                                  const filter = document.getElementById('modalFilter').value;
                                  const activity = dataStore.rawActivity.filter(a => a.phone === currentModalPhone);
                                  const filtered = filter === 'all' ? activity : activity.filter(a => a.type === filter);
                          
                                  document.querySelector('#modalTable tbody').innerHTML = filtered.map(a => `
                                      <tr>
                                          <td style="color:${a.type==='השמעה'?'var(--primary)':'var(--accent)'}; font-weight:bold">${a.type}</td>
                                          <td>${a.name}</td>
                                          <td>${a.sec}</td>
                                          <td>${a.date}</td>
                                      </tr>
                                  `).join('');
                              }
                          
                              function closeModal() { document.getElementById('userModal').style.display = 'none'; }
                              function updateProgress(c, t) { document.getElementById('progress-fill').style.width = (c/t*100) + '%'; }
                              function switchTab(e, id) {
                                  document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden'));
                                  document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
                                  document.getElementById(id).classList.remove('hidden');
                                  e.currentTarget.classList.add('active');
                              }
                              function updateChart() {
                                  if (dailyChart) dailyChart.destroy();
                                  const ctx = document.getElementById('dailyChart').getContext('2d');
                                  dailyChart = new Chart(ctx, {
                                      type: 'line',
                                      data: { labels: Object.keys(dataStore.daily), datasets: [{ label: 'שיחות', data: Object.values(dataStore.daily), borderColor: '#38bdf8', tension: 0.4, fill: true, backgroundColor: 'rgba(56, 189, 248, 0.1)' }] },
                                      options: { plugins: { legend: { display: false } } }
                                  });
                              }
                              document.getElementById('startDate').value = document.getElementById('endDate').value = new Date().toISOString().split('T')[0];
                          </script>
                          </body>
                          </html>
                          

                          תגובה 1 תגובה אחרונה תגובה ציטוט 0
                          • א מנותק
                            אA @מוטי מוטי מוטי
                            נערך לאחרונה על ידי אA

                            @מוטי-מוטי-מוטי
                            אחוזי הנטישה זה מביא את אלה שלא הקישו סוף ההשמעה.
                            והאזנה, מציג את אחוז האזנה לחלקי הקובץ, כלומר שהרוב האזינו ל17% מהקובץ.

                            ק תגובה 1 תגובה אחרונה תגובה ציטוט 0
                            • י מנותק
                              יוסף חיים 5
                              נערך לאחרונה על ידי

                              @אa מדהים!!!
                              רק עדכון קטן, בבחירת טווח תאריכים זה נותן את רק את החודש הראשון, וכן את כל החודש ולא לפי ימים.

                              א תגובה 1 תגובה אחרונה תגובה ציטוט 0
                              • א מנותק
                                אA @יוסף חיים 5
                                נערך לאחרונה על ידי

                                @יוסף-חיים-5
                                מה הכוונה?
                                אפשר לעבור בין החודשים וכן לבחור את טווח הימים.

                                5 תגובה 1 תגובה אחרונה תגובה ציטוט 0
                                • 5 מנותק
                                  565906 @אA
                                  נערך לאחרונה על ידי

                                  @אA
                                  החישובים בטוח לא נכונים עדיין....


                                  c02db0bf-0e0d-4d41-9f2f-4293fa875a67-image.png
                                  ccc8994b-2c8d-4f61-b14c-23f5b19a09a7-image.png

                                  א תגובה 1 תגובה אחרונה תגובה ציטוט 0
                                  • א מנותק
                                    אA @565906
                                    נערך לאחרונה על ידי

                                    @565906
                                    עדיין מטפל בזה.

                                    5 2 תגובות תגובה אחרונה תגובה ציטוט 1
                                    • 5 מנותק
                                      565906 @אA
                                      נערך לאחרונה על ידי 565906

                                      @אA אגב:
                                      גם בדאשבורד בגרף אני חושב שכדאי שיהיה גרף של שעות....


                                      זה בהצגה של נתונים רק מהיום...
                                      ab7b9c55-888b-4b1d-a7ac-7be1ca1d7ecd-image.png

                                      א תגובה 1 תגובה אחרונה תגובה ציטוט 0
                                      • י מנותק
                                        יוסף חיים 5
                                        נערך לאחרונה על ידי

                                        @אa אם אני עושה יום אחד בחודש הוא מראה את כל החודש, וכן אם אני בוחר כמה חודשים הוא נותן רק את הראשון

                                        תגובה 1 תגובה אחרונה תגובה ציטוט 0
                                        • 5 מנותק
                                          565906 @אA
                                          נערך לאחרונה על ידי 565906

                                          @אA
                                          בהשמעות אני חושב שכדאי להוסיף גם מיון לפי מספר קובץ מלבד אחוז השמעה / נטישה (שאגב: לא מספיק ברור מה זה - איך זה עובד) ופופולאריות

                                          תגובה 1 תגובה אחרונה תגובה ציטוט 1
                                          • א מנותק
                                            אA @565906
                                            נערך לאחרונה על ידי

                                            @565906
                                            חשבתי ע"ז.
                                            בעז"ה נוסיף.

                                            תגובה 1 תגובה אחרונה תגובה ציטוט 1
                                            • פוסט ראשון
                                              פוסט אחרון