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

    אני צריך דוח על שלוחה מסוימת איך אני מוציא את זה

    מתוזמן נעוץ נעול הועבר שאלות ועזרה הדדית
    3 פוסטים 3 כותבים 18 צפיות 1 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • ה מנותק
      הפצת התורה
      נערך לאחרונה על ידי

      אני צריך דוח על שלוחה מי התקשר וכמה זמן ומי השאיר הקלטה

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

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

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

          @הפצת-התורה יש לך את זה וגם את זה:
          (תפתח בפנקס רשימות ותשמור בשם עם סיומת HTML)

          <!DOCTYPE html>
          <html lang="he" dir="rtl">
          <head>
              <meta charset="UTF-8">
              <title>ניתוח נתוני מערכות | גרסה יציבה ומדויקת</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; }
                  .btn-run:hover { opacity: 0.8; }
                  .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: 20px; display: none; position: relative; text-align: center; }
                  #progress-fill { height: 100%; background: var(--primary); width: 0%; transition: 0.4s; position: absolute; top:0; right:0; }
                  #progress-text { position: relative; z-index: 2; font-size: 0.8rem; font-weight: bold; color: white; line-height: 20px; }
                  .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>
              <span id="progress-text">0%</span>
          </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="avg">אחוז השלמה</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><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; border-radius: 8px;"></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); color:white; border: 1px solid var(--primary); padding: 5px 15px; border-radius: 5px;">
                          <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>
           
          <!-- מודאל פירוט שלוחה -->
          <div id="extModal" class="modal-overlay" onclick="closeExtModal()">
              <div class="modal-content" onclick="event.stopPropagation()">
                  <span class="close-modal" onclick="closeExtModal()">&times;</span>
                  <h2 id="extModalTitle" style="color: var(--primary); margin-bottom: 20px;"></h2>
                  <table id="extModalTable">
                      <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);
                  console.scrollTop = console.scrollHeight; 
              }
           
              function normalizePhone(p) {
                  if (!p) return "חסוי";
                  let s = p.toString().replace(/\D/g, '');
                  if (s.length > 0 && !s.startsWith('0') && s.length >= 8) s = '0' + s;
                  return s;
              }
           
              function formatExtName(ext) {
                  if (!ext) return "ראשית";
                  let str = ext.toString().trim();
                  if (str === "" || str === "/" || str.toLowerCase() === "main" || str.toLowerCase() === "root") {
                      return "ראשית";
                  }
                  return str;
              }
           
              function parseYemotDateToIso(dStr) {
                  if (!dStr || !dStr.includes('/')) return null;
                  const parts = dStr.split('/');
                  if (parts.length === 3) {
                      const d = parts[0].padStart(2, '0');
                      const m = parts[1].padStart(2, '0');
                      const y = parts[2];
                      return `${y}-${m}-${d}`;
                  }
                  return null;
              }
           
              function timeStrToSeconds(tStr) {
                  if (!tStr) return 0;
                  if (!tStr.includes(':')) return parseInt(tStr) || 0;
                  const p = tStr.split(':').map(Number);
                  if (p.length === 3) return (p[0] * 3600) + (p[1] * 60) + p[2];
                  if (p.length === 2) return (p[0] * 60) + p[1];
                  return parseInt(tStr) || 0;
              }
           
              function ensureUser(phone) {
                  if (!dataStore.users[phone]) {
                      dataStore.users[phone] = { sec: 0, calls: 0 };
                  }
              }
           
              async function fetchNames(token) {
                  try {
                      const res = await fetch(`${API}DownloadFile?token=${token}&path=ivr2:/EnterIDValName.ini`);
                      const text = await res.text();
                      if(text && !text.includes("NOT_FOUND")) {
                          text.split(/\r?\n/).forEach(line => {
                              if (line.includes('=')) {
                                  const [phone, name] = line.split('=');
                                  const cleanPhone = normalizePhone(phone);
                                  if (cleanPhone && name) dataStore.names[cleanPhone] = name.trim();
                              }
                          });
                          addLog(`נטענו שמות מקובץ ההגדרות הראשי`);
                      }
                  } catch(e) { }
              }
           
              async function manualStart() {
                  if(autoRefreshTimer) clearInterval(autoRefreshTimer);
                  await startFullAnalysis();
                  document.getElementById('live-dot').classList.add('active');
                  document.getElementById('live-text').innerText = "LIVE (פעיל)";
              }
           
              async function startFullAnalysis() {
                  const token = document.getElementById('apiToken').value;
                  const startStr = document.getElementById('startDate').value; 
                  const endStr = document.getElementById('endDate').value;     
                  
                  if (!token) return alert("נא להזין טוקן API");
           
                  addLog(`מתחיל עיבוד נתונים מתאריך ${startStr} עד ${endStr}...`);
                  document.getElementById('progBar').style.display = 'block';
                  updateProgress(5, 100);
                  
                  dataStore = { exts: {}, plays: {}, users: {}, daily: {}, rawActivity: [], totalSec: 0, completed: 0, names: {} };
                  
                  await fetchNames(token);
                  updateProgress(10, 100);
           
                  // חישוב חודשים בטוח (מניעת תקלות של אזור זמן)
                  let currM = new Date(startStr);
                  currM.setDate(1); // עקיפת בעיית 31 לחודש
                  let endM = new Date(endStr);
                  let monthsToFetch = [];
                  while (currM <= endM || (currM.getFullYear() === endM.getFullYear() && currM.getMonth() === endM.getMonth())) {
                      let y = currM.getFullYear();
                      let m = String(currM.getMonth() + 1).padStart(2, '0');
                      monthsToFetch.push(`${y}-${m}`);
                      currM.setMonth(currM.getMonth() + 1);
                  }
           
                  // אובייקט לאיתור שיחות כפולות (למניעת קפיצת דקות)
                  let callMap = {};
           
                  for (let month of monthsToFetch) {
                      try {
                          addLog(`מייבא לוג כניסות עבור חודש ${month}...`);
                          const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogFolderEnterExit-${month}.ymgr&convertType=json`);
                          const json = await res.json();
                          
                          if (json.data && Array.isArray(json.data)) {
                              json.data.forEach(row => {
                                  const gregDate = row["תאריך"]; 
                                  const hebDate = row["תאריך עברי"] || row["עברי"] || ""; 
                                  const isoDate = parseYemotDateToIso(gregDate);
                                  
                                  if (!isoDate) return;
                                  
                                  // סינון מוחלט של ימים לפי טקסט (למשל "2026-03-31") - מונע כפילויות!
                                  if (isoDate >= startStr && isoDate <= endStr) {
                                      
                                      const extName = formatExtName(row["שלוחה"]);
                                      const phone = normalizePhone(row["טלפון"]);
                                      const sec = parseInt(row["סה\"כ שניות"]) || 0;
                                      const callId = row["מזהה שיחה"];
                                      const nameFromLog = row["שם מזהה"];
                                      
                                      if (nameFromLog && !dataStore.names[phone]) dataStore.names[phone] = nameFromLog;
           
                                      // -- חישוב נתונים לשלוחות --
                                      if (!dataStore.exts[extName]) {
                                          dataStore.exts[extName] = { count: 0, sec: 0, title: row["כותרת שלוחה"] || "", extUsers: {} };
                                      }
                                      dataStore.exts[extName].count++;
                                      dataStore.exts[extName].sec += sec;
           
                                      if (!dataStore.exts[extName].extUsers[phone]) {
                                          dataStore.exts[extName].extUsers[phone] = { count: 0, sec: 0 };
                                      }
                                      dataStore.exts[extName].extUsers[phone].count++;
                                      dataStore.exts[extName].extUsers[phone].sec += sec;
           
                                      // -- איסוף זמן למאזין וכללי במערכת (עם מניעת כפילויות של מזהה שיחה) --
                                      if (callId) {
                                          if (!callMap[callId]) callMap[callId] = { phone: phone, maxSec: 0 };
                                          if (sec > callMap[callId].maxSec) callMap[callId].maxSec = sec;
                                      } else {
                                          // במקרה נדיר שאין מזהה שיחה
                                          ensureUser(phone);
                                          dataStore.users[phone].sec += sec;
                                          dataStore.users[phone].calls++;
                                          dataStore.totalSec += sec;
                                      }
           
                                      if (!dataStore.daily[isoDate]) dataStore.daily[isoDate] = 0;
                                      dataStore.daily[isoDate]++;
           
                                      const displayDate = hebDate ? `${hebDate} (${gregDate})` : gregDate;
                                      dataStore.rawActivity.push({ phone, type: 'שלוחה', name: extName, sec: sec + " ש'", date: `${displayDate} ${row["התחלה שעה"]||''}` });
                                  }
                              });
                          }
                      } catch(e) { addLog(`לא נמצא לוג כניסות בחודש ${month}`, "orange"); }
                  }
           
                  // עדכון סך הדקות והשיחות במערכת (מסונן ללא כפילויות!)
                  Object.values(callMap).forEach(call => {
                      dataStore.totalSec += call.maxSec;
                      ensureUser(call.phone);
                      dataStore.users[call.phone].sec += call.maxSec;
                      dataStore.users[call.phone].calls += 1;
                  });
           
                  updateProgress(50, 100);
           
                  // חישוב ימים להשמעות בטוח
                  let daysToFetch = [];
                  let currDay = new Date(startStr);
                  let endDay = new Date(endStr);
                  while(currDay <= endDay) {
                      let y = currDay.getFullYear();
                      let m = String(currDay.getMonth() + 1).padStart(2, '0');
                      let d = String(currDay.getDate()).padStart(2, '0');
                      daysToFetch.push(`${y}-${m}-${d}`);
                      currDay.setDate(currDay.getDate() + 1);
                  }
           
                  for (let i=0; i<daysToFetch.length; i++) {
                      const day = daysToFetch[i];
                      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 && Array.isArray(json.data)) {
                              json.data.forEach(row => {
                                  const gregDate = row["תאריך"] || day;
                                  const isoDate = parseYemotDateToIso(gregDate);
                                  
                                  // וידוא שההשמעה בטווח התאריכים המדויק
                                  if (isoDate && (isoDate < startStr || isoDate > endStr)) return;
           
                                  const file = row["השמעה"];
                                  if (!file) return;
           
                                  const folder = formatExtName(row["שלוחה"]);
                                  const phone = normalizePhone(row["טלפון"]);
                                  const sec = parseInt(row["סה\"כ שניות"]) || 0;
                                  const fileLenSec = timeStrToSeconds(row["אורך הקובץ"]);
                                  const exitPoint = row["נקודת יציאה"];
                                  const nameFromLog = row["שם"];
                                  
                                  const hebDate = row["עברי"] || row["תאריך עברי"] || "";
                                  const displayDate = hebDate ? `${hebDate} (${gregDate})` : gregDate;
                                  
                                  if (nameFromLog && !dataStore.names[phone]) dataStore.names[phone] = nameFromLog;
                                  ensureUser(phone); // מוודא שמי שהאזין לקובץ יופיע בטבלת מאזינים גם אם לא נכנס לשלוחה רשמית
           
                                  const isEnd = (exitPoint === "סוף") || (fileLenSec > 0 && sec >= fileLenSec - 2);
                                  if (isEnd) dataStore.completed++;
           
                                  if (!dataStore.plays[file]) dataStore.plays[file] = { count: 0, sec: 0, drops: 0, pcts: [], folder: folder };
                                  dataStore.plays[file].count++;
                                  dataStore.plays[file].sec += sec;
                                  
                                  const pct = fileLenSec > 0 ? Math.min(100, Math.round((sec / fileLenSec) * 100)) : (isEnd ? 100 : 0);
                                  dataStore.plays[file].pcts.push(pct);
                                  
                                  if (!isEnd) dataStore.plays[file].drops++;
           
                                  dataStore.rawActivity.push({ phone, type: 'השמעה', name: file, sec: sec + " ש'", date: `${displayDate} ${row["התחלה שעה"]||''}` });
                              });
                          }
                      } catch(e) {}
                      
                      updateProgress(50 + Math.round(((i + 1) / daysToFetch.length) * 50), 100);
                  }
           
                  addLog("עיבוד הנתונים הסתיים בהצלחה.");
                  renderUI();
              }
           
              function updateProgress(c, t) { 
                  const pct = Math.round((c/t)*100);
                  document.getElementById('progress-fill').style.width = pct + '%'; 
                  document.getElementById('progress-text').innerText = pct + '%';
              }
           
              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();
              }
           
              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 class="clickable" onclick="openExtDetail('${i.name}')">
                          <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 && d.pcts.length ? Math.round(d.pcts.reduce((a,b)=>a+b,0)/d.pcts.length) : 0;
                      const drop = d.count ? Math.round((d.drops/d.count)*100) : 0;
                      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.folder || '-'}</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]) => ({
                      phone, name: dataStore.names[phone] || "לא ידוע", sec: d.sec || 0, calls: d.calls || 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;
                  document.getElementById('modalTitle').innerText = `פירוט מאזין: ${phone} (${dataStore.names[phone] || "לא ידוע"})`;
                  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 openExtDetail(extName) {
                  document.getElementById('extModalTitle').innerText = `פירוט מאזינים בשלוחה: ${extName}`;
                  const extData = dataStore.exts[extName];
                  if(!extData) return;
           
                  let usersArr = Object.entries(extData.extUsers).map(([phone, d]) => {
                      return { phone, name: dataStore.names[phone] || "לא ידוע", count: d.count, sec: d.sec };
                  });
           
                  usersArr.sort((a,b) => b.sec - a.sec);
           
                  document.querySelector('#extModalTable tbody').innerHTML = usersArr.map(u => `
                      <tr>
                          <td style="color:var(--warning)">${u.name}</td>
                          <td style="font-weight:bold">${u.phone}</td>
                          <td>${u.count}</td>
                          <td style="color:var(--primary); font-weight:bold">${(u.sec/60).toFixed(1)} דק'</td>
                      </tr>
                  `).join('');
           
                  document.getElementById('extModal').style.display = 'flex';
              }
           
              function closeExtModal() { document.getElementById('extModal').style.display = 'none'; }
           
              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');
                  const sortedDates = Object.keys(dataStore.daily).sort();
                  const sortedData = sortedDates.map(d => dataStore.daily[d]);
           
                  dailyChart = new Chart(ctx, {
                      type: 'line',
                      data: { labels: sortedDates, datasets: [{ label: 'פעולות במערכת', data: sortedData, borderColor: '#38bdf8', tension: 0.4, fill: true, backgroundColor: 'rgba(56, 189, 248, 0.1)' }] },
                      options: { plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
                  });
              }
           
              let dEnd = new Date();
              let dStart = new Date();
              dStart.setDate(dEnd.getDate() - 7);
              document.getElementById('endDate').value = dEnd.toISOString().split('T')[0];
              document.getElementById('startDate').value = dStart.toISOString().split('T')[0];
          </script>
          </body>
          </html>
          
          תגובה 1 תגובה אחרונה תגובה ציטוט 0
          • פוסט ראשון
            פוסט אחרון