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

    קבצים ותוכנות לשימוש במערכות ימות המשיח

    מתוזמן נעוץ נעול הועבר עזרה הדדית למשתמשים מתקדמים
    91 פוסטים 15 כותבים 2.2k צפיות 10 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • א מנותק
      אA
      נערך לאחרונה על ידי

      קובץ לניתוח נתוני המערכת

      הקוד

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <title>ניתוח נתוני מערכות | v26.3</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; }
              .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></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></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 };
          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);
          }
      
          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 };
              document.getElementById('progBar').style.display = 'block';
      
              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["התחלה שעה"] || "";
                          
                          if (!dataStore.exts[extName]) dataStore.exts[extName] = { count: 0, sec: 0 };
                          dataStore.exts[extName].count++;
                          dataStore.exts[extName].sec += sec;
                          
                          dataStore.totalSec += sec;
                          if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, calls: 0 };
                          dataStore.users[phone].sec += sec;
      
                          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["נקודת יציאה"] === "סוף";
                              
                              dataStore.daily[day]++;
                              dataStore.totalSec += sec;
                              if (isEnd) dataStore.completed++;
      
                              if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, calls: 0 };
                              dataStore.users[phone].sec += sec;
                              dataStore.users[phone].calls++;
      
                              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.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]) => ({phone, ...d})).filter(i => i.phone.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(--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}`;
              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>
      

      קרדיט: @אa

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

        עידכון:
        השרשור עודכן בקבצים חדשים.
        כל הקבצים בפוסט הראשון.

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

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

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

            @בוס
            קיבלתי.

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

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

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

                @בוס
                לא עדיף בקוד להעתקה קלה?

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

                  @אA כן אבל בתוך סופוילר

                  ככה:

                  <!DOCTYPE html>
                  <html lang="he" dir="rtl">
                  <head>
                      <meta charset="UTF-8">
                      <title>ניתוח נתוני מערכות | v26.3</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; }
                          .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></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></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 };
                      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);
                      }
                   
                      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 };
                          document.getElementById('progBar').style.display = 'block';
                   
                          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["התחלה שעה"] || "";
                                      
                                      if (!dataStore.exts[extName]) dataStore.exts[extName] = { count: 0, sec: 0 };
                                      dataStore.exts[extName].count++;
                                      dataStore.exts[extName].sec += sec;
                                      
                                      dataStore.totalSec += sec;
                                      if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, calls: 0 };
                                      dataStore.users[phone].sec += sec;
                   
                                      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["נקודת יציאה"] === "סוף";
                                          
                                          dataStore.daily[day]++;
                                          dataStore.totalSec += sec;
                                          if (isEnd) dataStore.completed++;
                   
                                          if (!dataStore.users[phone]) dataStore.users[phone] = { sec: 0, calls: 0 };
                                          dataStore.users[phone].sec += sec;
                                          dataStore.users[phone].calls++;
                   
                                          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.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]) => ({phone, ...d})).filter(i => i.phone.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(--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}`;
                          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 @בוס
                    נערך לאחרונה על ידי

                    @בוס

                    גם לך קופץ כל הזמ שהפורום התעדכן לגירסא האחרונה?

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

                      @אA

                      קפץ פעם אחת, בפועל לא התשנה משהו הנראה לעין.

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

                        @בוס

                        לי כבר קפץ פעמיים, אבל גם בלי שינוי נראה...

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

                          @אA

                          תיכף אחד מ"חכמי" הפורום יפתח ע"ז שרשור שלם...

                          רק לי קפץ התראה על שינוי הפורום...

                          וכמובן איך אפשק לשכוח, תמונה.

                          267358c1-7e2f-43b6-a8b7-caba2e9b6a8e-image.png

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