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

    📊 מציג לוגים מתקדם ומשוכלל

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

      מצורף בספוילר ‏‏קובץ להצגת הלוגים של המערכת (לוגי ymgr)
      יש להעתיק את הקוד, לשמור בפקס רשימות וכדו', ולקרוא לקובץ מציג_לוגים_מתקדם.html

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>מציג לוגים מתקדם</title>
          <style>
              /* ------------------- עיצוב כללי ------------------- */
              body {
                  font-family: 'Arial Hebrew', Arial, sans-serif;
                  margin: 0;
                  padding: 30px;
                  text-align: right;
                  direction: rtl;
                  background-color: #e9eef2;
                  color: #333;
              }
      
              .container {
                  max-width: 1700px;
                  margin: 0 auto;
              }
      
              h1 {
                  color: #1a5c92;
                  text-align: center;
                  margin-bottom: 30px;
                  font-size: 2em;
                  font-weight: bold;
              }
      
              /* ------------------- אזור הבקרה (פאנלים) ------------------- */
              .control-panel {
                  background-color: #ffffff;
                  padding: 20px;
                  border-radius: 10px;
                  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
                  margin-bottom: 25px;
                  /* מרווח בין הפאנלים */
              }
      
              .control-row {
                  display: flex;
                  align-items: center;
                  gap: 15px;
                  margin-bottom: 15px;
              }
      
              .control-panel label {
                  font-weight: bold;
                  color: #1a5c92;
                  white-space: nowrap;
              }
      
              .control-panel input[type="text"],
              .control-panel select {
                  flex-grow: 1;
                  padding: 10px;
                  border: 1px solid #ccc;
                  border-radius: 5px;
                  font-size: 1em;
                  text-align: right;
                  direction: rtl;
              }
      
              /* סטייל לאינפוט של נתיב ידני */
              .manual-path-input::placeholder {
                  color: #999;
                  font-style: italic;
              }
      
              .control-panel input#filter-input {
                  direction: rtl;
                  text-align: right;
                  margin-top: 5px;
              }
      
              .control-panel button {
                  background-color: #4CAF50;
                  color: white;
                  padding: 10px 20px;
                  border: none;
                  border-radius: 5px;
                  cursor: pointer;
                  font-size: 1em;
                  transition: background-color 0.3s;
              }
      
              .control-panel button:hover:not(:disabled) {
                  background-color: #45a049;
              }
      
              .control-panel button:disabled {
                  background-color: #a0a0a0;
                  cursor: not-allowed;
              }
      
              .control-panel .full-width {
                  flex-grow: 1;
              }
      
              .filter-group {
                  display: flex;
                  gap: 5px;
                  flex-grow: 1;
                  align-items: center;
              }
      
              .clear-filter-btn {
                  background-color: #c62828 !important;
                  padding: 10px 10px !important;
                  height: 40px;
                  font-size: 0.9em !important;
                  margin: 0;
                  white-space: nowrap;
              }
      
              .clear-filter-btn:hover:not(:disabled) {
                  background-color: #a02020 !important;
              }
      
              /* ------------------- בקרת נראות עמודות ------------------- */
              #column-visibility-panel {
                  background-color: #f7f7f7;
                  padding: 15px;
                  border-radius: 8px;
                  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
                  margin-top: 15px;
              }
      
              #column-visibility-panel h4 {
                  margin-top: 0;
                  color: #1a5c92;
                  border-bottom: 1px dashed #ccc;
                  padding-bottom: 5px;
                  margin-bottom: 10px;
                  /* 🆕 הוספנו פלקס כדי שהכפתור ישב ליד הכותרת */
                  display: flex;
                  align-items: center;
              }
      
              /* 🆕 סטייל לכפתור האיפוס */
              .reset-cols-btn {
                  background-color: #28a745 !important;
                  color: white;
                  padding: 4px 8px !important;
                  border: none;
                  border-radius: 4px;
                  cursor: pointer;
                  font-weight: bold;
                  font-size: 0.9em !important;
                  z-index: 100;
                  transition: background-color 0.3s;
                  direction: rtl;
                  margin-right: 15px;
                  /* מרווח מהכותרת */
                  height: auto !important;
                  line-height: normal !important;
                  white-space: nowrap;
              }
      
              .reset-cols-btn:hover:not(:disabled) {
                  background-color: #1e7e34 !important;
              }
      
              .col-checkbox-group {
                  display: flex;
                  flex-wrap: wrap;
                  gap: 15px;
              }
      
              .col-checkbox-group label {
                  font-weight: normal;
                  color: #333;
                  user-select: none;
                  white-space: nowrap;
              }
      
              .col-checkbox-group label input[type="checkbox"] {
                  display: inline-block;
              }
      
              /* ------------------- פקדי פגינציה ------------------- */
              .pagination-controls {
                  display: flex;
                  flex-direction: column;
                  justify-content: center;
                  /* ממורכז */
                  align-items: center;
                  padding: 10px 20px;
                  background-color: #f4f7f9;
                  border: 1px solid #ddd;
                  border-radius: 8px;
                  margin: 15px 0;
                  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
                  user-select: none;
                  flex-wrap: wrap;
                  gap: 10px;
                  /* מרווח בין קבוצות/טקסט */
              }
      
              /* קבוצת כפתורי הניווט ( Prev + Numbers + Next ) */
              .pagination-nav-group {
                  display: flex;
                  align-items: center;
                  flex-wrap: wrap;
                  margin: 5px 0;
                  /* מרווח עליון/תחתון קטן */
              }
      
              /* סטיילינג כפתורים כללי */
              .pagination-controls button {
                  padding: 8px 15px;
                  border: none;
                  border-radius: 5px;
                  cursor: pointer;
                  font-size: 0.9em;
                  transition: background-color 0.2s;
                  white-space: nowrap;
              }
      
              /* סטיילינג כפתורים "קודם" ו"הבא" */
              .pagination-controls .nav-button {
                  background-color: #1a5c92;
                  color: white;
                  padding: 8px 15px;
              }
      
              .pagination-controls .nav-button:hover:not(:disabled) {
                  background-color: #15476d;
              }
      
              .pagination-controls button:disabled {
                  background-color: #a0a0a0;
                  cursor: not-allowed;
              }
      
              /* רווחים בין כפתורי הניווט הראשיים לקבוצת המספרים */
              .pagination-controls .nav-button.prev {
                  margin-left: 10px;
              }
      
              .pagination-controls .nav-button.next {
                  margin-right: 10px;
              }
      
      
              .pagination-controls .status-text {
                  font-weight: bold;
                  color: #1a5c92;
                  white-space: nowrap;
                  margin: 5px 0;
                  text-align: center;
              }
      
              /* סטיילינג למספרי העמודים (השינוי העיקרי) */
              .pagination-controls .page-numbers {
                  display: flex;
                  align-items: center;
                  flex-wrap: wrap;
              }
      
              .pagination-controls .page-numbers button {
                  background-color: #f7f7f7;
                  color: #1a5c92;
                  padding: 8px 5px;
                  /* צמצום padding כדי לאפשר רוחב קבוע קטן יותר */
                  border: 1px solid #ccc;
                  margin: 0 3px;
                  font-weight: normal;
      
                  /* 🆕 רוחב קבוע למספרי העמודים */
                  min-width: 50px;
                  text-align: center;
                  /* ------------------------------------- */
              }
      
              .pagination-controls .page-numbers button.active {
                  background-color: #1a5c92;
                  color: white;
                  font-weight: bold;
                  border: 1px solid #1a5c92;
                  cursor: default;
              }
      
              .pagination-controls .page-numbers button:hover:not(.active):not(:disabled) {
                  background-color: #e0f2f1;
              }
      
      
              /* ------------------- אזור התוצאות ------------------- */
              #status-message {
                  margin-bottom: 15px;
                  padding: 10px;
                  border-radius: 5px;
                  font-weight: bold;
              }
      
              /* 🆕 סטייל מיוחד להבהרת הטוקן */
              /* הסרנו את המרווחים הפנימיים והחיצוניים מכיוון שזה עכשיו בתוך control-row חדש */
              #token-clarification {
                  font-size: 0.8em;
                  /* גופן לא גדול */
                  color: #555;
                  /* צבע אפור כהה */
                  text-align: right;
                  direction: rtl;
              }
      
              .data-table-container {
                  background-color: #ffffff;
                  padding: 20px;
                  border-radius: 10px;
                  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
                  overflow: auto;
                  margin-bottom: 25px;
              }
      
              .data-table {
                  width: 100%;
                  border-collapse: collapse;
              }
      
              /* Sticky Header */
              .data-table th {
                  border: 1px solid #ddd;
                  padding: 8px 12px;
                  text-align: center;
                  background-color: #f2f2f2;
                  color: #1a5c92;
                  font-weight: bold;
                  cursor: pointer;
                  user-select: none;
                  position: sticky;
                  top: 0;
                  z-index: 20;
                  max-width: 150px;
                  white-space: normal;
                  word-break: break-word;
              }
      
              .data-table th:after {
                  content: '';
                  position: absolute;
                  left: 10px;
                  top: 50%;
                  transform: translateY(-50%);
              }
      
              .data-table th.asc:after {
                  content: ' ▲';
                  color: #4CAF50;
              }
      
              .data-table th.desc:after {
                  content: ' ▼';
                  color: #c62828;
              }
      
              .data-table td {
                  border: 1px solid #ddd;
                  padding: 8px 12px;
                  text-align: center;
                  max-width: 150px;
                  white-space: normal;
                  overflow-x: auto;
                  word-break: break-word;
              }
      
              .data-table tr {
                  cursor: pointer;
                  transition: background-color 0.15s;
              }
      
              .data-table tr:nth-child(even) {
                  background-color: #f9f9f9;
              }
      
              .data-table tr:hover {
                  background-color: #e0f2f1;
              }
      
              /* הסתרת עמודות על פי ה-data-column-key */
              .col-hidden {
                  display: none;
              }
      
              /* הגדרת רוחב מקסימלי לתאי API ספציפיים */
              .data-table td[data-column-key="ApiSend"],
              .data-table td[data-column-key="ApiAnswer"] {
                  max-width: 350px;
                  font-size: 0.9em;
                  direction: ltr;
              }
      
              /* ------------------- פאנל פרטים ------------------- */
              #details-panel {
                  background-color: #fcfcfc;
                  border: 1px solid #ddd;
                  padding: 15px 25px;
                  border-radius: 10px;
                  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
                  margin-top: 20px;
                  margin-bottom: 20px;
                  display: none;
                  text-align: right;
                  /* 🆕 הוספת מיקום יחסי עבור כפתור הסגירה המוחלט */
                  position: relative;
              }
      
              /* 🆕 סטייל לכפתור הסגירה - מעודכן למיקום שמאלי וקטן */
              #details-panel .close-btn {
                  position: absolute;
                  top: 15px;
                  left: 20px;
                  /* 👈 מעביר את הכפתור לשמאל! */
                  background-color: #c62828;
                  color: white;
                  padding: 4px 8px;
                  /* מקטין את הכפתור */
                  border: none;
                  border-radius: 4px;
                  cursor: pointer;
                  font-weight: bold;
                  font-size: 0.9em;
                  /* מקטין את הפונט */
                  z-index: 100;
                  transition: background-color 0.3s;
                  direction: ltr;
              }
      
              #details-panel .close-btn:hover {
                  background-color: #a02020;
              }
      
              #details-panel h3 {
                  /* הסרנו את ה-padding-right שהיה מיותר */
                  /* שים לב! אם אתה משתמש ב-padding-left, זה יהיה המרווח משמאל (כפתור הסגירה) */
                  padding-left: 50px;
                  /* מרווח משמאל כדי למנוע התנגשות עם כפתור ה-X */
                  color: #1a5c92;
                  border-bottom: 2px solid #1a5c92;
                  padding-bottom: 5px;
                  margin-top: 0;
                  margin-bottom: 15px;
                  /* ודא שאין padding-right גבוה מדי שדוחף את הטקסט יותר מדי שמאלה */
              }
      
              #details-panel dl {
                  display: grid;
                  grid-template-columns: auto 1fr;
                  gap: 10px 20px;
                  margin: 0;
                  padding: 0;
              }
      
              #details-panel dt {
                  font-weight: bold;
                  color: #333;
                  grid-column: 1;
                  white-space: nowrap;
              }
      
              #details-panel dd {
                  margin: 0;
                  grid-column: 2;
                  text-align: left;
                  direction: ltr;
                  font-family: 'Consolas', 'Courier New', monospace;
                  font-size: 0.9em;
                  background-color: #eee;
                  padding: 5px;
                  border-radius: 3px;
                  overflow-x: auto;
                  max-width: 100%;
                  white-space: pre-wrap;
                  display: flex;
                  align-items: center;
                  gap: 10px;
              }
      
              #details-panel dd button {
                  background-color: #1a5c92;
                  color: white;
                  padding: 3px 8px;
                  border: none;
                  border-radius: 3px;
                  cursor: pointer;
                  font-size: 0.8em;
                  flex-shrink: 0;
                  transition: background-color 0.2s, color 0.2s;
              }
      
              #details-panel dd button:hover:not(:disabled) {
                  background-color: #15476d;
              }
      
              /* סטייל ייעודי לכפתור העתקה לאחר לחיצה */
              #details-panel dd button.copied-success {
                  background-color: #4CAF50;
                  /* ירוק */
              }
      
              #details-panel dd .value-text {
                  flex-grow: 1;
                  white-space: pre-wrap;
                  word-break: break-word;
              }
      
              /* עיצוב JSON מעוצב */
              #details-panel dd .json-formatted {
                  white-space: pre;
              }
      
              /* איפוס העיצוב המיוחד מה-option */
              #log-file-select option[disabled][selected] {
                  color: initial !important;
                  background-color: initial !important;
                  font-weight: initial !important;
                  font-size: initial !important;
                  padding: initial;
              }
      
              /* ------------------- מערכת סינון מתקדמת ------------------- */
              #advanced-filter-controls {
                  border: 1px dashed #1a5c92;
                  padding: 10px;
                  margin-bottom: 15px;
                  border-radius: 5px;
                  background-color: #fcfdff;
              }
      
              .filter-rule-row {
                  display: flex;
                  align-items: center;
                  gap: 10px;
                  padding: 8px 0;
                  border-bottom: 1px dotted #ddd;
              }
      
              .filter-rule-row:last-child {
                  border-bottom: none;
              }
      
              .filter-rule-row label {
                  font-weight: normal;
                  color: #333;
                  white-space: nowrap;
              }
      
              .filter-rule-row select,
              .filter-rule-row input[type="text"] {
                  padding: 8px;
                  border-radius: 4px;
                  font-size: 0.9em;
                  flex-grow: 1;
              }
      
              .filter-rule-row .col-select,
              .filter-rule-row .match-select {
                  flex-grow: 0;
                  min-width: 120px;
              }
      
              .filter-rule-row .value-input {
                  flex-grow: 3;
              }
      
              .filter-rule-row .remove-btn {
                  background-color: #dc3545 !important;
                  padding: 8px 10px !important;
                  height: auto;
                  font-size: 0.8em !important;
                  margin: 0;
                  flex-shrink: 0;
              }
      
              .filter-rule-row .remove-btn:hover:not(:disabled) {
                  background-color: #c82333 !important;
              }
          </style>
      </head>
      
      <body>
      
          <div class="container">
              <h1>📊 מציג לוגים מתקדם ומשוכלל</h1>
      
              <div class="control-panel" id="panel-auth-select">
                  <div class="control-row">
                      <label for="user-token">הזן טוקן (token):</label>
                      <input type="text" id="user-token" placeholder="הכנס כאן את הטוקן שלך - `api_key` (לעת עתה עד השינוי המתוכנן יש להכניס: `מערכת:סיסמה` )" class="example-token-value">
                      <button onclick="fetchFilesAndSetup()" id="fetch-files-btn">טען רשימת קבצי לוג 🔄</button>
                  </div>
                  <div class="control-row" style="margin-top: -10px; margin-bottom: 5px; align-items: flex-start;">
                      <label for="user-token" style="visibility: hidden; user-select: none; opacity: 0; pointer-events: none;">הזן טוקן (token):</label>
      
                      <div id="token-clarification" class="full-width">
                          הבהרה: הטוקן אינו נשמר בשום צורה, ואינו מועבר לשום מקום. הטוקן משמש את התוכנה בלבד, באופן מקומי בלבד, ובעת הפעולה הנוכחית בלבד.
                      </div>
      
                      <button style="visibility: hidden; pointer-events: none; background-color: transparent; border: none; padding: 10px 20px; font-size: 1em;"></button>
                  </div>
                  <div class="control-row" id="file-selection-row" style="display: none; margin-bottom: 0;">
                      <label for="log-file-select" id="log-file-label">בחר קובץ לוג להצגה:</label>
                      <select id="log-file-select" class="full-width" disabled
                          onchange="handleFileSelectionChange()"></select>
                      <button onclick="fetchData()" id="fetch-data-btn" disabled>הצג לוג נבחר</button>
                  </div>
      
                  <div class="control-row" id="manual-path-row" style="display: none; margin-top: 15px;">
                      <label for="manual-path-input">נתיב תיקייה/קובץ:</label>
                      <input type="text" id="manual-path-input" class="full-width manual-path-input"
                          placeholder="הזן נתיב (ללא הקידומת `ivr2:` ). או נתיב תיקייה (לדוגמה: `4/2/6` ), או נתיב קובץ מלא (לדוגמה: `Log/LogPlaybackPlayStop/LogPlaybackPlayStop.2025-10-17.ymgr` )"
                          onkeypress="if(event.key === 'Enter') { processManualPathEntry(); }">
                      <button onclick="processManualPathEntry()" id="confirm-manual-path-btn">טען</button>
                  </div>
                  </div>
              <div id="status-message"></div>
      
              <div class="control-panel" id="panel-export"
                  style="display: none; padding: 15px 20px; justify-content: flex-start;">
                  <div class="control-row" style="margin-bottom: 0;">
                      <label style="font-weight: bold; color: #1a5c92; margin-left: 20px;">ייצוא טבלה:</label>
                      <button onclick="exportData('excel', this)" id="export-excel-btn" disabled
                          style="background-color: #1e7e34;">ייצא ל-CSV (תואם Excel) 📊</button>
                  </div>
              </div>
      
              <div class="control-panel" id="panel-filter-cols" style="display: none;">
                  <div id="advanced-filter-controls">
                  </div>
                  <div class="control-row" style="margin-top: 15px; justify-content: flex-end; gap: 15px;">
                      <button onclick="addFilterRule()" style="background-color: #28a745; margin-left: auto;">+ הוסף תנאי
                          סינון</button>
                      <label for="filter-logic-select" style="margin: 0; font-weight: bold;">לוגיקת סינון כללית:</label>
                      <select id="filter-logic-select" onchange="filterTable()" style="width: auto; flex-grow: 0;">
                          <option value="AND">AND (חייב לעמוד בכל התנאים)</option>
                          <option value="OR">OR (חייב לעמוד לפחות באחד התנאים)</option>
                      </select>
                      <button onclick="clearAllFilters()" class="clear-filter-btn" disabled>נקה הכל 🧹</button>
                      <button onclick="filterTable()" id="apply-filter-btn">הפעל סינון 🔎</button>
                  </div>
      
                  <div id="column-visibility-panel">
                      <h4>
                          👁️ שליטה בנראות עמודות:
                          <button class="reset-cols-btn" onclick="resetColumnVisibilityToDefault()">איפוס לברירת מחדל ♻️</button>
                      </h4>
                      <div id="col-checkbox-container" class="col-checkbox-group">
                      </div>
                  </div>
              </div>
              <div id="details-panel" style="display: none;">
                  <button class="close-btn" onclick="closeDetailsPanel()">X סגור</button>
      
                  <h3>פרטי שורה נבחרת</h3>
                  <dl id="details-content"></dl>
              </div>
      
              <div class="data-table-container">
                  <div id="results">
                      <p style="text-align: center; color: #666;">שלב 1: אנא הכנס טוקן ולחץ על "טען רשימת קבצי לוג" כדי
                          להתחיל.</p>
                  </div>
              </div>
          </div>
      
          <script>
              // ה-URL הקבוע
              const API_BASE = "https://www.call2all.co.il/ym/api/";
              const API_DIR_PATH = "GetIVR2Dir";
              const API_RENDER_FILE = "RenderYMGRFile"; // משתנה ללא convertType
      
              // ------------------- משתנים גלובליים לפגינציה ודאטה -------------------
              let currentColumns = [];
              let sortState = { columnIndex: -1, direction: 'asc' };
              let tableData = [];
              let selectedFileName = '';
      
              // 🆕 משתנים חדשים לפגינציה, דאטה מסונן/ממוין, ומצב נראות העמודות
              let filteredAndSortedData = []; // הדאטה אחרי סינון ומיון (ממנו נציג את ה-slice)
              const ROWS_PER_PAGE = 100; // קובע כמה שורות להציג בכל עמוד
              let currentPage = 1; // העמוד הנוכחי
      
              let filterRules = []; // מערך שיכיל את כללי הסינון המורכבים
      
              // 🆕 משתנה גלובלי זמני לשמירת מצב נראות העמודות *עבור הלוג הנוכחי בלבד*
              let currentColumnVisibilityState = {};
      
      
              document.addEventListener('DOMContentLoaded', () => {
                  const tokenInput = document.getElementById('user-token');
      
                  tokenInput.addEventListener('focus', function () {
                      this.classList.remove('example-token-value');
                  });
      
                  tokenInput.addEventListener('blur', function () {
                      if (this.value === '') {
                          this.classList.remove('example-token-value');
                      }
                  });
      
                  tokenInput.addEventListener('keypress', function (event) {
                      if (event.key === 'Enter') {
                          event.preventDefault();
                          fetchFilesAndSetup();
                      }
                  });
      
                  // הסתרת הפאנלים בתחילה
                  document.getElementById('panel-export').style.display = 'none';
                  document.getElementById('panel-filter-cols').style.display = 'none';
              });
      
              /**
               * מטפל בשינוי בחירת הקובץ. מציג/מסתיר את שדה הקלט הידני.
               */
              function handleFileSelectionChange() {
                  const selectElement = document.getElementById('log-file-select');
                  const manualPathRow = document.getElementById('manual-path-row');
      
                  if (selectElement.value === '__other__') {
                      manualPathRow.style.display = 'flex';
                  } else {
                      manualPathRow.style.display = 'none';
                  }
              }
              
              // ------------------- הוספת פונקציית העזר populateLogDropdown -------------------
              /**
               * פונקציית עזר למילוי רשימת הגלילה של הלוגים (משותף לטעינה ראשונית וטעינה ידנית).
               * @param {Array<Object>} allYmgrFiles - רשימת הקבצים שמכילה את שדה what ו-mtime.
               * @param {string} initialText - הטקסט שיופיע בכותרת ברירת המחדל של הגלילה.
               * @param {boolean} isRecursiveSearch - האם התוצאות הגיעו מחיפוש רקורסיבי (כדי להחליט מה לבחור אוטומטית).
               */
              function populateLogDropdown(allYmgrFiles, initialText, isRecursiveSearch = true) {
                  const select = document.getElementById('log-file-select');
                  const selectRow = document.getElementById('file-selection-row');
                  const fetchDataBtn = document.getElementById('fetch-data-btn');
                  const statusDiv = document.getElementById('status-message');
                  const resultsDiv = document.getElementById('results');
                  const manualPathRow = document.getElementById('manual-path-row');
      
                  // מיון וניקוי
                  allYmgrFiles.sort((a, b) => a.what.localeCompare(b.what));
                  // 1. הגדרת שורת ההוראה כ-disabled ו-selected
                  select.innerHTML = `<option value="" disabled selected>${initialText}</option>`;
      
                  // הוספת אפשרות "אחר"
                  select.innerHTML += '<option value="__other__">אחר (הזנה ידנית)</option>';
                  
                  // הוספת הקבצים שנמצאו
                  allYmgrFiles.forEach(f => {
                      const option = document.createElement('option');
                      option.value = f.what;
      
                      // החלפת 'ivr2:' ב-''
                      const friendlyName = f.what.replace('ivr2:', '').replace(/\//g, '/');
                      option.textContent = `${friendlyName} (${f.mtime || 'אין תאריך'})`;
      
                      select.appendChild(option);
                  });
      
                  select.disabled = false;
                  fetchDataBtn.disabled = false;
                  selectRow.style.display = 'flex';
                  manualPathRow.style.display = 'none'; 
      
                  const fileCount = allYmgrFiles.length;
                  
                  if (fileCount > 0) {
                       // 2. שמירת שורת ההוראה כברירת מחדל, אלא אם זה קובץ יחיד שהוזן ידנית
                       if (fileCount === 1 && !isRecursiveSearch) {
                          // מקרה מיוחד: נמצא קובץ לוג יחיד מחיפוש לא רקורסיבי (כנראה קובץ ספציפי הוזן)
                          select.value = allYmgrFiles[0].what;
                          statusDiv.innerHTML = `<span style="color:#2E7D32;">✅ נמצא קובץ לוג בודד: ${allYmgrFiles[0].what.replace('ivr2:', '')}. אנא לחץ "הצג לוג נבחר".</span>`;
                       } else {
                          // מקרה כללי: נמצאו מספר קבצים או קובץ אחד בחיפוש רקורסיבי.
                          // משאירים את ה-select על הערך הריק שנבחר בשלב 1 (שורת ההוראה).
                          statusDiv.innerHTML = `<span style="color:#2E7D32;">✅ רשימת הקבצים נטענה בהצלחה. נמצאו ${fileCount} קבצי לוג. אנא בחר קובץ ולחץ "הצג לוג נבחר".</span>`;
                       }
                       resultsDiv.innerHTML = '<p style="text-align: center; color: #1a5c92;">שלב 2/3: בחר קובץ לוג מהרשימה ולחץ על "הצג לוג נבחר".</p>';
                  } else {
                      // אם לא נמצאו קבצים
                      select.value = '__other__'; 
                      handleFileSelectionChange(); // מציג שוב את שדה הקלט
                      statusDiv.innerHTML = `<span style="color:#c62828;">❌ לא נמצאו קבצי לוג בנתיב המבוקש. אנא נסה נתיב אחר.</span>`;
                      resultsDiv.innerHTML = '<p style="text-align: center; color: #c62828;">אנא הזן נתיב חדש ובצע חיפוש.</p>';
                  }
              }
              // ------------------- סוף הוספת פונקציית העזר -------------------
      
      
              /**
               * פונקציה רקורסיבית לטעינת כל קבצי ה-YMGR מנתיב נתון ומתיקיות המשנה שלו.
               */
              async function fetchFilesFromPath(path, token, allFiles = []) {
                  const encodedPath = encodeURIComponent(path);
                  const fullUrl = `${API_BASE}${API_DIR_PATH}?path=${encodedPath}&token=${token}`;
      
                  try {
                      const response = await fetch(fullUrl);
                      const jsonResponse = await response.json();
      
                      // 🆕 שינוי 2: בדיקה אם הטוקן אינו תקין (ResponseStatus אינו OK).
                      if (jsonResponse.responseStatus !== 'OK') {
                          // זורק שגיאה עם המידע המלא, שתטופל בפונקציה הקוראת
                          throw new Error(`API_ERROR:${jsonResponse.responseStatus}:${jsonResponse.message}`);
                      }
      
                      const ymgrFiles = jsonResponse.files.filter(f => f.fileType === 'YMGR');
                      allFiles.push(...ymgrFiles);
      
                      for (const dir of jsonResponse.dirs) {
                          const newPath = `${path}/${dir.name}`;
                          // יש להבטיח ששגיאות בנתיבי משנה לא יעצרו את הכל
                          await fetchFilesFromPath(newPath, token, allFiles);
                      }
      
                      return allFiles;
      
                  } catch (error) {
                      // אם השגיאה היא שגיאת API שזרקנו (טוקן לא תקין) - נזרוק אותה שוב.
                      if (error.message.startsWith('API_ERROR')) {
                          throw error;
                      }
                      console.error(`❌ שגיאת רשת/קוד בטעינת נתיב: ${path}`, error);
                      // במקרה של שגיאת רשת אחרת - נחזיר את מה שיש ונמשיך
                      return allFiles;
                  }
              }
      
              /**
               * פונקציה לטעינת רשימת קבצי ה-YMGR (כולל רקורסיה) והכנת הממשק לבחירה.
               */
              async function fetchFilesAndSetup() {
                  const tokenInput = document.getElementById('user-token');
                  const token = tokenInput.value.trim();
                  const statusDiv = document.getElementById('status-message');
                  const select = document.getElementById('log-file-select');
                  const selectRow = document.getElementById('file-selection-row');
                  const fetchDataBtn = document.getElementById('fetch-data-btn');
                  const resultsDiv = document.getElementById('results');
      
                  const manualPathRow = document.getElementById('manual-path-row');
      
                  const exportPanel = document.getElementById('panel-export');
                  const filterPanel = document.getElementById('panel-filter-cols');
                  const exportExcelBtn = document.getElementById('export-excel-btn');
      
                  statusDiv.innerHTML = '<span style="color:#1a5c92;">טוען רשימת קבצים באופן רקורסיבי... 🔄</span>';
                  select.innerHTML = '<option value="">טוען...</option>';
                  select.disabled = true;
                  fetchDataBtn.disabled = true;
                  resultsDiv.innerHTML = '<p style="text-align: center; color: #666;">מחפש קבצי לוג בכל תיקיות המשנה...</p>';
                  document.getElementById('details-panel').style.display = 'none';
      
                  document.getElementById('apply-filter-btn').disabled = true;
                  document.querySelector('.clear-filter-btn').disabled = true;
      
      
                  manualPathRow.style.display = 'none';
      
                  exportPanel.style.display = 'none';
                  filterPanel.style.display = 'none';
                  exportExcelBtn.disabled = true;
      
      
                  if (!token) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא הכנס טוקן תקין.</span>`;
                      return;
                  }
      
                  tokenInput.classList.remove('example-token-value');
      
                  const initialPath = 'ivr2:/Log';
                  let allYmgrFiles = [];
                  let isTokenInvalid = false;
      
                  try {
                      // שימוש ב-await כיוון ש-fetchFilesFromPath עשויה לזרוק שגיאה
                      allYmgrFiles = await fetchFilesFromPath(initialPath, token);
                  } catch (error) {
                      if (error.message.startsWith('API_ERROR')) {
                          const parts = error.message.split(':');
                          const status = parts[1];
                          const msg = parts.slice(2).join(':');
      
                          // 🆕 שינוי 2: טיפול בהודעות שגיאה ספציפיות לטוקן לא תקין
                          if (status === 'FORBIDDEN' || status === 'EXCEPTION' || msg.includes('session token is invalid') || msg.includes('user name or password do not match')) {
                              statusDiv.innerHTML = `<span style="color:#c62828; font-size:1.1em; font-weight: bold;">❌ שגיאת טוקן: הטוקן אינו תקין או פג תוקף.</span>
                                  <span style="font-size: 0.9em; color:#888;"> פרטי שגיאה: ${msg}</span>`;
                              isTokenInvalid = true;
                          } else {
                              // שגיאת API אחרת
                              statusDiv.innerHTML = `<span style="color:#c62828;">❌ שגיאת API כללית בטעינת רשימת הקבצים: ${msg}</span>`;
                              isTokenInvalid = true; // נתייחס לזה כשגיאה חוסמת
                          }
                      } else {
                          // שגיאת רשת או אחרת (שגיאת קוד)
                          statusDiv.innerHTML = `<span style="color:#c62828;">❌ שגיאה כללית או שגיאת רשת. אנא בדוק את הקונסול.</span>`;
                          isTokenInvalid = true;
                      }
                  }
      
      
                  if (isTokenInvalid) {
                      resultsDiv.innerHTML = '<p style="text-align: center; color: #c62828;">אנא תקן את הטוקן ונסה שוב.</p>';
                      return; // יציאה במקרה של שגיאת טוקן
                  }
      
      
                  if (allYmgrFiles.length === 0) {
                      statusDiv.innerHTML = `<span style="color:#1a5c92;">✅ הנתונים נטענו בהצלחה, אך לא נמצאו קבצי YMGR בנתיב: ${initialPath} או בתיקיות המשנה.</span>`;
      
                      // במקרה שלא נמצאו קבצים, עדיין מאפשרים הזנה ידנית:
                      select.innerHTML = '<option value="" disabled selected>בחר קובץ לוג להצגה...</option><option value="__other__" selected>אחר (הזנה ידנית)</option>';
                      select.disabled = false;
                      fetchDataBtn.disabled = false;
                      selectRow.style.display = 'flex';
                      manualPathRow.style.display = 'flex';
                      resultsDiv.innerHTML = '<p style="text-align: center; color: #1a5c92;">שלב 2: לא נמצאו קבצים אוטומטית. ניתן להזין נתיב ידני למטה.</p>';
                      return;
                  }
                  
                  // 🆕 שימוש בפונקציית העזר החדשה במקום הלוגיקה המקורית
                  const initialText = 'שים לב! כברירת מחדל מוצגים ברשימה רק לוגים מתיקיית `Log`, לבחירת לוג מנתיב אחר בחר `אחר`.';
                  populateLogDropdown(allYmgrFiles, initialText, true);
              }
      
              // ------------------- הוספת פונקציית processManualPathEntry -------------------
              /**
               * פונקציה שמטפלת בהזנה ידנית של נתיב (תיקייה או קובץ) וממלאת את רשימת הגלילה.
               */
              async function processManualPathEntry() {
                  const token = document.getElementById('user-token').value.trim();
                  const manualPathInput = document.getElementById('manual-path-input');
                  const manualPath = manualPathInput.value.trim();
                  const statusDiv = document.getElementById('status-message');
                  const select = document.getElementById('log-file-select');
                  const selectRow = document.getElementById('file-selection-row');
                  const fetchDataBtn = document.getElementById('fetch-data-btn');
                  const resultsDiv = document.getElementById('results');
                  const manualPathRow = document.getElementById('manual-path-row');
      
                  if (!token) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא הכנס טוקן תקין.</span>`;
                      return;
                  }
      
                  if (!manualPath) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא הזן נתיב לוג או תיקייה.</span>`;
                      return;
                  }
                  
                  // איפוס UI
                  statusDiv.innerHTML = `<span style="color:#1a5c92;">מבצע חיפוש בנתיב: ${manualPath} 🔄</span>`;
                  select.disabled = true;
                  fetchDataBtn.disabled = true;
                  selectRow.style.display = 'flex';
                  resultsDiv.innerHTML = '<p style="text-align: center; color: #666;">מחפש קבצי לוג...</p>';
                  
                  const cleanPath = manualPath.replace(/^\/|\/$/g, '');
                  const basePath = `ivr2:${cleanPath}`;
                  let allYmgrFiles = [];
                  let isApiError = false;
      
                  // 1. בדיקה אם הנתיב הוא קובץ ספציפי (מסתיים ב-.ymgr)
                  if (cleanPath.toLowerCase().endsWith('.ymgr')) {
                      // אם הוזן קובץ ספציפי - מוסיפים אותו בלבד לרשימה
                      const fakeFile = {
                          what: basePath,
                          name: cleanPath.split('/').pop(),
                          fileType: 'YMGR',
                          mtime: 'נתיב ידני',
                          size: 0
                      };
                      allYmgrFiles = [fakeFile];
                      const initialText = 'נמצא קובץ יחיד, ניתן להציג אותו או לבחור בחיפוש ידני נוסף.';
                      populateLogDropdown(allYmgrFiles, initialText, false); // false = לא רקורסיבי
                      return;
                  }
      
                  // 2. נניח שמדובר בנתיב תיקייה ומבצעים חיפוש רקורסיבי
                  try {
                      allYmgrFiles = await fetchFilesFromPath(basePath, token);
                  } catch (error) {
                       if (error.message.startsWith('API_ERROR')) {
                          const parts = error.message.split(':');
                          const status = parts[1];
                          const msg = parts.slice(2).join(':');
                          if (status === 'FORBIDDEN' || status === 'EXCEPTION' || msg.includes('session token is invalid') || msg.includes('user name or password do not match')) {
                              statusDiv.innerHTML = `<span style="color:#c62828; font-size:1.1em; font-weight: bold;">❌ שגיאת טוקן: הטוקן אינו תקין או פג תוקף.</span>
                                  <span style="font-size: 0.9em; color:#888;"> פרטי שגיאה: ${msg}</span>`;
                              isApiError = true;
                          } else if (msg.includes('dir not found')) {
                              // זו כנראה לא שגיאה חוסמת, אלא שהנתיב שהוזן לא נמצא, נמשיך הלאה.
                              console.warn(`הנתיב שהוזן לא נמצא כתיקייה: ${manualPath}. מנסה להניח שמדובר בטקסט חלקי...`);
                          }
                      }
                  }
                  
                  if (isApiError) return;
      
                  // 3. מילוי רשימת הגלילה עם הקבצים שנמצאו (אם נמצאו)
                  const initialText = `נמצאו לוגים בנתיב: ${manualPath}.`;
                  populateLogDropdown(allYmgrFiles, initialText, true);
              }
              // ------------------- סוף הוספת הפונקציה -------------------
      
      
              /**
               * מנתח מחרוזת CSV באמצעות הספרייה PapaParse.
               */
              function parseCSV(csv) {
                  const results = Papa.parse(csv, {
                      header: true,
                      skipEmptyLines: true,
                      trimHeaders: true,
                      delimiter: ',',
                  });
                  return results.data;
              }
      
              /**
               * פונקציה לטעינת הנתונים מהקובץ הנבחר והצגתם.
               */
              async function fetchData() {
                  const token = document.getElementById('user-token').value.trim();
                  const selectElement = document.getElementById('log-file-select');
                  let selectedFileWhat = selectElement.value;
                  let finalWhatPath;
      
                  // אם לא נבחר קובץ ספציפי
                  if (!selectedFileWhat) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא בחר קובץ לוג מהרשימה או בחר 'אחר'.</span>`;
                      return;
                  }
      
                  // 🆕 טיפול בנתיב ידני (אם נבחר 'אחר') - מפנה ללחיצה על הכפתור החדש
                  if (selectedFileWhat === '__other__') {
                      const statusDiv = document.getElementById('status-message');
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא בחר קובץ לוג מהרשימה. לבחירת לוגים מנתיב אחר, הזן נתיב בתיבה ולחץ 'חפש קבצים/אישור'.</span>`;
                      document.getElementById('manual-path-row').style.display = 'flex'; // ודא שהקלט הידני גלוי
                      return;
                  }
                  // ------------------------------------------------------------------
      
                  finalWhatPath = selectedFileWhat; // השתמש בנתיב המלא מהרשימה
      
                  // מציאת שם הקובץ הנבחר (כולל הנתיב המוצג)
                  selectedFileName = selectElement.options[selectElement.selectedIndex].textContent; // 🆕 שמירה בגלובלי
                  
                  const resultsDiv = document.getElementById('results');
                  const statusDiv = document.getElementById('status-message');
                  const exportPanel = document.getElementById('panel-export');
                  const filterPanel = document.getElementById('panel-filter-cols');
                  const exportExcelBtn = document.getElementById('export-excel-btn');
      
                  if (!token) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ אנא הכנס טוקן תקין.</span>`;
                      return;
                  }
                  
      
                  resultsDiv.innerHTML = '<div style="text-align:center; color:#1a5c92;">טוען נתונים מהלוג המבוקש... 🔄</div>';
                  statusDiv.innerHTML = '<span style="color:#1a5c92;">טוען נתונים מהלוג המבוקש... 🔄</span>';
                  document.getElementById('apply-filter-btn').disabled = true;
                  document.querySelector('.clear-filter-btn').disabled = true;
                  exportExcelBtn.disabled = true;
                  document.getElementById('details-panel').style.display = 'none';
                  exportPanel.style.display = 'none';
                  filterPanel.style.display = 'none';
      
                  const encodedWhat = encodeURIComponent(finalWhatPath);
                  // שינוי ה-convertType ל-csv
                  const fullUrl = `${API_BASE}${API_RENDER_FILE}?convertType=csv&token=${token}&wath=${encodedWhat}`;
      
                  try {
                      const response = await fetch(fullUrl);
                      const rawText = await response.text();
      
                      if (rawText.includes('Not Found') || rawText.includes('File not found') || rawText.includes('session token is invalid') || rawText.includes('user name or password do not match')) {
                          statusDiv.innerHTML = `<span style="color:#c62828;">❌ שגיאה: לא ניתן לטעון את הלוג. ייתכן שהקובץ לא קיים, הטוקן אינו תקין או שגיאת API.</span>`;
                          resultsDiv.innerHTML = '';
                          return;
                      }
      
                      // טיפול בקובץ ריק או ללא תוכן (כותרות בלבד)
                      const lines = rawText.trim().split('\n');
                      if (lines.length <= 1) {
                          statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ הקובץ נטען, אך הוא ריק או מכיל כותרות בלבד.</span>`;
                          resultsDiv.innerHTML = '';
                          return;
                      }
      
      
                      const csv = rawText;
                      tableData = parseCSV(csv);
      
                      if (tableData.length === 0) {
                          statusDiv.innerHTML = `<span style="color:#c62828;">⚠️ שגיאת ניתוח CSV או שאין נתונים בלוג.</span>`;
                          resultsDiv.innerHTML = '';
                          return;
                      }
      
                      // קביעת העמודות מתוך כותרות ה-CSV
                      currentColumns = Object.keys(tableData[0]);
      
                      // 🆕 איפוס מצב הנראות הגלובלי (הזמני)
                      currentColumnVisibilityState = {};
      
                      // הגדרת הנתונים המסוננים והממוינים ככל הנתונים בהתחלה
                      filteredAndSortedData = [...tableData];
                      currentPage = 1; // איפוס לעמוד הראשון
      
                      // קריאה לפונקציה החדשה להגדרת נראות העמודות
                      setupColumnVisibilityControls(currentColumns, selectedFileName);
                      renderPaginatedTable(filteredAndSortedData, currentColumns);
                      attachSortListeners(currentColumns);
      
                      // הפעלת מערכת הסינון המתקדמת
                      setupAdvancedFilterControls(currentColumns);
      
                      // עדכון פקדים
                      document.getElementById('apply-filter-btn').disabled = false;
                      document.querySelector('.clear-filter-btn').disabled = false;
                      exportExcelBtn.disabled = false;
      
                      // הצגת פאנלי הייצוא והסינון
                      exportPanel.style.display = 'flex';
                      filterPanel.style.display = 'block';
      
                      const recordCount = tableData.length;
                      // עדכון טקסט הסטטוס
                      statusDiv.innerHTML = `<span style="color:#2E7D32;">✅ הנתונים נטענו בהצלחה. סה"כ שורות בלוג: ${recordCount}. פרטי הלוג: ${selectedFileName}.</span>`;
      
                  } catch (error) {
                      console.error("Fetch/Parsing Error:", error);
                      statusDiv.innerHTML = `<span style="color:#c62828;">❌ שגיאה כללית בטעינת הנתונים או בניתוח ה-CSV. אנא בדוק את הקונסול.</span>`;
                      resultsDiv.innerHTML = '';
                  }
              }
      
              /**
               * פונקציה לייצוא נתוני הטבלה לקובץ CSV/Excel עם פידבק UI.
               */
              function exportData(type, buttonElement) {
                  if (!tableData || tableData.length === 0) {
                      alert("אין נתונים לייצוא.");
                      return;
                  }
      
                  const originalText = buttonElement.textContent;
                  const originalBg = buttonElement.style.backgroundColor;
      
                  // 1. UI Feedback - Start
                  buttonElement.textContent = 'מייצא...⏳';
                  buttonElement.style.backgroundColor = '#ffc107';
                  buttonElement.disabled = true;
      
                  const columns = currentColumns;
                  const mimeType = 'text/csv;charset=utf-8;'; // CSV with BOM
      
                  // לוגיקה ליצירת שם קובץ
                  let baseName = selectedFileName;
                  const parts = baseName.split('(');
                  let mainPart = parts[0].trim().replace(/\s*>\s*/g, '/');
                  if (mainPart.endsWith('.ymgr')) {
                      mainPart = mainPart.substring(0, mainPart.lastIndexOf('.'));
                  }
                  
                  const filename = `Log_Export_${mainPart.replace(/[^a-zA-Z0-9]/g, '_')}_${new Date().toISOString().slice(0, 10)}.csv`;
      
                  // 3. יצירת CSV
                  const csvData = [columns.join(',')];
                  filteredAndSortedData.forEach(item => {
                      const row = columns.map(col => {
                          let value = item[col] !== undefined ? String(item[col]) : '';
                          // 4. ניקוי והגנה - החלף גרשיים כפולים (") בגרשיים כפולים כפולים ("") ועטוף הכל בגרשיים כפולים אם מכיל פסיקים או גרשיים.
                          value = value.replace(/"/g, '""');
                          if (value.includes(',') || value.includes('\n') || value.includes('"')) {
                              value = `"${value}"`;
                          }
                          return value;
                      }).join(',');
                      csvData.push(row);
                  });
      
                  const csv = csvData.join('\n');
      
                  // 4. הורדה לקובץ (כולל BOM לתמיכה טובה בעברית ב-Excel)
                  const blob = new Blob(["\uFEFF", csv], { type: mimeType });
                  const link = document.createElement('a');
      
                  if (link.download !== undefined) {
                      const url = URL.createObjectURL(blob);
                      link.setAttribute('href', url);
                      link.setAttribute('download', filename);
                      link.style.visibility = 'hidden';
                      document.body.appendChild(link);
                      link.click();
                      document.body.removeChild(link);
      
                      // 5. UI Feedback - Success
                      buttonElement.textContent = '✅ הושלם';
                      buttonElement.style.backgroundColor = '#28a745';
      
                      // Revert after a short delay
                      setTimeout(() => {
                          buttonElement.textContent = originalText;
                          buttonElement.style.backgroundColor = originalBg;
                          buttonElement.disabled = false;
                      }, 2000);
                  } else {
                      alert('הדפדפן אינו תומך בהורדה אוטומטית. אנא העתק את הנתונים ידנית.');
                      // 5. UI Feedback - Revert on failure
                      buttonElement.textContent = originalText;
                      buttonElement.style.backgroundColor = originalBg;
                      buttonElement.disabled = false;
                  }
              }
      
              /**
               * פונקציה לייצור HTML של פקדי הפגינציה (כולל מספרי עמודים).
               */
              function createPaginationControls(dataLength, current, total) {
                  if (total <= 1) return '';
      
                  const isPrevDisabled = current === 1;
                  const isNextDisabled = current === total;
                  const startIndex = (current - 1) * ROWS_PER_PAGE + 1;
                  let endIndex = Math.min(startIndex + ROWS_PER_PAGE - 1, dataLength);
      
                  let pageNumbersHtml = '';
                  const MAX_PAGES_SHOWN = 5; // מספר הכפתורים המקסימלי שיוצג
      
                  // חישוב טווח הכפתורים להצגה
                  let startPage = Math.max(1, current - Math.floor(MAX_PAGES_SHOWN / 2));
                  let endPage = Math.min(total, startPage + MAX_PAGES_SHOWN - 1);
      
                  // התאמה אם הגענו לסוף
                  if (endPage - startPage + 1 < MAX_PAGES_SHOWN) {
                      startPage = Math.max(1, endPage - MAX_PAGES_SHOWN + 1);
                  }
      
                  // בניית כפתורי מספרי העמודים
                  pageNumbersHtml += '<div class="page-numbers">';
      
                  // כפתור '1...' אם אנחנו לא קרובים להתחלה
                  if (startPage > 1) {
                      pageNumbersHtml += `<button onclick="goToPage(1)">1...</button>`;
                  }
      
                  // כפתורי העמודים המרכזיים
                  for (let i = startPage; i <= endPage; i++) {
                      const activeClass = i === current ? 'active' : '';
                      pageNumbersHtml += `<button class="${activeClass}" onclick="goToPage(${i})">${i}</button>`;
                  }
      
                  // כפתור '...Total' אם אנחנו לא קרובים לסוף
                  if (endPage < total) {
                      pageNumbersHtml += `<button onclick="goToPage(${total})">...${total}</button>`;
                  }
      
                  pageNumbersHtml += '</div>';
      
                  // יצירת פקדי הפגינציה
                  return `
                      <div class="pagination-controls">
                          <div class="pagination-nav-group">
                              <button class="nav-button prev" onclick="goToPage(${current - 1})" ${isPrevDisabled ? 'disabled' : ''}>« הקודם</button>
                              ${pageNumbersHtml}
                              <button class="nav-button next" onclick="goToPage(${current + 1})" ${isNextDisabled ? 'disabled' : ''}>הבא »</button>
                          </div>
                          <div class="status-text">
                              מציג שורות ${startIndex} עד ${endIndex} מתוך ${dataLength} | עמוד ${current} מתוך ${total}
                          </div>
                      </div>
                  `;
              }
      
              /**
               * מנווט לעמוד פגינציה ספציפי ומרנדר את הטבלה מחדש.
               */
              function goToPage(pageNumber) {
                  if (pageNumber === currentPage || pageNumber < 1) return;
                  const totalPages = Math.ceil(filteredAndSortedData.length / ROWS_PER_PAGE);
                  if (pageNumber > totalPages && totalPages > 0) return;
      
                  currentPage = pageNumber;
                  renderPaginatedTable(filteredAndSortedData, currentColumns);
      
                  // גלילה למעלה לראש הטבלה
                  document.getElementById('results').scrollIntoView({ behavior: 'smooth', block: 'start' });
              }
      
      
              /**
               * מרנדר את הטבלה עם נתונים מפוגננים (slice).
               * @param {Array<Object>} data - הנתונים המסונן/ממוין
               * @param {Array<string>} columns - רשימת העמודות
               */
              function renderPaginatedTable(data, columns) {
                  const resultsDiv = document.getElementById('results');
                  const dataLength = data.length;
      
                  // 1. חישוב פגינציה
                  const totalPages = Math.ceil(dataLength / ROWS_PER_PAGE);
                  // ודא שהעמוד הנוכחי נמצא בטווח
                  if (currentPage > totalPages && totalPages > 0) {
                      currentPage = totalPages;
                  } else if (currentPage < 1) {
                      currentPage = 1;
                  } else if (totalPages === 0) {
                      currentPage = 0;
                  }
      
                  const startIndex = (currentPage - 1) * ROWS_PER_PAGE;
                  const endIndex = startIndex + ROWS_PER_PAGE;
                  const currentSlice = data.slice(startIndex, endIndex);
      
                  // 2. יצירת פקדי הפגינציה
                  const paginationControlsHtml = createPaginationControls(dataLength, currentPage, totalPages);
                  let html = paginationControlsHtml; // הוספת הפקדים מעל הטבלה
      
                  // אם אין נתונים בסינון, הצג הודעה
                  if (dataLength === 0) {
                      html += '<p style="text-align: center; color: #c62828; font-weight: bold; margin-top: 20px;">❌ לא נמצאו שורות תואמות לכללי הסינון הנוכחיים.</p>';
                      resultsDiv.innerHTML = html;
                      return;
                  }
      
                  // 3. בניית הטבלה
                  html += '<table class="data-table" id="call-log-table"><thead><tr>';
                  
                  // עמודת אינדקס
                  html += '<th data-column-key="__index_col__">#</th>';
      
                  // יצירת הכותרות (Headers)
                  columns.forEach(col => {
                      const headerClass = sortState.columnIndex !== -1 && columns[sortState.columnIndex - 1] === col ? sortState.direction : '';
                      html += `<th data-column-key="${col}" class="${headerClass}">${col}</th>`;
                  });
                  html += '</tr></thead><tbody>';
      
                  // בניית שורות הטבלה (מה-slice הנוכחי)
                  currentSlice.forEach((item, indexInSlice) => {
                      // האינדקס הגלובלי (במערך הממוין/מסונן)
                      const globalIndex = startIndex + indexInSlice;
                      html += `<tr onclick="showRowDetails(this, ${globalIndex})">`;
                      
                      // הצגת האינדקס הגלובלי
                      html += `<td>${globalIndex + 1}</td>`;
      
                      columns.forEach(col => {
                          const cellValue = item[col] !== undefined ? item[col] : '';
                          html += `<td data-column-key="${col}">${cellValue}</td>`;
                      });
                      html += '</tr>';
                  });
      
                  html += '</tbody></table>';
                  html += paginationControlsHtml; // הוספת הפקדים מתחת לטבלה
                  resultsDiv.innerHTML = html;
      
                  // החלת נראות העמודות לאחר יצירת הטבלה
                  applyColumnVisibility();
              }
      
              /**
               * מוסיף מאזיני אירועים לכותרות הטבלה לצורך מיון.
               */
              function attachSortListeners(columns) {
                  const table = document.getElementById('call-log-table');
                  if (!table) return;
      
                  const headers = table.querySelectorAll('th');
      
                  headers.forEach((header, index) => {
                      // מחיקת מאזינים ישנים
                      if (header.sortHandler) {
                          header.removeEventListener('click', header.sortHandler);
                      }
      
                      // העמודה הראשונה היא האינדקס, לה נותנים מפתח מיוחד
                      const columnKey = index === 0 ? '__index_col__' : columns[index - 1];
      
                      // יצירת המאזין החדש
                      const newHandler = () => {
                          sortTable(index, columns, columnKey);
                      };
      
                      // שמירת המאזין על הכותרת כדי למחוק אותו בפעם הבאה
                      header.sortHandler = newHandler;
                      header.addEventListener('click', newHandler);
                  });
              }
      
              /**
               * מבצע מיון של הנתונים הגלובליים המסוננים/ממוינים ומציג את התוצאות מחדש.
               */
              function sortTable(columnIndexInTable, columns, columnKey) {
                  const table = document.getElementById('call-log-table');
                  const header = table.querySelectorAll('th')[columnIndexInTable];
      
                  let direction = sortState.direction;
                  
                  // אם זו אותה עמודה, החלף כיוון
                  if (sortState.columnIndex === columnIndexInTable) {
                      direction = direction === 'asc' ? 'desc' : 'asc';
                  } else {
                      direction = 'asc';
                  }
      
                  sortState.columnIndex = columnIndexInTable;
                  sortState.direction = direction;
      
                  // הסרת קלאסים מכל הכותרות
                  table.querySelectorAll('th').forEach(th => {
                      th.classList.remove('asc', 'desc');
                  });
                  header.classList.add(direction);
      
                  // קביעת סוג המיון
                  const columnName = columnKey;
                  const isNumeric = columnName === '__index_col__' || columnName.includes('שניות') || columnName.includes('מספר') || columnName.includes('Time') || columnName.includes('טלפון') || columnName.includes('כמות');
      
                  // מיון הנתונים המסוננים/ממוינים בזיכרון
                  filteredAndSortedData.sort((item1, item2) => {
                      let v1 = item1[columnKey] !== undefined ? String(item1[columnKey]) : '';
                      let v2 = item2[columnKey] !== undefined ? String(item2[columnKey]) : '';
                      let comparison = 0;
      
                      if (columnKey === '__index_col__') {
                          // מיון לפי אינדקס גלובלי (כאשר אין מידע אחר)
                          // משתמשים במיקום המקורי שלהם ב-tableData כדי לשמור על יציבות
                          const index1 = tableData.indexOf(item1);
                          const index2 = tableData.indexOf(item2);
                          comparison = index1 - index2;
                      } else if (isNumeric) {
                          // למיון מספרי
                          const cleanV1 = v1.replace(/[^\d.]/g, '');
                          const cleanV2 = v2.replace(/[^\d.]/g, '');
                          v1 = parseFloat(cleanV1) || 0;
                          v2 = parseFloat(cleanV2) || 0;
                          comparison = v1 - v2;
                      } else {
                          // למיון טקסט
                          comparison = v1.localeCompare(v2);
                      }
      
                      return comparison * (direction === 'asc' ? 1 : -1);
                  });
      
                  currentPage = 1; // איפוס לעמוד הראשון לאחר מיון
                  renderPaginatedTable(filteredAndSortedData, currentColumns);
              }
      
              // ------------------- פונקציות סינון מורכבות 🆕 -------------------
              /**
               * מפעיל את פקדי הסינון המתקדמים ויוצר את כלל הסינון הראשון.
               */
              function setupAdvancedFilterControls(columns) {
                  const filterContainer = document.getElementById('advanced-filter-controls');
                  filterContainer.innerHTML = '';
                  filterRules = [];
      
                  // וודא ש'נקה הכל' וכפתור ההפעלה זמינים
                  document.querySelector('.clear-filter-btn').disabled = false;
                  document.getElementById('apply-filter-btn').disabled = false;
      
                  // יוצר את כלל הסינון הראשון
                  addFilterRule(columns);
              }
      
              /**
               * מוסיף כלל סינון חדש לטופס.
               */
              function addFilterRule() {
                  const columns = currentColumns;
                  const ruleId = Date.now();
                  const ruleIndex = filterRules.length;
      
                  const newRule = {
                      id: ruleId,
                      logic: ruleIndex > 0 ? 'AND' : null, // הכלל הראשון לא צריך לוגיקה מקדימה
                      column: '__all_cols__',
                      matchType: 'contains',
                      value: ''
                  };
                  filterRules.push(newRule);
                  
                  const container = document.getElementById('advanced-filter-controls');
                  const columnOptions = columns.map(col => `<option value="${col}">${col}</option>`).join('');
      
                  const ruleHtml = `
                      <div class="filter-rule-row" data-rule-id="${ruleId}">
                          ${ruleIndex > 0 ? `<label for="logic-select-${ruleId}">שילוב:</label> 
                          <select id="logic-select-${ruleId}" class="col-select" onchange="updateFilterRule(${ruleId}, 'logic', this.value)">
                              <option value="AND">וגם (AND)</option>
                              <option value="OR">או (OR)</option>
                          </select>` : ''}
      
                          <label for="column-select-${ruleId}">סנן לפי עמודה:</label>
                          <select id="column-select-${ruleId}" class="col-select" onchange="updateFilterRule(${ruleId}, 'column', this.value)">
                              <option value="__all_cols__">כל העמודות</option>
                              ${columnOptions}
                          </select>
      
                          <label for="match-select-${ruleId}">סוג סינון:</label>
                          <select id="match-select-${ruleId}" class="match-select" onchange="updateFilterRule(${ruleId}, 'matchType', this.value)">
                              <option value="contains">מכיל (לבן - 'מופיע')</option>
                              <option value="not_contains">לא מכיל (שחור - 'לא מופיע')</option>
                              <option value="exact">שווה בדיוק</option>
                              <option value="not_exact">לא שווה בדיוק</option>
                              <option value="starts_with">מתחיל ב</option>
                              <option value="ends_with">נגמר ב</option>
                              <option value="is_empty">ריק</option>
                              <option value="is_not_empty">לא ריק</option>
                          </select>
      
                          <label for="value-input-${ruleId}">ערך:</label>
                          <input type="text" id="value-input-${ruleId}" class="value-input" oninput="updateFilterRule(${ruleId}, 'value', this.value)" placeholder="ערך לסינון..." onkeypress="if(event.key === 'Enter') { filterTable(); }">
                          <button class="remove-btn" onclick="removeFilterRule(${ruleId})">X הסר</button>
                      </div>
                  `;
                  container.insertAdjacentHTML('beforeend', ruleHtml);
                  
                  // וודא שברירת המחדל של העמודה מוגדרת
                  document.getElementById(`column-select-${ruleId}`).value = '__all_cols__';
              }
      
              /**
               * מעדכן כלל סינון במערך הגלובלי.
               */
              function updateFilterRule(ruleId, key, value) {
                  const rule = filterRules.find(r => r.id === ruleId);
                  if (rule) {
                      // שמירת הערך כפי שהוזן, ללא הסרת רווחים
                      rule[key] = value;
                  }
              }
      
              /**
               * מסיר כלל סינון מה-HTML ומהמערך הגלובלי.
               */
              function removeFilterRule(ruleId) {
                  filterRules = filterRules.filter(r => r.id !== ruleId);
                  const elementToRemove = document.querySelector(`.filter-rule-row[data-rule-id="${ruleId}"]`);
                  if (elementToRemove) {
                      elementToRemove.remove();
                      // אם הסרנו את הכלל הראשון (אינדקס 0), ודא שהכלל הבא אחריו לא מציג שילוב
                      const firstRemainingRule = document.querySelector('.filter-rule-row:first-of-type');
                      if (firstRemainingRule) {
                          const logicSelect = firstRemainingRule.querySelector('[id^="logic-select-"]');
                          const logicLabel = firstRemainingRule.querySelector('label[for^="logic-select-"]');
                          if (logicSelect) {
                               logicSelect.remove();
                          }
                          if (logicLabel) {
                              logicLabel.remove();
                          }
                      }
                  }
                  // הפעל סינון אוטומטית אם היו כללים פעילים
                  if (tableData.length > 0) {
                       filterTable();
                  }
              }
      
              /**
               * מנקה את כל כללי הסינון ומאפס את הטבלה לנתוני המקור.
               */
              function clearAllFilters() {
                  const filterContainer = document.getElementById('advanced-filter-controls');
                  const statusDiv = document.getElementById('status-message');
                  
                  filterContainer.innerHTML = '';
                  filterRules = [];
                  document.getElementById('filter-logic-select').value = 'AND';
      
                  // איפוס נתונים
                  filteredAndSortedData = [...tableData];
                  sortState = { columnIndex: -1, direction: 'asc' }; // איפוס מיון
                  currentPage = 1;
      
                  // רינדור מחדש
                  renderPaginatedTable(filteredAndSortedData, currentColumns);
                  
                  // איפוס פקדים
                  document.querySelector('.clear-filter-btn').disabled = true;
                  document.getElementById('apply-filter-btn').disabled = true;
      
                  // יוצר כלל סינון ראשון מחדש כדי לאפשר סינון קל
                  setupAdvancedFilterControls(currentColumns);
                  document.querySelector('.clear-filter-btn').disabled = true; // שוב, כדי שישאר כבוי
                  
                  statusDiv.innerHTML = `<span style="color:#28a745; font-weight: bold;">✅ כללי הסינון נוקו. מוצגות כל ${tableData.length} השורות.</span>`;
                  
                  // מחיקת הודעת הסטטוס לאחר מספר שניות
                  setTimeout(() => {
                      if (statusDiv.innerHTML.includes('כללי הסינון נוקו')) {
                          statusDiv.innerHTML = `<span style="color:#2E7D32;">✅ הנתונים נטענו בהצלחה. סה"כ שורות בלוג: ${tableData.length}. פרטי הלוג: ${selectedFileName}.</span>`;
                      }
                  }, 3000);
              }
      
              /**
               * מבצע את הסינון על הנתונים הגלובליים ומציג את התוצאות מחדש.
               */
              function filterTable() {
                  const statusDiv = document.getElementById('status-message');
                  const generalLogic = document.getElementById('filter-logic-select').value;
                  
                  // כללים פעילים (שיש להם ערך)
                  const activeRules = filterRules.filter(r => r.value.trim() !== '' || r.matchType === 'is_empty' || r.matchType === 'is_not_empty');
      
                  if (activeRules.length === 0) {
                      // אין כללי סינון פעילים
                      filteredAndSortedData = [...tableData];
                  } else {
                      filteredAndSortedData = tableData.filter(item => {
                          // 1. יצירת מערך של תוצאות (true/false) לכל כלל סינון
                          const ruleResults = activeRules.map(rule => {
                              return applyRuleToRow(item, rule);
                          });
      
                          // 2. שילוב התוצאות לפי לוגיקת AND/OR הכללית
                          if (generalLogic === 'AND') {
                              // אם AND, כל התוצאות חייבות להיות TRUE
                              return ruleResults.every(result => result === true);
                          } else if (generalLogic === 'OR') {
                              // אם OR, מספיק שאחת התוצאות תהיה TRUE
                              return ruleResults.some(result => result === true);
                          }
                          return false; // ברירת מחדל
                      });
                  }
      
                  // איפוס המיון ועדכון הטבלה
                  sortState = { columnIndex: -1, direction: 'asc' }; // איפוס מיון
                  currentPage = 1;
                  renderPaginatedTable(filteredAndSortedData, currentColumns);
      
                  // עדכון הודעת הסטטוס על הסינון
                  const totalCount = tableData.length;
                  const recordCount = filteredAndSortedData.length;
      
                  if (activeRules.length > 0 && recordCount === 0) {
                      statusDiv.innerHTML = `<span style="color:#c62828;">❌ לא נמצאו שורות תואמות. ייתכן שיש סתירה בין חוקי הסינון. מוצגות 0 מתוך ${totalCount} שורות.</span>`;
                  } else if (activeRules.length > 0) {
                      statusDiv.innerHTML = `<span style="color:#1a5c92;">🔎 סינון הופעל בהצלחה. מוצגות ${recordCount} שורות מתוך ${totalCount} סה"כ.</span>`;
                  } else {
                      // אם אין כללים פעילים (למרות שהיה קליק על הכפתור)
                      statusDiv.innerHTML = `<span style="color:#2E7D32;">✅ הנתונים נטענו בהצלחה. סה"כ שורות בלוג: ${totalCount}. פרטי הלוג: ${selectedFileName}.</span>`;
                  }
              }
      
              /**
               * מחיל כלל סינון אחד על שורת נתונים בודדת.
               */
              function applyRuleToRow(item, rule) {
                  const { column, matchType, value } = rule;
                  // הפיכת ערך הסינון לרישיות קטנות וקיצוץ רווחים
                  const filterValue = value.toLowerCase().trim();
      
                  if (matchType === 'is_empty') {
                      if (column === '__all_cols__') {
                          // מחזיר אמת אם לפחות עמודה אחת ריקה
                          return currentColumns.some(col => !item[col] || String(item[col]).trim() === '');
                      }
                      // מחזיר אמת אם העמודה הספציפית ריקה
                      return !item[column] || String(item[column]).trim() === '';
                  }
                  
                  if (matchType === 'is_not_empty') {
                      if (column === '__all_cols__') {
                          // מחזיר אמת אם לפחות עמודה אחת לא ריקה
                          return currentColumns.some(col => item[col] && String(item[col]).trim() !== '');
                      }
                      // מחזיר אמת אם העמודה הספציפית אינה ריקה
                      return item[column] && String(item[column]).trim() !== '';
                  }
      
                  // אם נבחרה אפשרות "כל העמודות"
                  if (column === '__all_cols__') {
                      // במקרה זה, מחזירים אמת אם הכלל מתקיים בלפחות עמודה אחת
                      return currentColumns.some(col => {
                          const cellValue = String(item[col] || '').toLowerCase();
                          return performComparison(cellValue, filterValue, matchType);
                      });
                  }
      
                  // עבור עמודה ספציפית
                  const cellValue = String(item[column] || '').toLowerCase();
                  return performComparison(cellValue, filterValue, matchType);
              }
              
              /**
               * פונקציית עזר לביצוע ההשוואה בפועל (עבור סינון).
               */
              function performComparison(cellValue, filterValue, matchType) {
                   // טיפול מיוחד במקרה של שני ערכים ריקים
                  if (cellValue === '' && filterValue === '') {
                      // אם שניהם ריקים, רק סוגי סינון "שחורים" (לא מכיל / לא שווה) יכשלו.
                      if (matchType.startsWith('not_')) {
                          return false;
                      }
                      return true; // אם מחפשים 'מכיל' ערך ריק או 'שווה' לערך ריק, זה יתאים
                  }
      
                  // טיפול במקרה של תא ריק מול ערך סינון לא ריק
                  if (cellValue === '' && filterValue !== '') {
                      // זה יתאים רק לסינון שחור ("לא מכיל" / "לא שווה")
                      if (matchType === 'not_contains' || matchType === 'not_exact') {
                          return true;
                      }
                      // סוגי סינון "לבנים" (כמו 'מכיל', 'שווה', 'מתחיל ב', 'נגמר ב') נכשלים
                      return false;
                  }
      
                  switch (matchType) {
                      case 'contains':
                          return cellValue.includes(filterValue);
                      case 'not_contains':
                          return !cellValue.includes(filterValue);
                      case 'exact':
                          return cellValue === filterValue;
                      case 'not_exact':
                          return cellValue !== filterValue;
                      case 'starts_with':
                          return cellValue.startsWith(filterValue);
                      case 'ends_with':
                          return cellValue.endsWith(filterValue);
                      default:
                          return true;
                  }
              }
      
              /**
               * פונקציה לניקוי הסינון והצגת כל השורות.
               */
              function clearFilter() {
                  clearAllFilters();
              }
              
              // ------------------- פונקציות נראות עמודות 🆕 -------------------
      
              /**
               * שומר או מאחזר את מצב הנראות של עמודה ספציפית במצב הגלובלי הזמני.
               */
              function toggleColumnVisibility(columnKey) {
                  const checkbox = document.querySelector(`input[data-column-key="${CSS.escape(columnKey)}"]`);
                  if (!checkbox) return;
      
                  const isVisible = checkbox.checked;
                  currentColumnVisibilityState[columnKey] = isVisible;
                  applyColumnVisibility(); // החלת השינוי על הטבלה
              }
      
              /**
               * מגדיר את פקדי הצ'קבוקסים לנראות העמודות על פי העמודות בלוג הנבחר.
               * @param {Array<string>} columns - רשימת העמודות של הלוג הנוכחי.
               * @param {string} logFileName - שם קובץ הלוג
               */
              function setupColumnVisibilityControls(columns, logFileName) {
                  const container = document.getElementById('col-checkbox-container');
                  container.innerHTML = '';
      
                  // 1. קביעת עמודות ברירת מחדל ללוג API
                  const API_DEFAULT_COLUMNS = [
                      'שלוחה', 'טלפון', 'תאריך לועזי', 'שעה', 'תאריך עברי', 'ApiSend', 'ApiAnswer', 
                      '25Phone', '25Date', '25Time', '25HebrewDate', '25ApiSend', '25ApiAnswer'
                  ];
                  
                  let defaultVisibleHeaders = [...columns];
                  const logNameLower = (logFileName || '').toLowerCase();
                  const useApiDefault = logNameLower.includes('logapi.ymgr') || logNameLower.includes('logapi');
                  
                  if (useApiDefault) {
                      // לוגיקת ברירת מחדל מיוחדת ללוגי API
                      defaultVisibleHeaders = API_DEFAULT_COLUMNS;
                  }
      
                  // 2. 🆕 איפוס ואתחול המצב הגלובלי הזמני לברירת המחדל
                  currentColumnVisibilityState = {};
                  columns.forEach(col => {
                      if (col === '__index_col__') return;
                      const defaultIsVisible = defaultVisibleHeaders.includes(col);
                      // שמירת ברירת המחדל במצב הגלובלי הזמני
                      currentColumnVisibilityState[col] = defaultIsVisible;
      
                      const label = document.createElement('label');
                      // שימוש במצב המאותחל עבור ה-checked state
                      label.innerHTML = `
                          <input type="checkbox" data-column-key="${col}" onchange="toggleColumnVisibility('${col}')" ${defaultIsVisible ? 'checked' : ''}>
                          ${col}
                      `;
                      container.appendChild(label);
                  });
      
                  // 3. החלת הנראות מיד לאחר יצירת הטבלה
                  applyColumnVisibility();
              }
      
              /**
               * פונקציה לאיפוס נראות העמודות לברירת המחדל של הלוג הנוכחי.
               */
              function resetColumnVisibilityToDefault() {
                  if (currentColumns.length === 0) return;
      
                  // 1. קריאה חוזרת ל-setupColumnVisibilityControls כדי לחשב ולאתחל את המצב הגלובלי מחדש
                  // (פעולה זו מאתחלת את currentColumnVisibilityState על בסיס ברירות המחדל).
                  setupColumnVisibilityControls(currentColumns, selectedFileName);
      
                  // 2. רינדור מחדש של הטבלה כדי להחיל את הנראות החדשה
                  renderPaginatedTable(filteredAndSortedData, currentColumns);
      
                  // 3. הצגת הודעת סטטוס
                  const statusDiv = document.getElementById('status-message');
                  const originalContent = statusDiv.innerHTML;
                  statusDiv.innerHTML = '<span style="color:#28a745; font-weight: bold;">✅ נראות העמודות אופסה לברירת המחדל של הלוג הנוכחי.</span>';
                  
                  // מחיקת הודעת הסטטוס לאחר מספר שניות
                  setTimeout(() => {
                      if (statusDiv.innerHTML.includes('נראות העמודות אופסה')) {
                          statusDiv.innerHTML = originalContent;
                      }
                  }, 3000);
              }
      
      
              /**
               * מחילה את מצב הנראות הנוכחי (currentColumnVisibilityState) על הטבלה ב-DOM.
               */
              function applyColumnVisibility() {
                  const table = document.getElementById('call-log-table');
                  if (!table) return;
      
                  // מעבר על כל מצבי הנראות
                  Object.keys(currentColumnVisibilityState).forEach(col => {
                      const isVisible = currentColumnVisibilityState[col];
                      const escapedCol = CSS.escape(col);
                      // חפש הן את כותרות העמודות (TH) והן את תאי הנתונים (TD)
                      const colElements = document.querySelectorAll(`[data-column-key="${escapedCol}"]`);
                      
                      colElements.forEach(el => {
                          if (el.tagName === 'TH' || el.tagName === 'TD') {
                              el.classList.toggle('col-hidden', !isVisible);
                          }
                      });
      
                      const checkbox = document.querySelector(`input[data-column-key="${escapedCol}"]`);
                      if (checkbox) {
                          // וודא שמצב הצ'קבוקס משקף את המצב בפועל
                          checkbox.checked = isVisible;
                      }
                  });
              }
      
      
              /**
               * פונקציה להצגת פרטי שורה מלאים בפורמט קריא.
               */
              function showRowDetails(rowElement, rowIndex) {
                  const detailsPanel = document.getElementById('details-panel');
                  const detailsContent = document.getElementById('details-content');
      
                  document.querySelectorAll('#call-log-table tr').forEach(tr => {
                      tr.style.outline = 'none';
                  });
                  rowElement.style.outline = '3px solid #1a5c92';
      
                  const fullData = filteredAndSortedData[rowIndex];
                  let html = '';
      
                  html += `<dt># מספר שורה</dt><dd class="value-text">${rowIndex + 1}</dd>`;
      
                  currentColumns.forEach(key => {
                      const rawValue = fullData[key] !== undefined ? fullData[key] : '';
      
                      if (rawValue || rawValue === 0) {
                          let valueToDisplay = rawValue;
                          let copyButtonHtml = '';
      
                          // עיצוב JSON
                          if (key === 'ApiSend' || key === 'ApiAnswer') {
                              try {
                                  const jsonObject = JSON.parse(rawValue);
                                  valueToDisplay = JSON.stringify(jsonObject, null, 2);
                              } catch (e) {
                                  // לא JSON
                              }
                          }
      
                          // הוספת כפתור העתקה
                          if (key === 'ApiSend' || key === 'ApiAnswer' || key === 'ApiTime') {
                              copyButtonHtml = `<button onclick="copyToClipboard(this.previousElementSibling.textContent, this)">העתק</button>`;
                          }
      
                          valueToDisplay = String(valueToDisplay);
                          
                          html += `<dt>${key}</dt>`;
                          html += `<dd>
                              <span class="value-text ${valueToDisplay.includes('\n') ? 'json-formatted' : ''}">${valueToDisplay}</span>
                              ${copyButtonHtml}
                          </dd>`;
                      }
                  });
      
                  detailsContent.innerHTML = html;
                  detailsPanel.style.display = 'block';
                  // detailsPanel.scrollIntoView({ behavior: 'smooth', block: 'start' });
              }
      
              /**
               * 🆕 פונקציה לסגירת פאנל פרטי השורה הנבחרת.
               */
              function closeDetailsPanel() {
                  const detailsPanel = document.getElementById('details-panel');
                  detailsPanel.style.display = 'none';
                  
                  // הסרת ההדגשה מהשורה הנבחרת
                  document.querySelectorAll('#call-log-table tr').forEach(tr => {
                      tr.style.outline = 'none';
                  });
              }
      
              /**
               * 🆕 פונקציה להעתקת טקסט ללוח המערכת עם פידבק UI.
               */
              function copyToClipboard(text, buttonElement) {
                  navigator.clipboard.writeText(text).then(() => {
                      const originalText = buttonElement.textContent;
                      const originalBg = buttonElement.style.backgroundColor;
                      
                      buttonElement.textContent = 'הועתק! ✅';
                      buttonElement.classList.add('copied-success');
      
                      setTimeout(() => {
                          buttonElement.textContent = originalText;
                          buttonElement.classList.remove('copied-success');
                          buttonElement.style.backgroundColor = originalBg; // החזרת הצבע המקורי
                      }, 1500);
      
                  }).catch(err => {
                      console.error('Failed to copy text: ', err);
                      alert('שגיאה בהעתקה: ' + err);
                  });
              }
      
          </script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js"></script>
      </body>
      
      </html>
      

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

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

      תגובה 1 תגובה אחרונה תגובה ציטוט 10
      • י יעקב יצחק התייחס לנושא זה

      שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.

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

      בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗

      הרשמה התחברות
      • פוסט ראשון
        פוסט אחרון