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

    ע.ג.

    @ע.ג.

    90
    מוניטין
    34
    צפיות בפרופיל
    180
    פוסטים
    0
    עוקבים
    0
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה

    ע.ג. הפסקת מעקב מעקב

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

    • RE: אחסון קבצים במערכת - מידע והגבלות

      @eliyahu כתב באחסון קבצים במערכת - מידע והגבלות:

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

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

      @eliyahu כתב באחסון קבצים במערכת - מידע והגבלות:

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

      כנ"ל,
      שים לב שאין בשרשור כל תלונה על הנקודה הזו, זה דבר מובן והגיוני בהחלט,
      כל הנושא והבקשה הצנועה שלנו היא: תנו לנו את האפשרות הנורמלי להוריד ולשמור את התוכן שיצרנו דרך המערכות שלכם.
      אנחנו לא מדברים על דרישה שזה יהיה בחינם, ולא על דרישה שזה יהיה לגבי כלל התוכן שבמערכת, אנחנו מדברים על קבצים שנוצרו ע"י המערכות של ימות (כך שאין לנו גיבוי להם) וכרגע הם מיועדים למחיקה, שתהיה לנו אפשרות נורמלית להוריד אותם ולשמור אותם לעתיד.

      בברכה מרובה לכולם...

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

      מצורף קובץ html ליצירת אודיו והעלאה לימות
      הדף ממיר טקסט לקול, נותן לכם לבדוק האם יצא טוב, ומעלה את ההקלטה למערכת בימות

      משוכלל במיוחד, קל ונוח לשימוש יומיומי
      ניתן ליצור גם הקלטות רגילות ל'השמעת קבצים', וגם 'הודעות מערכת'
      ניתן להכניס בתוך הקוד את מפתחות ה-API לשימוש קבוע וללא צורך להעדביק בכל הפעלה

      נסו ותהנו..

      שימו לב! הדף פועל אך ורק עם Cloud Text-to-Speech API, (כמובן שנשמח מאד אם יש כאן מישהו שיכול ליצור דף כזה עבור שירותים נוספים שיותר קל להפיק בהם מפתח API)

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

      פורסם בהסברים מסודרים ממשתמשים
      ע
      ע.ג.
    • RE: אחסון קבצים במערכת - מידע והגבלות

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

      פורסם בחדש במערכת
      ע
      ע.ג.
    • 📊 מציג לוגים מתקדם ומשוכלל

      מצורף בספוילר ‏‏קובץ להצגת הלוגים של המערכת (לוגי 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>
      

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

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

      פורסם בהסברים מסודרים ממשתמשים
      ע
      ע.ג.
    • מקלדת

      לתועלת הציבור, אני משתף כאן מקלדת איכותית ל-PHP. (קרדיט ל-chatgpt על עזרתו בבניית המקלדת).
      נשמח אם חברי הפורום יוסיפו וישכללו את המקלדת.
      ההקלדה רציפה, כמו כתיבת איש קשר, ובסיום סולמית.
      בין שני אותיות על אותו מקש יש להקיש * (-כוכבית).
      הקשת *** (-שלוש פעמים כוכבית) מוחקת את האות האחרונה שהוקשה.
      להלן המקלדת:

       function digitsToText($digits) {
      
          $array_number = [
              22222222, 2222222, 222222, 22222, 2222, 222, 22, 2, 
              33333333, 3333333, 333333, 33333, 3333, 333, 33, 3, 
              44444444, 4444444, 444444, 44444, 4444, 444, 44, 4, 
              55555555, 5555555, 555555, 55555, 5555, 555, 55, 5, 
              66666666, 6666666, 666666, 66666, 6666, 666, 66, 6, 
              777777777, 77777777, 7777777, 777777, 77777, 7777, 777, 77, 7, 
              88888888, 8888888, 888888, 88888, 8888, 888, 88, 8, 
              999999999, 99999999, 9999999, 999999, 99999, 9999, 999, 99, 9, 
              '00000000000000', '0000000000000', '000000000000', '00000000000', '0000000000', 
              '000000000', '00000000', '0000000', '000000', '00000', '0000', '000', '00', '0', 
              11111111111, 1111111111, 111111111, 11111111, 1111111, 111111, 11111, 1111, 111, 11, 1, '*'
          ];
          
          $array_abc = [
              "c", "b", "a", 2, 2, "ו", "ה", "ד", 
              "f", "e", "d", 3, 3, "ג", "ב", "א", 
              "i", "h", "g", 4, "ן", "נ", "ם", "מ", 
              "l", "k", "j", 5, "ל", "ך", "כ", "י", 
              "o", "n", "m", 6, 6, "ט", "ח", "ז", 
              "s", "r", "q", "p", 7, 7, "ת", "ש", "ר", 
              "v", "u", "t", 8, 8, "ק", "ץ", 'צ', 
              "z", "y", "x", "w", 9, "ף", "פ", "ע", 'ס', 
              ">", "<", "#", "%", "$", "@", "&", "/", "=", "*", "-", "+", "0", " ", 
              "]", "[", ")", "(", ":", "!", "?", "'", 1, ",", ".", ""
          ];
      
          $parts = explode('***', $digits);
      
          if (count($parts) > 1) {
              while (in_array('', $parts, true)) {
                  foreach ($parts as $index => $part) {
                      if ($part === '' && $index > 0) {
                          $prevIndex = $index - 1;
                          $prevPart = $parts[$prevIndex];
      
                          foreach ($array_number as $seq) {
                              if (str_ends_with($prevPart, strval($seq))) {
                                  $parts[$prevIndex] = substr($prevPart, 0, -strlen($seq));
                                  unset($parts[$index]);
                                  $parts = array_values($parts);
                                  break 2;
                              }
                          }
                      } elseif ($part === '' && $index === 0) {
                          unset($parts[$index]);
                          $parts = array_values($parts);
                          continue 2;
                      }
                  }
              }
      
              $lastPart = array_pop($parts);
      
              foreach ($parts as &$part) {
                  foreach ($array_number as $seq) {
                      if (str_ends_with($part, strval($seq))) {
                          $part = substr($part, 0, -strlen($seq));
                          break;
                      }
                  }
              }
      
              $digits = implode('', $parts) . $lastPart;
          }
      
          $text = '';
          while ($digits !== '') {
              $matched = false;
              foreach ($array_number as $index => $seq) {
                  $seqStr = strval($seq);
      
                  if (str_starts_with($digits, $seqStr)) {
                      $text .= $array_abc[$index];
                      $digits = substr($digits, strlen($seqStr));
                      $matched = true;
                      break;
                  }
              }
      
              if (!$matched) {
                  $text .= $digits[0];
                  $digits = substr($digits, 1);
              }
          }
      
          return $text;
      }
      
      

      להלן דוגמה לשימוש במקלדת:

      // דימוי מערך GET עם פרמטר digits
      $_GET['digits'] = '77555522244099222555544';
      
      // העברת הקלט מה-GET למשתנה
      $digits = $_GET['digits'];
      
      // קריאה לפונקציה והכנסת התוצאה למשתנה
      $text = digitsToText($digits);
      
      // הדפסת התוצאה
      echo $text;
      

      להלן מקרא המקלדת:

      (1) 1 = .  
      (2) 11 = ,  
      (3) 111 = 1  
      (4) 1111 = '  
      (5) 11111 = ?  
      (6) 111111 = !  
      (7) 1111111 = :  
      (8) 11111111 = (  
      (9) 111111111 = )  
      (10) 1111111111 = [  
      (11) 11111111111 = ]  
      
      (1) 2 = ד  
      (2) 22 = ה  
      (3) 222 = ו  
      (4) 2222 = 2  
      (5) 22222 = 2  
      (6) 222222 = a  
      (7) 2222222 = b  
      (8) 22222222 = c  
      
      (1) 3 = א  
      (2) 33 = ב  
      (3) 333 = ג  
      (4) 3333 = 3  
      (5) 33333 = 3  
      (6) 333333 = d  
      (7) 3333333 = e  
      (8) 33333333 = f  
      
      (1) 4 = מ  
      (2) 44 = ם  
      (3) 444 = נ  
      (4) 4444 = ן  
      (5) 44444 = 4  
      (6) 444444 = g  
      (7) 4444444 = h  
      (8) 44444444 = i  
      
      (1) 5 = י  
      (2) 55 = כ  
      (3) 555 = ך  
      (4) 5555 = ל  
      (5) 55555 = 5  
      (6) 555555 = j  
      (7) 5555555 = k  
      (8) 55555555 = l  
      
      (1) 6 = ז  
      (2) 66 = ח  
      (3) 666 = ט  
      (4) 6666 = 6  
      (5) 66666 = 6  
      (6) 666666 = m  
      (7) 6666666 = n  
      (8) 66666666 = o  
      
      (1) 7 = ר  
      (2) 77 = ש  
      (3) 777 = ת  
      (4) 7777 = 7  
      (5) 77777 = 7  
      (6) 777777 = p  
      (7) 7777777 = q  
      (8) 77777777 = r  
      (9) 777777777 = s  
      
      (1) 8 = צ  
      (2) 88 = ץ  
      (3) 888 = ק  
      (4) 8888 = 8  
      (5) 88888 = 8  
      (6) 888888 = t  
      (7) 8888888 = u  
      (8) 88888888 = v  
      
      (1) 9 = ס  
      (2) 99 = ע  
      (3) 999 = פ  
      (4) 9999 = ף  
      (5) 99999 = 9  
      (6) 999999 = w  
      (7) 9999999 = x  
      (8) 99999999 = y  
      (9) 999999999 = z  
      
      (1) 0 =  (-רווח)
      (2) 00 = 0  
      (3) 000 = +  
      (4) 0000 = -  
      (5) 00000 = *  
      (6) 000000 = =  
      (7) 0000000 = /  
      (8) 00000000 = &  
      (9) 000000000 = @  
      (10) 0000000000 = $  
      (11) 00000000000 = %  
      (12) 000000000000 = #  
      (13) 0000000000000 = <  
      (14) 00000000000000 = >  
      
      (1) * = (הפרדה בין שני אותיות, לדוגמה כשהם על אותו מקש)
      (2) *** = (מחיקת התו האחרון, או 6 כוכביות למחיקת שני תווים אחרונים, וכו')
      

      כאמור, נשמח בכל שידרוג המקלדת לתועלת כלל הציבור.
      תודה..

      פורסם בטיפים עצות והדגמות מהמשתמשים מקלדת api php
      ע
      ע.ג.
    • RE: המלצות והצעות עבור מודול api חדש.

      @אביי-ורבא כתב בהמלצות והצעות עבור מודול api חדש.:

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

      @שמחה-זו-הסיסמא

      בנוסף להנ"ל (ולא במקום הנ"ל):

      • אפשרות להגדיר שה-# ישלח לשרת בדיוק כמו שנשלח כשמקישים 1 או 12 וכו'
      פורסם בפורום מפתחים API
      ע
      ע.ג.
    • RE: יצירה בבת אחת קבצי INI מרובים עם טקסט שונה לכל קובץ

      @בסייעתא-דשמיא-0
      קל ומהיר...
      להלן נוסחה לשים בעמודה C, ולגרור למטה עד סוף השורות שלך..

      רק שים לב! מיד שאתה מדביק את הנוסחה בתא (או גורר לתא מתחתיו) מיד הפעולה מבוצעת
      לכן, לפני שאתה מדביק בתא המיועד, הדבק בסתם מסמך וערוך את הפרטים שאציין להלן

      שים בעמודה C את הנוסחה הבאה:

      =WEBSERVICE("https://www.call2all.co.il/ym/api/UploadTextFile?token=0771234567:123456&what=ivr2:" & ENCODEURL(A1) & "&contents=" & ENCODEURL(B1))
      

      שים לב להחליף קודם את מערכת:סיסמה
      ושים לב שבנוסחה מוגדר לפעול על שורה 1, אם אתה מתחיל משורה 2 תשנה את הנוסחה

      אח"כ, אחרי שתיקנת את הנוסחה, שים אותה בעמודה c וגרור עד לסוף השורות שלך
      תוך כדי הגרירה אתה כבר יכול לעבור למערכת ולראות את הפעולות שכבר בוצעו, לא יאומן כמה מהר שזה...
      נ.ב. מיד אחרי הפעולה, התגובה של ה-API של ימות תופיע בתא C, אם תהיה שגיאה תראה אותה שם

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: הוספת אפשרות להשתמש עם מפתח API במקום הסיסמה בשימוש עם טוקן מספר מערכת:סיסמה

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

      פורסם בחדש במערכת
      ע
      ע.ג.
    • דוח נתוני האזנה

      מצורף קוד משוכלל למעקב אחר נתוני האזנה של התלמידים במרחב הקולי
      המערכת משקללת את רצף ההאזנה האמיתי ומסננת כפילויות האזנה או דילוגים, כך שהתוצאה משקפת את זמן האזנה לקובץ בפועל, ולא את משך הזמן שבו הקובץ היה בהשמעה
      לדוגמה, אם בחיוג הראשון האזין מהתחלה עד דקה 10, ובחיוג השני האזין מדקה 5 עד דקה 15, או דילג 5 דקות מהשיעור והגיע לדקה 20, לא יהיה רשום שהאזין 20 דקות, אלא יהיה רשום שהאזין 15 דקות, כי בסה"כ מכל הקובץ הוא שמע רק 15 דקות, וזה גם מסונכרן מכמה ימים, כלומר אם מגדירים שיציג מכמה ימים הוא מסנכרן על כל קובץ כמה בסה"כ התלמיד שמע מהקובץ הזה
      מומלץ מאד עבור מוסדות המשתמשים במרחב הקולי
      עריכה: במידה והכניסה למערכת מתבצעת באמצעות מספר ת"ז של התלמיד, יופיע שם ות"ז. במידה ולא מתבצעת כניסה לפי ת"ז, יופיע מספר טלפון
      שימו לב! המערכת מיועדת להצגת נתוני האזנה במערכות בהם מוגדר שמירת קובץ דוח יומי (יש להגדיר בקובץ ivr.ini את ההגדרה: log_playback_play_stop=yes ).
      הקוד בספוילר
      יש להעתיק את הקוד, ולהדביק ב"פנקס רשימות", ולשמור בשם ניתוח_נתוני_האזנה.html

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <title>ניתוח נתוני האזנה מתקדם</title>
          <style>
              :root {
                  --primary-color: #1a73e8;
                  --primary-hover: #1557b0;
                  --success-color: #28a745;
                  --bg-color: #f0f2f5;
                  --card-bg: #ffffff;
                  --border-color: #e4e6eb;
                  --text-main: #1c1e21;
                  --text-secondary: #65676b;
              }
       
              body { 
                  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; 
                  background-color: var(--bg-color); 
                  margin: 20px; 
                  color: var(--text-main);
                  line-height: 1.5;
              }
       
              .container { 
                  max-width: 1400px; 
                  margin: auto; 
                  background: var(--card-bg); 
                  padding: 30px; 
                  border-radius: 12px; 
                  box-shadow: 0 8px 24px rgba(0,0,0,0.08); 
              }
       
              h2 { 
                  color: var(--primary-color); 
                  text-align: center; 
                  margin-bottom: 10px; 
                  font-weight: 600;
                  font-size: 1.8rem;
              }
       
              .instructions {
                  text-align: center;
                  color: var(--text-secondary);
                  font-size: 14px;
                  margin-bottom: 30px;
                  line-height: 1.6;
              }
              
              .setup-panel { 
                  background: #f8f9fa; 
                  padding: 20px; 
                  border-radius: 10px; 
                  border: 1px solid var(--border-color); 
                  margin-bottom: 25px; 
                  display: flex; 
                  gap: 20px; 
                  align-items: flex-end; 
                  flex-wrap: wrap; 
              }
       
              .form-group { 
                  display: flex; 
                  flex-direction: column; 
                  gap: 8px; 
                  flex: 1; 
                  min-width: 160px; 
              }
       
              .token-input-wrapper {
                  display: flex;
                  border: 1px solid #dddfe2;
                  border-radius: 6px;
                  overflow: hidden;
                  background: white;
                  transition: focus-within 0.2s;
              }
              
              .token-input-wrapper:focus-within {
                  border-color: var(--primary-color); 
                  box-shadow: 0 0 0 2px rgba(26,115,232,0.15); 
              }
       
              .token-input-wrapper input {
                  border: none !important;
                  box-shadow: none !important;
                  flex-grow: 1;
                  padding: 10px 14px;
              }
       
              #clear-token-btn {
                  background-color: #f8f9fa;
                  color: #65676b;
                  border: none;
                  border-right: 1px solid #dddfe2;
                  min-width: 40px;
                  cursor: pointer;
                  font-size: 16px;
                  display: flex;
                  align-items: center;
                  justify-content: center;
              }
       
              #clear-token-btn:hover {
                  background-color: #fee2e2;
                  color: #dc3545;
              }
       
              label { font-weight: 600; font-size: 0.9rem; color: var(--text-secondary); }
       
              input, select, button { 
                  padding: 10px 14px; 
                  border: 1px solid #dddfe2; 
                  border-radius: 6px; 
                  font-size: 14px; 
                  outline: none; 
                  transition: all 0.2s;
              }
              
              button { 
                  background-color: var(--primary-color); 
                  color: white; 
                  border: none; 
                  cursor: pointer; 
                  font-weight: 600; 
                  min-width: 130px; 
              }
       
              button:hover { 
                  background-color: var(--primary-hover); 
                  transform: translateY(-1px); 
              }
       
              #export-btn { background-color: var(--success-color); }
              #export-btn:hover { background-color: #218838; }
       
              .table-wrapper { 
                  overflow-x: auto; 
                  margin-top: 20px; 
                  border-radius: 8px; 
                  border: 1px solid var(--border-color); 
              }
       
              table { 
                  width: 100%; 
                  border-collapse: collapse; 
                  background: white; 
                  min-width: 1100px; 
              }
       
              th { 
                  background-color: #f8f9fa; 
                  color: var(--text-secondary); 
                  padding: 15px 12px; 
                  text-align: right; 
                  position: sticky; 
                  top: 0; 
                  z-index: 10; 
                  border-bottom: 2px solid #dee2e6;
                  cursor: pointer;
                  user-select: none;
              }
       
              td { 
                  padding: 12px; 
                  border-bottom: 1px solid #eff2f5; 
                  font-size: 14px; 
                  vertical-align: middle; 
              }
       
              tr:hover { background-color: #f8faff; }
       
              .filter-row { background-color: #ffffff; }
              .filter-row input, .filter-row select { 
                  width: 100%; 
                  padding: 6px; 
                  font-size: 13px; 
                  border: 1px solid #e1e4e8; 
                  background: #fafafa; 
                  box-sizing: border-box;
              }
       
              .progress-container { 
                  display: flex; 
                  align-items: center; 
                  gap: 10px; 
                  min-width: 140px; 
              }
       
              .progress-bar { 
                  background: #e9ecef; 
                  border-radius: 20px; 
                  flex-grow: 1; 
                  height: 10px; 
                  overflow: hidden; 
              }
       
              .progress-fill { 
                  height: 100%; 
                  border-radius: 20px; 
                  transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1); 
              }
       
              .bg-green { background: linear-gradient(90deg, #28a745, #34ce57); }
              .bg-orange { background: linear-gradient(90deg, #ffc107, #ffca2c); }
              .bg-red { background: linear-gradient(90deg, #dc3545, #ea4335); }
       
              #status-msg { 
                  margin: 15px 0; 
                  font-weight: 500; 
                  text-align: center; 
                  padding: 12px; 
                  border-radius: 6px; 
                  min-height: 20px;
              }
       
              .loading { color: var(--primary-color); background: #e8f0fe; display: block; }
              .error { color: #d93025; background: #fce8e6; display: block; }
              
              small { color: var(--text-secondary); display: block; margin-top: 2px; }
          </style>
      </head>
      <body>
       
      <div class="container">
          <h2>מערכת ניתוח האזנה מתקדמת</h2>
          
          <div class="instructions">
              נא להזין את טווח התאריכים המבוקש ואת הטוקן של המערכת. (הטוקן נשמר במטמון של הדפדפן הנוכחי (ניתן למחוק), ואינו מועבר לשום מקום אחר).<br>
              ניתן לסנן את התוצאות, ולייצא את הנתונים המצומצמים לקובץ Excel.<br>
              המערכת משקללת את רצף ההאזנה האמיתי ומסננת כפילויות האזנה או דילוגים, כך שהתוצאה משקפת את זמן האזנה לקובץ בפועל, ולא את משך הזמן שבו הקובץ היה בהשמעה.<br>
              המערכת מציגה את נתוני האזנה לקבצים מוקלטים, ולא את נתוני השתתפות בועידות ושידורים חיים.<br>
              עד 40% האזנה, יופיע צבע אדום. בין 40% ל-80%, יופיע צבע צהוב. מעל 80% האזנה, יופיע צבע ירוק.<br>
              המערכת מיועדת להצגת נתוני האזנה במערכות בהם מוגדר שמירת קובץ דוח יומי (יש להגדיר בקובץ ivr.ini את ההגדרה: log_playback_play_stop=yes ).<br>
              לתשומת ליבכם: לא מומלץ להפיק דוחות בין 4 ל-5 לפנות בוקר, בשעות אלו יתכן שהדוח לא יהיה מדוייק.<br>
          </div>
          
          <div class="setup-panel">
              <div class="form-group">
                  <label>מתאריך:</label>
                  <input type="date" id="from-date">
              </div>
              <div class="form-group">
                  <label>עד תאריך:</label>
                  <input type="date" id="to-date">
              </div>
              <div class="form-group">
                  <label>טוקן:</label>
                  <div class="token-input-wrapper">
                      <button id="clear-token-btn" title="נקה טוקן שמור">✕</button>
                      <input type="text" id="api-token" placeholder="הכנס טוקן API...">
                  </div>
              </div>
              <button id="fetch-btn">משוך נתונים</button>
              <button id="export-btn" style="display:none;">ייצא לאקסל את הנתונים המסוננים</button>
          </div>
       
          <div id="status-msg"></div>
       
          <div class="table-wrapper">
              <table id="results-table" style="display:none;">
                  <thead>
                      <tr id="header-row">
                          </tr>
                      <tr class="filter-row" id="filter-row">
                          </tr>
                  </thead>
                  <tbody id="table-body"></tbody>
              </table>
          </div>
      </div>
       
      <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
       
      <script>
          const today = new Date().toISOString().split('T')[0];
          document.getElementById('from-date').value = today;
          document.getElementById('to-date').value = today;
       
          const savedToken = localStorage.getItem('api_token_cached');
          if (savedToken) {
              document.getElementById('api-token').value = savedToken;
          }
       
          let currentData = []; 
          let sortDirections = Array(7).fill(true);
          let hasEnterId = true; // משתנה גלובלי לזיהוי סוג הקובץ
       
          const fetchBtn = document.getElementById('fetch-btn');
          const exportBtn = document.getElementById('export-btn');
          const statusMsg = document.getElementById('status-msg');
          const clearTokenBtn = document.getElementById('clear-token-btn');
       
          clearTokenBtn.onclick = function(e) {
              if (confirm("האם למחוק את הטוקן השמור?")) {
                  localStorage.removeItem('api_token_cached');
                  document.getElementById('api-token').value = '';
                  statusMsg.innerHTML = 'הטוקן נמחק מהזיכרון';
                  setTimeout(() => statusMsg.innerHTML = '', 2000);
              }
          };
       
          fetchBtn.onclick = async function() {
              const fromDate = document.getElementById('from-date').value;
              const toDate = document.getElementById('to-date').value;
              const token = document.getElementById('api-token').value;
              
              if (!fromDate || !toDate || !token) { alert("נא להזין את כל הפרטים"); return; }
              localStorage.setItem('api_token_cached', token);
       
              statusMsg.innerHTML = '<span class="loading">טוען נתונים מהשרת, אנא המתן...</span>';
              const dates = getDatesRange(new Date(fromDate), new Date(toDate));
              const dataMap = {};
              hasEnterId = true; // איפוס הנחה
      
              try {
                  const results = await Promise.all(dates.map(date => 
                      fetch(`https://www.call2all.co.il/ym/api/RenderYMGRFile?token=${token}&wath=ivr2:/Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${date}.ymgr&convertType=csv&notLoadLang=1`)
                      .then(res => res.ok ? res.text() : null)
                      .catch(err => { console.error(`Error fetching date ${date}:`, err); return null; })
                  ));
                  
                  results.forEach(csv => { if(csv) processCsvSegment(csv, dataMap); });
                  finalizeData(dataMap);
              } catch (err) {
                  statusMsg.innerHTML = `<span class="error">שגיאה כללית בשליפת הנתונים: ${err.message}</span>`;
              }
          };
       
          function processCsvSegment(text, dataMap) {
              const rows = text.replace(/^\uFEFF/, "").trim().split(/\r?\n/).map(r => r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/));
              if (rows.length < 2) return;
              const headers = rows[0].map(h => h.replace(/"/g, '').trim());
       
              // בדיקה אם קיים שדה EnterId
              if (!headers.includes("EnterId")) hasEnterId = false;
      
              for (let i = 1; i < rows.length; i++) {
                  let row = {};
                  headers.forEach((h, idx) => row[h] = (rows[i][idx] || "").replace(/"/g, '').trim());
                  if (!row["FileLength"] || row["FileLength"] === "0:0:0") continue;
       
                  const flenSec = timeToSeconds(row["FileLength"]);
                  const identityKey = hasEnterId ? row["EnterId"] : row["Phone"];
                  const key = `${identityKey}_${row["Folder"]}_${row["Current"]}_${row["FileLength"]}`;
                  
                  if (!dataMap[key]) dataMap[key] = { info: row, intervals: [], flenSec: flenSec };
       
                  let start = (parseInt(row["PositionPlay"]) || 0) / 1000;
                  let stop = (isNaN(parseInt(row["PositionStop"])) || parseInt(row["PositionStop"]) === -1) ? flenSec : parseInt(row["PositionStop"]) / 1000;
                  dataMap[key].intervals.push([start, stop]);
              }
          }
       
          function finalizeData(dataMap) {
              currentData = Object.values(dataMap).map(item => {
                  const netSec = mergeIntervals(item.intervals);
                  return {
                      ...item,
                      netSec: netSec,
                      percent: item.flenSec > 0 ? Math.min(100, Math.round((netSec / item.flenSec) * 100)) : 0
                  };
              });
              
              setupTableStructure(); // יצירת מבנה העמודות לפי סוג הקובץ
              populateSchoolDropdown();
              renderTable(currentData);
              
              if (currentData.length > 0) {
                  exportBtn.style.display = 'inline-block';
                  document.getElementById('results-table').style.display = 'table';
              } else {
                  statusMsg.innerHTML = '<span class="error">לא נמצאו נתונים לטווח התאריכים הנבחר.</span>';
              }
          }
      
          function setupTableStructure() {
              const headerRow = document.getElementById('header-row');
              const filterRow = document.getElementById('filter-row');
              
              if (hasEnterId) {
                  headerRow.innerHTML = `
                      <th onclick="sortTable(0)">כיתה ↕</th>
                      <th onclick="sortTable(1)">שם ות"ז ↕</th>
                      <th onclick="sortTable(2)">שלוחה ↕</th>
                      <th onclick="sortTable(3)">שם הקובץ ↕</th>
                      <th onclick="sortTable(4)">אורך קובץ ↕</th>
                      <th onclick="sortTable(5)">האזנה נטו ↕</th>
                      <th onclick="sortTable(6)">אחוז האזנה ↕</th>
                  `;
                  filterRow.innerHTML = `
                      <th><select id="filter-school" class="col-filter-action"><option value="">הכל</option></select></th>
                      <th><input type="text" id="filter-name" class="col-filter-action" placeholder="חיפוש..."></th>
                      <th><input type="text" id="filter-folder" class="col-filter-action" placeholder="סנן שלוחה..."></th>
                      <th><input type="text" id="filter-file" class="col-filter-action" placeholder="סנן קובץ..."></th>
                      <th></th><th></th>
                      <th>
                          <div class="filter-group" style="display:flex; gap:5px; align-items:center;">
                              <span style="font-size:11px">מעל</span>
                              <input type="number" id="filter-percent" class="col-filter-action" min="0" max="100" placeholder="0" style="width:50px">
                              <span style="font-size:11px">%</span>
                          </div>
                      </th>
                  `;
              } else {
                  headerRow.innerHTML = `
                      <th onclick="sortTable(1)">טלפון ↕</th>
                      <th onclick="sortTable(2)">שלוחה ↕</th>
                      <th onclick="sortTable(3)">שם הקובץ ↕</th>
                      <th onclick="sortTable(4)">אורך קובץ ↕</th>
                      <th onclick="sortTable(5)">האזנה נטו ↕</th>
                      <th onclick="sortTable(6)">אחוז האזנה ↕</th>
                  `;
                  filterRow.innerHTML = `
                      <th><input type="text" id="filter-name" class="col-filter-action" placeholder="חיפוש טלפון..."></th>
                      <th><input type="text" id="filter-folder" class="col-filter-action" placeholder="סנן שלוחה..."></th>
                      <th><input type="text" id="filter-file" class="col-filter-action" placeholder="סנן קובץ..."></th>
                      <th></th><th></th>
                      <th>
                          <div class="filter-group" style="display:flex; gap:5px; align-items:center;">
                              <span style="font-size:11px">מעל</span>
                              <input type="number" id="filter-percent" class="col-filter-action" min="0" max="100" placeholder="0" style="width:50px">
                              <span style="font-size:11px">%</span>
                          </div>
                      </th>
                  `;
              }
              document.querySelectorAll('.col-filter-action').forEach(el => { el.oninput = applyMultiFilter; });
          }
       
          function populateSchoolDropdown() {
              if (!hasEnterId) return;
              const schools = [...new Set(currentData.map(item => item.info.School || "-"))].sort();
              const select = document.getElementById('filter-school');
              if (select) {
                  select.innerHTML = '<option value="">הכל</option>';
                  schools.forEach(s => {
                      const opt = document.createElement('option');
                      opt.value = s; opt.textContent = s;
                      select.appendChild(opt);
                  });
              }
          }
       
          function renderTable(data) {
              const tbody = document.getElementById('table-body');
              let html = '';
              data.forEach(item => {
                  const color = item.percent > 80 ? 'bg-green' : (item.percent > 40 ? 'bg-orange' : 'bg-red');
                  
                  let identityCell = '';
                  if (hasEnterId) {
                      identityCell = `<td>${item.info.School || '-'}</td><td><strong>${item.info.ValName}</strong><small>${item.info.EnterId}</small></td>`;
                  } else {
                      identityCell = `<td><strong>${item.info.Phone}</strong></td>`;
                  }
      
                  html += `<tr>
                      ${identityCell}
                      <td>${item.info.Folder.replace(/\//g, '>')}</td>
                      <td>${item.info.Current}</td>
                      <td>${formatSeconds(item.flenSec)}</td>
                      <td>${formatSeconds(item.netSec)}</td>
                      <td>
                          <div class="progress-container">
                              <div class="progress-bar"><div class="progress-fill ${color}" style="width: ${item.percent}%"></div></div>
                              <span style="font-weight:bold; min-width:35px">${item.percent}%</span>
                          </div>
                      </td>
                  </tr>`;
              });
              tbody.innerHTML = html;
              applyMultiFilter();
          }
       
          function applyMultiFilter() {
              const fSchool = hasEnterId ? document.getElementById('filter-school').value.toLowerCase() : "";
              const fName = document.getElementById('filter-name').value.toLowerCase();
              const fFolder = document.getElementById('filter-folder').value.toLowerCase();
              const fFile = document.getElementById('filter-file').value.toLowerCase();
              const fMinPercent = parseInt(document.getElementById('filter-percent').value) || 0;
              const rows = document.querySelectorAll('#table-body tr');
              
              let count = 0;
              rows.forEach(row => {
                  let tSchool = "", tName = "", tFolder = "", tFile = "", tPercent = 0;
                  
                  if (hasEnterId) {
                      tSchool = row.cells[0].innerText.toLowerCase();
                      tName = row.cells[1].innerText.toLowerCase();
                      tFolder = row.cells[2].innerText.toLowerCase();
                      tFile = row.cells[3].innerText.toLowerCase();
                      tPercent = parseInt(row.cells[6].innerText);
                  } else {
                      tName = row.cells[0].innerText.toLowerCase();
                      tFolder = row.cells[1].innerText.toLowerCase();
                      tFile = row.cells[2].innerText.toLowerCase();
                      tPercent = parseInt(row.cells[5].innerText);
                  }
      
                  const match = (fSchool === "" || tSchool === fSchool) && (tName.includes(fName)) && (tFolder.includes(fFolder)) && (tFile.includes(fFile)) && (tPercent >= fMinPercent);
                  row.style.display = match ? '' : 'none';
                  if (match) count++;
              });
              statusMsg.innerHTML = `נמצאו ${count} רשומות לאחר סינון.`;
          }
       
          function sortTable(idx) {
              sortDirections[idx] = !sortDirections[idx];
              const dir = sortDirections[idx] ? 1 : -1;
              currentData.sort((a, b) => {
                  let v1, v2;
                  switch(idx) {
                      case 0: v1 = (a.info.School || "").toLowerCase(); v2 = (b.info.School || "").toLowerCase(); break;
                      case 1: v1 = (hasEnterId ? (a.info.ValName || "") : a.info.Phone).toLowerCase(); v2 = (hasEnterId ? (b.info.ValName || "") : b.info.Phone).toLowerCase(); break;
                      case 2: v1 = a.info.Folder.toLowerCase(); v2 = b.info.Folder.toLowerCase(); break;
                      case 3: v1 = a.info.Current.toLowerCase(); v2 = b.info.Current.toLowerCase(); break;
                      case 4: v1 = a.flenSec; v2 = b.flenSec; break;
                      case 5: v1 = a.netSec; v2 = b.netSec; break;
                      case 6: v1 = a.percent; v2 = b.percent; break;
                  }
                  return v1 < v2 ? -1 * dir : (v1 > v2 ? 1 * dir : 0);
              });
              renderTable(currentData);
          }
       
          function timeToSeconds(t){
              const p = t.trim().split(':').map(Number);
              return p.length === 3 ? p[0]*3600 + p[1]*60 + p[2] : (p.length === 2 ? p[0]*60 + p[1] : 0);
          }
          function formatSeconds(s){
              let h=Math.floor(s/3600), m=Math.floor((s%3600)/60), sec=Math.floor(s%60);
              return `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${sec.toString().padStart(2,'0')}`;
          }
          function mergeIntervals(arr){
              if(!arr.length) return 0;
              arr.sort((a,b) => a[0]-b[0]);
              let total = 0, [start, end] = arr[0];
              for(let i=1; i<arr.length; i++){
                  if(arr[i][0] <= end) end = Math.max(end, arr[i][1]);
                  else { total += (end - start); [start, end] = arr[i]; }
              }
              return total + (end - start);
          }
          function getDatesRange(s,e){
              const d=[]; let c=new Date(s);
              while(c<=e){ d.push(c.toISOString().split('T')[0]); c.setDate(c.getDate()+1); }
              return d;
          }
       
          exportBtn.onclick = function() {
              const rows = document.querySelectorAll('#table-body tr');
              const exportedData = [];
              rows.forEach(row => {
                  if (row.style.display !== 'none') {
                      let obj = {};
                      if (hasEnterId) {
                          obj["כיתה"] = row.cells[0].innerText;
                          obj["שם תלמיד"] = row.cells[1].querySelector('strong').innerText;
                          obj["תעודת זהות"] = row.cells[1].querySelector('small').innerText;
                          obj["שלוחה"] = row.cells[2].innerText;
                          obj["שם הקובץ"] = row.cells[3].innerText;
                          obj["אורך קובץ"] = row.cells[4].innerText;
                          obj["האזנה נטו"] = row.cells[5].innerText;
                          obj["אחוז האזנה"] = row.cells[6].innerText.replace('%', '');
                      } else {
                          obj["טלפון"] = row.cells[0].innerText;
                          obj["שלוחה"] = row.cells[1].innerText;
                          obj["שם הקובץ"] = row.cells[2].innerText;
                          obj["אורך קובץ"] = row.cells[3].innerText;
                          obj["האזנה נטו"] = row.cells[4].innerText;
                          obj["אחוז האזנה"] = row.cells[5].innerText.replace('%', '');
                      }
                      exportedData.push(obj);
                  }
              });
              const ws = XLSX.utils.json_to_sheet(exportedData);
              const wb = XLSX.utils.book_new();
              XLSX.utils.book_append_sheet(wb, ws, "דוח האזנה");
              const ts = new Date().toISOString().replace('T', '_').replace(/[:.]/g, '-').slice(0, 19);
              XLSX.writeFile(wb, `דוח_האזנה_${ts}.xlsx`);
          };
      </script>
       
      </body>
      </html>
      

      פורסם בהסברים מסודרים ממשתמשים
      ע
      ע.ג.
    • RE: רעיון נפלא לחידושים במערכות!!

      @nk-0
      רעיון עצום!!!
      אולי עד שימות יזוזו עם זה, אולי מישהו מרים את הכפפה להתניע כזה דבר באופן עצמאי?
      מה אומרים?

      פורסם בבקשות לפיתוח
      ע
      ע.ג.

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

    • RE: מנסה להפיק דוח שיחות נכנסות למערכת לפי שלוחות

      @על-אוטומט
      שמח לשמוע
      לילה טוב...

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: מנסה להפיק דוח שיחות נכנסות למערכת לפי שלוחות

      @על-אוטומט
      הלוג הזה הוא הלוג שמתעד כל כניסה ויציאה מכל שלוחה:
      LogFolderEnterExit-2026-03.ymgr

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: מנסה להפיק דוח שיחות נכנסות למערכת לפי שלוחות

      @על-אוטומט
      נזכרתי שיש לך את זה
      https://f2.freeivr.co.il/post/164334
      הוא ממש משוכלל
      הוא מושלם!!

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: מנסה להפיק דוח שיחות נכנסות למערכת לפי שלוחות

      @על-אוטומט
      מה שהבאת מציג רק נתונים של האזנות לקבצי שמע, ולכן הוא כביכול "מסנן" לך הרבה שיחות
      קח את הלינק הבא, תשים את מספר המערכת והסיסמה שלך, ותריץ בדפדפן, מיד ירד לך הלוג המלא של החודש המבוקש (החודש שמופיע לקראת סוף הלינק)
      https://www.call2all.co.il/ym/api/RenderYMGRFile?token=0773137770:123456&wath=ivr2:/Log/LogFolderEnterExit-2026-03.ymgr&convertType=csv
      ציינת שאתה חדש בתחום, אז אני רק מציין שהלינק הנ"ל פונה ישירות לשרתים של ימות המשיח, ואין מה לחשוש מכך שמכניסים סיסמה, רק למי שיש גישה למחשב שלך (לדוגמה בחדר מחשבים או ספק האינטרנט וכו') רק לו יש גישה לסיסמה שאתה מריץ

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: קבלת שיחת SIP

      @0799222222
      אני לא רוצה לחייג למספר
      אני רוצה לבצע שיחת IP
      ממרכזיה אחרת למערכת שלי בימות
      אפשרי?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: קבלת שיחת SIP

      @הרב
      יש לך אפשרות לעזור לי?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: קבלת שיחת SIP

      @0799222222
      לא הדגשתי מספיק
      המרכזיה שמעבירה את השיחות לא רוצה לחייג למערכת שלי, אלא רק להפעיל שיחת סיפ למערכת
      אפשרי?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • קבלת שיחת SIP

      שלום רב
      יש מרכזיה שרוצה להעביר אלי שיחת SIP
      כלומר, המרכזיה כביכול "תחייג" למערכת שלי, אך לא חיוג רגיל אלא חיוג SIP
      אני רוצה שה"חיוג" יכנס לשלוחה 99
      יש אפשרות?
      איך?
      אני כמובן לא רוצה להגדיר את המערכת כמערכת מרכזיית SIP בלבד, שכן אז כל שאר השלוחות לא יפעלו
      בתודה מראש..

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: קבצים ותוכנות לשימוש במערכות ימות המשיח

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

      @ע.ג. זה עובד רק עם זהוי?
      בלי זהוי זה לא יכול לעבוד?

      ביצעתי את השינוי, עידכנתי את הקוד בפוסט המקורי
      https://f2.freeivr.co.il/post/176080
      כרגע זה פועל במקביל:
      אם יש במערכת כניסה לפי ת"ז, יוצג לפי שם ות"ז (וללא מספר טלפון)
      אם אין כיסה לפי ת"ז, יופיע מספר טלפון

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ע
      ע.ג.
    • RE: קבצים ותוכנות לשימוש במערכות ימות המשיח

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

      @ע.ג. זה עובד רק עם זהוי?
      בלי זהוי זה לא יכול לעבוד?

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

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