• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות
    1. דף הבית
    2. kol
    K
    מנותק
    • פרופיל
    • עוקב אחרי 0
    • עוקבים 0
    • נושאים 7
    • פוסטים 61
    • קבוצות 0

    kol

    @kol

    13
    מוניטין
    54
    צפיות בפרופיל
    61
    פוסטים
    0
    עוקבים
    0
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה

    kol הפסקת מעקב מעקב

    הפוסטים הטובים ביותר שנוצרו על ידי kol

    • RE: הורדת דמי הקישוריות

      @פלוס כתב בהורדת דמי הקישוריות:

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

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

      פורסם בעזרה הדדית למשתמשים מתקדמים
      K
      kol
    • RE: עדכונים בנוגע לקריסת השרתים של ימות מהבוקר, בעז"ה הכל יסתדר וישוחזר, רק סבלנות!!! ולא לגעת סתם במערכות!

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

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: למה אין נתונים לקווים? 🤔

      מצורף הקובץ מתוקן ועובד טוב מאוד עם מלא שידורגים

      בשביל לקבל נתוני השמעות תוסיפו במערכת בהגדרות מתקדמות בשלוחה הראשית בקובץ IVR.ini את ההגדרות הבאות

      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
      

      הנה הקוד:

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <title>ניתוח נתוני מערכות | v36.0 - גרסה מאובטחת</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); }
              .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.2s; }
              .btn-run:hover { opacity: 0.9; transform: scale(1.02); }
              .btn-reset-date { background: transparent; color: #94a3b8; border: 1px solid var(--border); padding: 8px; border-radius: 8px; cursor: pointer; }
              .btn-export { background: #475569; color: white; border: none; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; display: flex; align-items: center; gap: 5px; }
              .nav-tabs { background: #1e293b; display: flex; padding: 0 20px; border-bottom: 1px solid var(--border); overflow-x: auto; }
              .tab { padding: 15px 25px; cursor: pointer; color: #94a3b8; border-bottom: 3px solid transparent; transition: 0.3s; white-space: nowrap; }
              .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: 20px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 25px; }
              .stat-num { font-size: 2.2rem; font-weight: 800; color: var(--primary); }
              .control-bar { display: flex; gap: 20px; align-items: center; background: #334155; padding: 12px 20px; border-radius: 12px 12px 0 0; justify-content: space-between; flex-wrap: wrap; }
              .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; min-width: 600px; }
              th { text-align: right; padding: 12px; background: #475569; color: white; font-size: 0.85rem; cursor: pointer; user-select: none; transition: 0.2s; position: relative; }
              th:hover { background: #64748b; }
              th i { margin-right: 8px; font-size: 0.7rem; opacity: 0.5; }
              th.sort-asc i, th.sort-desc i { opacity: 1; color: var(--primary); }
              td { padding: 12px; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
              .progress-container { width: 100%; background: #1e293b; height: 6px; display: none; position: relative; }
              #progress-fill { height: 100%; background: var(--primary); width: 0%; transition: 0.4s; }
              .hidden { display: none; }
              .btn-play { color: var(--primary); cursor: pointer; background: transparent; border: 1px solid var(--primary); padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; }
              .btn-play:hover { background: var(--primary); color: #0f172a; }
          </style>
      </head>
      <body>
      
      <header>
          <div class="logo-box">
              <div class="logo-icon"><i class="fas fa-headphones"></i></div>
              <div class="logo-text">ניתוח נתוני מערכות</div>
          </div>
          <div class="config-box">
              <!-- השדה ריק כעת לצורך אבטחה -->
              <input type="password" id="apiToken" placeholder="הכנס טוקן API כאן" value="">
              <button class="btn-reset-date" title="חזור לתחילת חודש" onclick="setDefaultDates()"><i class="fas fa-calendar-alt"></i></button>
              <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(250px, 1fr)); gap: 20px; 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">
                  <input type="text" id="extSearch" class="search-input" placeholder="חפש שלוחה..." onkeyup="renderExts()">
                  <button class="btn-export" onclick="exportTableToCSV('extTable', 'שלוחות')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="extTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('extTable', 0, 'string')">שלוחה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 1, 'number')">כניסות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 2, 'number')">ייחודיים <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 3, 'number')">סה"כ דקות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 4, 'number')">ממוצע <i class="fas fa-sort"></i></th>
                      </tr>
                  </thead>
                  <tbody></tbody>
              </table>
          </div>
      
          <div id="play-tab" class="tab-content hidden">
              <div class="control-bar">
                  <input type="text" id="playSearch" class="search-input" placeholder="חפש קובץ, שלוחה או טלפון..." onkeyup="renderPlays()">
                  <button class="btn-export" onclick="exportTableToCSV('playTable', 'השמעות')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="playTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('playTable', 0, 'string')">זמן אחרון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 1, 'string')">שלוחה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 2, 'string')">קובץ <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 3, 'string')">מאזין אחרון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 4, 'number')">השמעות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 5, 'number')">סה"כ דק' <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 6, 'number')">אחוז <i class="fas fa-sort"></i></th>
                          <th>פעולה</th>
                      </tr>
                  </thead>
                  <tbody></tbody>
              </table>
          </div>
      
          <div id="users-tab" class="tab-content hidden">
              <div class="control-bar">
                  <input type="text" id="userSearch" class="search-input" placeholder="חפש טלפון..." onkeyup="renderUsers()">
                  <button class="btn-export" onclick="exportTableToCSV('usersTable', 'מאזינים')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="usersTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('usersTable', 0, 'string')">מספר טלפון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 1, 'number')">דקות האזנה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 2, 'number')">פעולות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 3, 'string')">סוג <i class="fas fa-sort"></i></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:15px; height:450px; overflow-y:auto; font-family:monospace; font-size: 12px; line-height: 1.4;">
                      <div>מערכת v36.0 מוכנה לאחסון בשרת. הטוקן הוסר מהקוד.</div>
                  </div>
              </div>
          </div>
      </div>
      
      <div id="playerModal" class="hidden" style="position:fixed; bottom:20px; left:20px; background:var(--card); padding:15px; border-radius:12px; border:1px solid var(--primary); z-index:1000; box-shadow: 0 10px 30px rgba(0,0,0,0.5);">
          <div style="display:flex; justify-content:space-between; margin-bottom:10px;">
              <span id="playerTitle" style="font-size:0.8rem; color:var(--primary)">נגן השמעה</span>
              <i class="fas fa-times" style="cursor:pointer" onclick="closePlayer()"></i>
          </div>
          <audio id="audioPlayer" controls style="width:250px; height:35px;"></audio>
          <div id="playerError" style="color:var(--danger); font-size:11px; margin-top:5px; display:none;">שגיאה בטעינת הקובץ</div>
      </div>
      
      <script>
          const API = "https://www.call2all.co.il/ym/api/";
          let dataStore = { exts: {}, plays: {}, users: {}, daily: {}, totalSec: 0, completed: 0 };
          let dailyChart = null;
          let sortState = { tableId: null, colIndex: null, direction: 'asc' };
      
          function setDefaultDates() {
              const now = new Date();
              const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
              const f = (d) => {
                  let year = d.getFullYear();
                  let month = String(d.getMonth() + 1).padStart(2, '0');
                  let day = String(d.getDate()).padStart(2, '0');
                  return `${year}-${month}-${day}`;
              };
              document.getElementById('startDate').value = f(firstDay);
              document.getElementById('endDate').value = f(now);
          }
      
          function addLog(msg, color = '#22c55e') {
              const console = document.getElementById('debug-console');
              const div = document.createElement('div');
              div.style.color = color;
              div.innerHTML = `[${new Date().toLocaleTimeString()}] ${msg}`;
              console.appendChild(div);
              console.scrollTop = console.scrollHeight;
          }
      
          async function manualStart() {
              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: {}, totalSec: 0, completed: 0 };
              document.getElementById('progBar').style.display = 'block';
      
              const monthKey = start.substring(0, 7);
              try {
                  const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogFolderEnterExit-${monthKey}.ymgr&convertType=json`);
                  const json = await res.json();
                  if (json.data) {
                      json.data.forEach(row => {
                          const ext = row["שלוחה"] || "ראשי";
                          const phone = row["טלפון"] || "חסוי";
                          const sec = parseInt(row["סה\"כ שניות"] || row["שניות"] || 0);
                          const date = row["תאריך"] || start;
      
                          if (!dataStore.exts[ext]) dataStore.exts[ext] = { count: 0, sec: 0, unique: new Set() };
                          dataStore.exts[ext].count++;
                          dataStore.exts[ext].sec += sec;
                          dataStore.exts[ext].unique.add(phone);
                          trackUser(phone, sec);
                          dataStore.daily[date] = (dataStore.daily[date] || 0) + 1;
                          dataStore.totalSec += sec;
                      });
                  }
              } catch(e) { addLog("לוג כניסות לא נמצא.", "#f59e0b"); }
      
              let days = [];
              let dt = new Date(start);
              const endDt = new Date(end);
              while(dt <= endDt) {
                  days.push(new Date(dt).toISOString().split('T')[0]);
                  dt.setDate(dt.getDate() + 1);
              }
      
              for (let day of days) {
                  updateProgress(days.indexOf(day)+1, days.length);
                  const paths = [`ivr2:/Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${day}.ymgr`,`ivr2:/Log/LogPlaybackPlayStop.${day}.ymgr`,`ivr2:/Log/LogPlayback.${day}.ymgr`];
                  for (let path of paths) {
                      try {
                          const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=${path}&convertType=json`);
                          const json = await res.json();
                          if (json.data && json.data.length > 0) {
                              addLog(`יום ${day}: נמצאו נתוני השמעות.`);
                              processPlaybackRows(json.data, day);
                              break;
                          }
                      } catch(e) {}
                  }
              }
              renderUI();
          }
      
          function processPlaybackRows(rows, day) {
              rows.forEach(row => {
                  const keys = Object.keys(row);
                  const extKey = keys.find(k => k.includes("שלוחה") || k.includes("Folder") || k.includes("תיקייה")) || "שלוחה";
                  const fileKey = keys.find(k => k.includes("קובץ") || k.includes("השמעה") || k.includes("Play")) || keys[0];
                  const secKey = keys.find(k => k.includes("שניות") || k.includes("זמן") || k.includes("Sec")) || keys[1];
                  const exitKey = keys.find(k => k.includes("יציאה") || k.includes("Exit")) || "";
                  const timeKey = keys.find(k => k.includes("שעה") || k.includes("Time")) || "";
      
                  const extName = row[extKey] || "לא ידוע";
                  const fileName = row[fileKey] || "לא ידוע";
                  const phone = row["טלפון"] || "חסוי";
                  const sec = parseInt(row[secKey] || 0);
                  const time = row[timeKey] || "";
                  const isEnd = (row[exitKey] || "").toString().includes("סוף") || (row[exitKey] || "").toString().toLowerCase() === "end";
                  
                  const playId = `${extName}|${fileName}`;
      
                  if (!dataStore.plays[playId]) {
                      dataStore.plays[playId] = { ext: extName, file: fileName, lastPhone: phone, lastTime: `${day} ${time}`, count: 0, sec: 0, ends: 0 };
                  }
                  
                  dataStore.plays[playId].count++;
                  dataStore.plays[playId].sec += sec;
                  dataStore.plays[playId].lastPhone = phone;
                  dataStore.plays[playId].lastTime = `${day} ${time}`;
                  if (isEnd) dataStore.plays[playId].ends++;
      
                  trackUser(phone, sec);
                  dataStore.daily[day] = (dataStore.daily[day] || 0) + 1;
                  dataStore.totalSec += sec;
                  if (isEnd) dataStore.completed++;
              });
          }
      
          async function playFile(ext, file) {
              const token = document.getElementById('apiToken').value;
              const modal = document.getElementById('playerModal');
              const player = document.getElementById('audioPlayer');
              const errDiv = document.getElementById('playerError');
              
              errDiv.style.display = 'none';
              modal.classList.remove('hidden');
              document.getElementById('playerTitle').innerText = `טוען: שלוחה ${ext} | קובץ ${file}`;
      
              let cleanExt = ext.replace('ivr2:', '').replace(/^\//, '');
              if (!cleanExt.startsWith('ivr2:')) cleanExt = 'ivr2:/' + cleanExt;
              const fileBase = file.padStart(3, '0');
              
              const formats = ['.wav', '.mp3', '.amr'];
              let success = false;
      
              for (const fmt of formats) {
                  const fullPath = `${cleanExt}/${fileBase}${fmt}`;
                  const url = `${API}DownloadFile?token=${token}&path=${fullPath}`;
                  try {
                      player.src = url;
                      await player.play();
                      success = true;
                      document.getElementById('playerTitle').innerText = `מנגן: ${ext}/${fileBase}${fmt}`;
                      break;
                  } catch (e) {}
              }
              if (!success) errDiv.style.display = 'block';
          }
      
          function closePlayer() {
              const modal = document.getElementById('playerModal');
              const player = document.getElementById('audioPlayer');
              modal.classList.add('hidden');
              player.pause();
              player.src = "";
          }
      
          function trackUser(p, s) {
              if (!dataStore.users[p]) dataStore.users[p] = { sec: 0, count: 0 };
              dataStore.users[p].sec += s;
              dataStore.users[p].count++;
          }
      
          function sortTable(tableId, colIndex, type) {
              const table = document.getElementById(tableId);
              const tbody = table.querySelector('tbody');
              const rows = Array.from(tbody.querySelectorAll('tr'));
              const headers = table.querySelectorAll('th');
      
              if (sortState.tableId === tableId && sortState.colIndex === colIndex) {
                  sortState.direction = sortState.direction === 'asc' ? 'desc' : 'asc';
              } else {
                  sortState.tableId = tableId;
                  sortState.colIndex = colIndex;
                  sortState.direction = 'asc';
              }
      
              headers.forEach(th => th.classList.remove('sort-asc', 'sort-desc'));
              headers[colIndex].classList.add(sortState.direction === 'asc' ? 'sort-asc' : 'sort-desc');
      
              const sortedRows = rows.sort((a, b) => {
                  let aVal = a.children[colIndex].innerText;
                  let bVal = b.children[colIndex].innerText;
                  if (type === 'number') {
                      aVal = parseFloat(aVal.replace(/[^0-9.-]+/g, "")) || 0;
                      bVal = parseFloat(bVal.replace(/[^0-9.-]+/g, "")) || 0;
                  } else {
                      aVal = aVal.toLowerCase();
                      bVal = bVal.toLowerCase();
                  }
                  if (aVal < bVal) return sortState.direction === 'asc' ? -1 : 1;
                  if (aVal > bVal) return sortState.direction === 'asc' ? 1 : -1;
                  return 0;
              });
      
              tbody.innerHTML = '';
              sortedRows.forEach(row => tbody.appendChild(row));
          }
      
          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 q = document.getElementById('extSearch').value.toLowerCase();
              let h = "";
              Object.entries(dataStore.exts).forEach(([n, d]) => {
                  if (n.toLowerCase().includes(q)) {
                      h += `<tr><td style="color:var(--primary); font-weight:bold">${n}</td><td>${d.count}</td><td>${d.unique.size}</td><td>${(d.sec/60).toFixed(1)}</td><td>${Math.round(d.sec/d.count || 0)} ש'</td></tr>`;
                  }
              });
              document.querySelector('#extTable tbody').innerHTML = h;
          }
      
          function renderPlays() {
              const q = document.getElementById('playSearch').value.toLowerCase();
              let h = "";
              Object.values(dataStore.plays).forEach(d => {
                  if (d.file.toLowerCase().includes(q) || d.ext.toLowerCase().includes(q) || d.lastPhone.includes(q)) {
                      const pct = Math.round((d.ends/d.count)*100);
                      h += `<tr>
                          <td style="font-size:0.75rem; color:#94a3b8">${d.lastTime}</td>
                          <td style="color:var(--primary)">${d.ext}</td>
                          <td style="font-weight:bold">${d.file}</td>
                          <td>${d.lastPhone}</td>
                          <td>${d.count}</td>
                          <td>${(d.sec/60).toFixed(1)}</td>
                          <td>${pct}%</td>
                          <td><button class="btn-play" onclick="playFile('${d.ext}', '${d.file}')"><i class="fas fa-play"></i></button></td>
                      </tr>`;
                  }
              });
              document.querySelector('#playTable tbody').innerHTML = h || "<tr><td colspan='8' style='text-align:center'>אין נתונים</td></tr>";
          }
      
          function renderUsers() {
              const q = document.getElementById('userSearch').value;
              let h = "";
              Object.entries(dataStore.users).forEach(([p, d]) => {
                  if (p.includes(q)) {
                      h += `<tr><td>${p}</td><td>${(d.sec/60).toFixed(1)}</td><td>${d.count}</td><td>מאזין</td></tr>`;
                  }
              });
              document.querySelector('#usersTable tbody').innerHTML = h;
          }
      
          function exportTableToCSV(tableId, filename) {
              let csv = [];
              const rows = document.querySelectorAll(`#${tableId} tr`);
              for (let i = 0; i < rows.length; i++) {
                  let row = [], cols = rows[i].querySelectorAll("td, th");
                  for (let j = 0; j < cols.length; j++) row.push('"' + cols[j].innerText + '"');
                  csv.push(row.join(","));
              }
              const csvFile = new Blob(["\ufeff" + csv.join("\n")], { type: "text/csv" });
              const downloadLink = document.createElement("a");
              downloadLink.download = `${filename}.csv`;
              downloadLink.href = window.URL.createObjectURL(csvFile);
              downloadLink.style.display = "none";
              document.body.appendChild(downloadLink);
              downloadLink.click();
          }
      
          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');
              const labels = Object.keys(dataStore.daily).sort();
              dailyChart = new Chart(ctx, {
                  type: 'line',
                  data: { labels, datasets: [{ label: 'פעולות', data: labels.map(l => dataStore.daily[l]), borderColor: '#38bdf8', tension: 0.3, fill: true, backgroundColor: 'rgba(56, 189, 248, 0.1)' }] },
                  options: { scales: { y: { beginAtZero: true } } }
              });
          }
      
          window.onload = setDefaultDates;
      </script>
      </body>
      </html>
      
      פורסם בבקשות לפיתוח
      K
      kol
    • RE: דרוש קריינות להעברה של הודעה אחת

      @דורש1 אמר בדרוש קריינות להעברה של הודעה אחת:

      שלום וברכה
      אולי יש למישהו הודעה מוכנה של
      עקב חוסר קליטה.
      פנייתכם נקלטה במערכת.
      אנו נשוב אליכם בהקדם האפשרי.

      תודה רבה

      יש בחינם תכתוב כאן ותקבל הקלטה מוכנה https://app.almareader.com/

      פורסם בעזרה הדדית למשתמשים מתקדמים
      K
      kol
    • RE: עדכונים בנוגע לקריסת השרתים של ימות מהבוקר, בעז"ה הכל יסתדר וישוחזר, רק סבלנות!!! ולא לגעת סתם במערכות!

      @kol כתב בעדכונים בנוגע לקריסת השרתים של ימות מאמצע הלילה !!!:

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

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

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: מודול הקלטה למערכת אחרת (פיתוח פרטי)

      @דוד_מלך_ישראל
      מאוד יעזור שיהיה אפשר להקליט את אותה הודעה בשתי מערכות.

      פורסם בטיפים עצות והדגמות מהמשתמשים
      K
      kol
    • מודול הפעלה מהירה לקמפיין הפסיק לעבוד

      מודול הפעלה מהירה לקמפיין הפסיק לעבוד!!!!

      type=yemot_dialer_campaign_start
      

      https://f2.freeivr.co.il/topic/45/הפעלה-מהירה-לקמפיין?_=1662025217155

      פורסם בבאגים במערכת
      K
      kol
    • RE: קו מיוחד שידור חי ליל תשעה באב מהרב בידרמן {וככל הנראה גם מהרב טויסיג} ניתן לעשות הפניה

      יש גם את זה עשרות שיעורים ממאות רבנים
      02-5091066
      ניתן לעשות הפניה ההגדרות הם:

      type=routing_yemot
      routing_yemot_number=025091066
      
      פורסם בעזרה הדדית למשתמשים מתקדמים
      K
      kol
    • RE: איך מורידים למחשב רשימת תפוצה של 40 אלף מספרים

      @אורי-ש אמר באיך מורידים למחשב רשימת תפוצה של 40 אלף מספרים:

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

      לא יורד כל המספרים (רק ה 20 הראשונים או כמה שאתה כותב, אבל רשימה גדולה לא יורד)

      פורסם בעזרה הדדית למשתמשים מתקדמים
      K
      kol
    • RE: הודעות מערכת טריוויה

      @אט מופיע שם
      M1111 שלום ל..
      M1222 סך הניקוד שברשותך הוא

      פורסם בעזרה הדדית למשתמשים מתקדמים
      K
      kol

    פוסטים אחרונים שנוצרו על ידי kol

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

      מצורף הקובץ מתוקן ועובד טוב מאוד עם מלא שידורגים

      בשביל לקבל נתוני השמעות תוסיפו במערכת בהגדרות מתקדמות בשלוחה הראשית בקובץ IVR.ini את ההגדרות הבאות

      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
      

      הנה הקוד:

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <title>ניתוח נתוני מערכות | v36.0 - גרסה מאובטחת</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); }
              .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.2s; }
              .btn-run:hover { opacity: 0.9; transform: scale(1.02); }
              .btn-reset-date { background: transparent; color: #94a3b8; border: 1px solid var(--border); padding: 8px; border-radius: 8px; cursor: pointer; }
              .btn-export { background: #475569; color: white; border: none; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; display: flex; align-items: center; gap: 5px; }
              .nav-tabs { background: #1e293b; display: flex; padding: 0 20px; border-bottom: 1px solid var(--border); overflow-x: auto; }
              .tab { padding: 15px 25px; cursor: pointer; color: #94a3b8; border-bottom: 3px solid transparent; transition: 0.3s; white-space: nowrap; }
              .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: 20px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 25px; }
              .stat-num { font-size: 2.2rem; font-weight: 800; color: var(--primary); }
              .control-bar { display: flex; gap: 20px; align-items: center; background: #334155; padding: 12px 20px; border-radius: 12px 12px 0 0; justify-content: space-between; flex-wrap: wrap; }
              .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; min-width: 600px; }
              th { text-align: right; padding: 12px; background: #475569; color: white; font-size: 0.85rem; cursor: pointer; user-select: none; transition: 0.2s; position: relative; }
              th:hover { background: #64748b; }
              th i { margin-right: 8px; font-size: 0.7rem; opacity: 0.5; }
              th.sort-asc i, th.sort-desc i { opacity: 1; color: var(--primary); }
              td { padding: 12px; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
              .progress-container { width: 100%; background: #1e293b; height: 6px; display: none; position: relative; }
              #progress-fill { height: 100%; background: var(--primary); width: 0%; transition: 0.4s; }
              .hidden { display: none; }
              .btn-play { color: var(--primary); cursor: pointer; background: transparent; border: 1px solid var(--primary); padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; }
              .btn-play:hover { background: var(--primary); color: #0f172a; }
          </style>
      </head>
      <body>
      
      <header>
          <div class="logo-box">
              <div class="logo-icon"><i class="fas fa-headphones"></i></div>
              <div class="logo-text">ניתוח נתוני מערכות</div>
          </div>
          <div class="config-box">
              <!-- השדה ריק כעת לצורך אבטחה -->
              <input type="password" id="apiToken" placeholder="הכנס טוקן API כאן" value="">
              <button class="btn-reset-date" title="חזור לתחילת חודש" onclick="setDefaultDates()"><i class="fas fa-calendar-alt"></i></button>
              <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(250px, 1fr)); gap: 20px; 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">
                  <input type="text" id="extSearch" class="search-input" placeholder="חפש שלוחה..." onkeyup="renderExts()">
                  <button class="btn-export" onclick="exportTableToCSV('extTable', 'שלוחות')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="extTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('extTable', 0, 'string')">שלוחה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 1, 'number')">כניסות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 2, 'number')">ייחודיים <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 3, 'number')">סה"כ דקות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('extTable', 4, 'number')">ממוצע <i class="fas fa-sort"></i></th>
                      </tr>
                  </thead>
                  <tbody></tbody>
              </table>
          </div>
      
          <div id="play-tab" class="tab-content hidden">
              <div class="control-bar">
                  <input type="text" id="playSearch" class="search-input" placeholder="חפש קובץ, שלוחה או טלפון..." onkeyup="renderPlays()">
                  <button class="btn-export" onclick="exportTableToCSV('playTable', 'השמעות')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="playTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('playTable', 0, 'string')">זמן אחרון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 1, 'string')">שלוחה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 2, 'string')">קובץ <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 3, 'string')">מאזין אחרון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 4, 'number')">השמעות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 5, 'number')">סה"כ דק' <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('playTable', 6, 'number')">אחוז <i class="fas fa-sort"></i></th>
                          <th>פעולה</th>
                      </tr>
                  </thead>
                  <tbody></tbody>
              </table>
          </div>
      
          <div id="users-tab" class="tab-content hidden">
              <div class="control-bar">
                  <input type="text" id="userSearch" class="search-input" placeholder="חפש טלפון..." onkeyup="renderUsers()">
                  <button class="btn-export" onclick="exportTableToCSV('usersTable', 'מאזינים')"><i class="fas fa-file-excel"></i> ייצוא</button>
              </div>
              <table id="usersTable">
                  <thead>
                      <tr>
                          <th onclick="sortTable('usersTable', 0, 'string')">מספר טלפון <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 1, 'number')">דקות האזנה <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 2, 'number')">פעולות <i class="fas fa-sort"></i></th>
                          <th onclick="sortTable('usersTable', 3, 'string')">סוג <i class="fas fa-sort"></i></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:15px; height:450px; overflow-y:auto; font-family:monospace; font-size: 12px; line-height: 1.4;">
                      <div>מערכת v36.0 מוכנה לאחסון בשרת. הטוקן הוסר מהקוד.</div>
                  </div>
              </div>
          </div>
      </div>
      
      <div id="playerModal" class="hidden" style="position:fixed; bottom:20px; left:20px; background:var(--card); padding:15px; border-radius:12px; border:1px solid var(--primary); z-index:1000; box-shadow: 0 10px 30px rgba(0,0,0,0.5);">
          <div style="display:flex; justify-content:space-between; margin-bottom:10px;">
              <span id="playerTitle" style="font-size:0.8rem; color:var(--primary)">נגן השמעה</span>
              <i class="fas fa-times" style="cursor:pointer" onclick="closePlayer()"></i>
          </div>
          <audio id="audioPlayer" controls style="width:250px; height:35px;"></audio>
          <div id="playerError" style="color:var(--danger); font-size:11px; margin-top:5px; display:none;">שגיאה בטעינת הקובץ</div>
      </div>
      
      <script>
          const API = "https://www.call2all.co.il/ym/api/";
          let dataStore = { exts: {}, plays: {}, users: {}, daily: {}, totalSec: 0, completed: 0 };
          let dailyChart = null;
          let sortState = { tableId: null, colIndex: null, direction: 'asc' };
      
          function setDefaultDates() {
              const now = new Date();
              const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
              const f = (d) => {
                  let year = d.getFullYear();
                  let month = String(d.getMonth() + 1).padStart(2, '0');
                  let day = String(d.getDate()).padStart(2, '0');
                  return `${year}-${month}-${day}`;
              };
              document.getElementById('startDate').value = f(firstDay);
              document.getElementById('endDate').value = f(now);
          }
      
          function addLog(msg, color = '#22c55e') {
              const console = document.getElementById('debug-console');
              const div = document.createElement('div');
              div.style.color = color;
              div.innerHTML = `[${new Date().toLocaleTimeString()}] ${msg}`;
              console.appendChild(div);
              console.scrollTop = console.scrollHeight;
          }
      
          async function manualStart() {
              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: {}, totalSec: 0, completed: 0 };
              document.getElementById('progBar').style.display = 'block';
      
              const monthKey = start.substring(0, 7);
              try {
                  const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogFolderEnterExit-${monthKey}.ymgr&convertType=json`);
                  const json = await res.json();
                  if (json.data) {
                      json.data.forEach(row => {
                          const ext = row["שלוחה"] || "ראשי";
                          const phone = row["טלפון"] || "חסוי";
                          const sec = parseInt(row["סה\"כ שניות"] || row["שניות"] || 0);
                          const date = row["תאריך"] || start;
      
                          if (!dataStore.exts[ext]) dataStore.exts[ext] = { count: 0, sec: 0, unique: new Set() };
                          dataStore.exts[ext].count++;
                          dataStore.exts[ext].sec += sec;
                          dataStore.exts[ext].unique.add(phone);
                          trackUser(phone, sec);
                          dataStore.daily[date] = (dataStore.daily[date] || 0) + 1;
                          dataStore.totalSec += sec;
                      });
                  }
              } catch(e) { addLog("לוג כניסות לא נמצא.", "#f59e0b"); }
      
              let days = [];
              let dt = new Date(start);
              const endDt = new Date(end);
              while(dt <= endDt) {
                  days.push(new Date(dt).toISOString().split('T')[0]);
                  dt.setDate(dt.getDate() + 1);
              }
      
              for (let day of days) {
                  updateProgress(days.indexOf(day)+1, days.length);
                  const paths = [`ivr2:/Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${day}.ymgr`,`ivr2:/Log/LogPlaybackPlayStop.${day}.ymgr`,`ivr2:/Log/LogPlayback.${day}.ymgr`];
                  for (let path of paths) {
                      try {
                          const res = await fetch(`${API}RenderYMGRFile?token=${token}&wath=${path}&convertType=json`);
                          const json = await res.json();
                          if (json.data && json.data.length > 0) {
                              addLog(`יום ${day}: נמצאו נתוני השמעות.`);
                              processPlaybackRows(json.data, day);
                              break;
                          }
                      } catch(e) {}
                  }
              }
              renderUI();
          }
      
          function processPlaybackRows(rows, day) {
              rows.forEach(row => {
                  const keys = Object.keys(row);
                  const extKey = keys.find(k => k.includes("שלוחה") || k.includes("Folder") || k.includes("תיקייה")) || "שלוחה";
                  const fileKey = keys.find(k => k.includes("קובץ") || k.includes("השמעה") || k.includes("Play")) || keys[0];
                  const secKey = keys.find(k => k.includes("שניות") || k.includes("זמן") || k.includes("Sec")) || keys[1];
                  const exitKey = keys.find(k => k.includes("יציאה") || k.includes("Exit")) || "";
                  const timeKey = keys.find(k => k.includes("שעה") || k.includes("Time")) || "";
      
                  const extName = row[extKey] || "לא ידוע";
                  const fileName = row[fileKey] || "לא ידוע";
                  const phone = row["טלפון"] || "חסוי";
                  const sec = parseInt(row[secKey] || 0);
                  const time = row[timeKey] || "";
                  const isEnd = (row[exitKey] || "").toString().includes("סוף") || (row[exitKey] || "").toString().toLowerCase() === "end";
                  
                  const playId = `${extName}|${fileName}`;
      
                  if (!dataStore.plays[playId]) {
                      dataStore.plays[playId] = { ext: extName, file: fileName, lastPhone: phone, lastTime: `${day} ${time}`, count: 0, sec: 0, ends: 0 };
                  }
                  
                  dataStore.plays[playId].count++;
                  dataStore.plays[playId].sec += sec;
                  dataStore.plays[playId].lastPhone = phone;
                  dataStore.plays[playId].lastTime = `${day} ${time}`;
                  if (isEnd) dataStore.plays[playId].ends++;
      
                  trackUser(phone, sec);
                  dataStore.daily[day] = (dataStore.daily[day] || 0) + 1;
                  dataStore.totalSec += sec;
                  if (isEnd) dataStore.completed++;
              });
          }
      
          async function playFile(ext, file) {
              const token = document.getElementById('apiToken').value;
              const modal = document.getElementById('playerModal');
              const player = document.getElementById('audioPlayer');
              const errDiv = document.getElementById('playerError');
              
              errDiv.style.display = 'none';
              modal.classList.remove('hidden');
              document.getElementById('playerTitle').innerText = `טוען: שלוחה ${ext} | קובץ ${file}`;
      
              let cleanExt = ext.replace('ivr2:', '').replace(/^\//, '');
              if (!cleanExt.startsWith('ivr2:')) cleanExt = 'ivr2:/' + cleanExt;
              const fileBase = file.padStart(3, '0');
              
              const formats = ['.wav', '.mp3', '.amr'];
              let success = false;
      
              for (const fmt of formats) {
                  const fullPath = `${cleanExt}/${fileBase}${fmt}`;
                  const url = `${API}DownloadFile?token=${token}&path=${fullPath}`;
                  try {
                      player.src = url;
                      await player.play();
                      success = true;
                      document.getElementById('playerTitle').innerText = `מנגן: ${ext}/${fileBase}${fmt}`;
                      break;
                  } catch (e) {}
              }
              if (!success) errDiv.style.display = 'block';
          }
      
          function closePlayer() {
              const modal = document.getElementById('playerModal');
              const player = document.getElementById('audioPlayer');
              modal.classList.add('hidden');
              player.pause();
              player.src = "";
          }
      
          function trackUser(p, s) {
              if (!dataStore.users[p]) dataStore.users[p] = { sec: 0, count: 0 };
              dataStore.users[p].sec += s;
              dataStore.users[p].count++;
          }
      
          function sortTable(tableId, colIndex, type) {
              const table = document.getElementById(tableId);
              const tbody = table.querySelector('tbody');
              const rows = Array.from(tbody.querySelectorAll('tr'));
              const headers = table.querySelectorAll('th');
      
              if (sortState.tableId === tableId && sortState.colIndex === colIndex) {
                  sortState.direction = sortState.direction === 'asc' ? 'desc' : 'asc';
              } else {
                  sortState.tableId = tableId;
                  sortState.colIndex = colIndex;
                  sortState.direction = 'asc';
              }
      
              headers.forEach(th => th.classList.remove('sort-asc', 'sort-desc'));
              headers[colIndex].classList.add(sortState.direction === 'asc' ? 'sort-asc' : 'sort-desc');
      
              const sortedRows = rows.sort((a, b) => {
                  let aVal = a.children[colIndex].innerText;
                  let bVal = b.children[colIndex].innerText;
                  if (type === 'number') {
                      aVal = parseFloat(aVal.replace(/[^0-9.-]+/g, "")) || 0;
                      bVal = parseFloat(bVal.replace(/[^0-9.-]+/g, "")) || 0;
                  } else {
                      aVal = aVal.toLowerCase();
                      bVal = bVal.toLowerCase();
                  }
                  if (aVal < bVal) return sortState.direction === 'asc' ? -1 : 1;
                  if (aVal > bVal) return sortState.direction === 'asc' ? 1 : -1;
                  return 0;
              });
      
              tbody.innerHTML = '';
              sortedRows.forEach(row => tbody.appendChild(row));
          }
      
          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 q = document.getElementById('extSearch').value.toLowerCase();
              let h = "";
              Object.entries(dataStore.exts).forEach(([n, d]) => {
                  if (n.toLowerCase().includes(q)) {
                      h += `<tr><td style="color:var(--primary); font-weight:bold">${n}</td><td>${d.count}</td><td>${d.unique.size}</td><td>${(d.sec/60).toFixed(1)}</td><td>${Math.round(d.sec/d.count || 0)} ש'</td></tr>`;
                  }
              });
              document.querySelector('#extTable tbody').innerHTML = h;
          }
      
          function renderPlays() {
              const q = document.getElementById('playSearch').value.toLowerCase();
              let h = "";
              Object.values(dataStore.plays).forEach(d => {
                  if (d.file.toLowerCase().includes(q) || d.ext.toLowerCase().includes(q) || d.lastPhone.includes(q)) {
                      const pct = Math.round((d.ends/d.count)*100);
                      h += `<tr>
                          <td style="font-size:0.75rem; color:#94a3b8">${d.lastTime}</td>
                          <td style="color:var(--primary)">${d.ext}</td>
                          <td style="font-weight:bold">${d.file}</td>
                          <td>${d.lastPhone}</td>
                          <td>${d.count}</td>
                          <td>${(d.sec/60).toFixed(1)}</td>
                          <td>${pct}%</td>
                          <td><button class="btn-play" onclick="playFile('${d.ext}', '${d.file}')"><i class="fas fa-play"></i></button></td>
                      </tr>`;
                  }
              });
              document.querySelector('#playTable tbody').innerHTML = h || "<tr><td colspan='8' style='text-align:center'>אין נתונים</td></tr>";
          }
      
          function renderUsers() {
              const q = document.getElementById('userSearch').value;
              let h = "";
              Object.entries(dataStore.users).forEach(([p, d]) => {
                  if (p.includes(q)) {
                      h += `<tr><td>${p}</td><td>${(d.sec/60).toFixed(1)}</td><td>${d.count}</td><td>מאזין</td></tr>`;
                  }
              });
              document.querySelector('#usersTable tbody').innerHTML = h;
          }
      
          function exportTableToCSV(tableId, filename) {
              let csv = [];
              const rows = document.querySelectorAll(`#${tableId} tr`);
              for (let i = 0; i < rows.length; i++) {
                  let row = [], cols = rows[i].querySelectorAll("td, th");
                  for (let j = 0; j < cols.length; j++) row.push('"' + cols[j].innerText + '"');
                  csv.push(row.join(","));
              }
              const csvFile = new Blob(["\ufeff" + csv.join("\n")], { type: "text/csv" });
              const downloadLink = document.createElement("a");
              downloadLink.download = `${filename}.csv`;
              downloadLink.href = window.URL.createObjectURL(csvFile);
              downloadLink.style.display = "none";
              document.body.appendChild(downloadLink);
              downloadLink.click();
          }
      
          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');
              const labels = Object.keys(dataStore.daily).sort();
              dailyChart = new Chart(ctx, {
                  type: 'line',
                  data: { labels, datasets: [{ label: 'פעולות', data: labels.map(l => dataStore.daily[l]), borderColor: '#38bdf8', tension: 0.3, fill: true, backgroundColor: 'rgba(56, 189, 248, 0.1)' }] },
                  options: { scales: { y: { beginAtZero: true } } }
              });
          }
      
          window.onload = setDefaultDates;
      </script>
      </body>
      </html>
      
      פורסם בבקשות לפיתוח
      K
      kol
    • RE: מספר 1-700 - למי שיש פרטים, שיכנס!!!

      @ממוצע
      כיום רק על 1700 מקבלים תגמול (קש"ג)
      שאר המספרים לא!!!

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: מספר 1-700 - למי שיש פרטים, שיכנס!!!

      @שולי כתב במספר 1-700 - למי שיש פרטים, שיכנס!!!:

      @kol זה רק ל 3.5 דקות הראשונות של השיחה לא?

      רק מנייח התגמול הוא 3.5 הדקות הראשונות
      אבל מנייד זה על כל הדקות

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: מספר 1-700 - למי שיש פרטים, שיכנס!!!

      בדקתי בתקנות של משרד התקשורת כדי להבין איך זה עובד עם מספרי ‎1-700.
      מספרי ‎1700 מוגדרים בתקנות כשירות “שיחה בחיוב מפוצל”. לכן כאשר שיחה מגיעה מרשת אחרת, ההתחשבנות בין המפעילים נעשית לפי “תעריף השלמת שיחה” ולכן יש תגמול על כל דקת שיחה מנייד ל1700.

      במסמך של משרד התקשורת על שירות ‎1-700 כתוב במפורש בהגדרות:
      שתעריף השלמת השיחה נקבע לפי תקנה 3 לתקנות הקישור-גומלין.

      גובה התעריף לפי התקנות האחרונות הוא טיפה יותר מ: 0.004 ₪ לדקה של שיחה נכנסת עבור השלמת שיחה לרשת מפ״א.

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

      תקנות התקשורת (בזק ושידורים) – תשלומים בעד קישור-גומלין
      סעיף 3(א) – תעריף השלמת שיחה לרשת מפ״א
      https://he.wikisource.org/wiki/תקנות_התקשורת_(בזק_ושידורים)_(קישור-גומלין)

      מסמך משרד התקשורת על שירות ‎1-700
      ראה סעיף 1.1 בהגדרות ו-הנספח להסדרי חיובים
      https://www.gov.il/BlobFolder/policy/02012021/he/Decision_Determining_Maximum_Payments_for_Bezeq's_Retail_Telephony_Services-Attache-F.pdf

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: עדכונים בנוגע לקריסת השרתים של ימות מהבוקר, בעז"ה הכל יסתדר וישוחזר, רק סבלנות!!! ולא לגעת סתם במערכות!

      חבר'ה הכול בסדר, אפשר להירגע.
      כל הקווים יתעדכנו לנקודת הזמן של הגיבוי האחרון.
      התהליך לוקח זמן מפני שמדובר בכמות גדולה של מידע.
      אין מה לעשות שינויים במערכת זה יידרס ממילא!

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: עדכונים בנוגע לקריסת השרתים של ימות מהבוקר, בעז"ה הכל יסתדר וישוחזר, רק סבלנות!!! ולא לגעת סתם במערכות!

      @kol כתב בעדכונים בנוגע לקריסת השרתים של ימות מאמצע הלילה !!!:

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

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

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: עדכונים בנוגע לקריסת השרתים של ימות מהבוקר, בעז"ה הכל יסתדר וישוחזר, רק סבלנות!!! ולא לגעת סתם במערכות!

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

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: קו העדכונים

      @יעקב-1 כתב בקו העדכונים:

      יש למישהו מספר של קו העדכונים?
      (שמביא עדכונים בצורה אוטומטית כל הזמן)

      אתה מתכוון לזה? 0776003062
      תעשה העברה למערכת אחרת או ניתוביה כי זה חסום

      פורסם בשאלות ועזרה הדדית
      K
      kol
    • RE: המרת טקסט לדיבור מטורפת!!!! ב ח י נ ם!!!!!

      מישהו יודע איך אפשר להגדיר שידבר מהר יותר?

      פורסם בטיפים עצות והדגמות מהמשתמשים
      K
      kol
    • RE: המרת טקסט לדיבור מטורפת!!!! ב ח י נ ם!!!!!

      @מנסה תודה רבה

      פורסם בטיפים עצות והדגמות מהמשתמשים
      K
      kol