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

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

    מתוזמן נעוץ נעול הועבר הסברים מסודרים ממשתמשים
    1 פוסטים 1 כותבים 229 צפיות 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 תגובה אחרונה תגובה ציטוט 6
      • פוסט ראשון
        פוסט אחרון