| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | <!DOCTYPE html> |
| | <html dir="rtl" lang="he"> |
| | <head> |
| | <link rel="icon" href="/favicon-32x32.png" type="image/x-icon" sizes="32x32"/> |
| | <meta charset="UTF-8" /> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| | <meta property="og:title" content="הכירו את ארגז הכלים של שגיא בר און"> |
| | <meta property="og:description" content="כנסו עכשיו לכלי המדהים שלנו!"> |
| | <meta property="og:image" content="https://thewitcher-sagi-ai-tools.static.hf.space/sagi_ai_toolbox.png"> |
| | <meta property="og:url" content="https://thewitcher-sagi-ai-tools.static.hf.space"> |
| | <meta property="og:type" content="website"> |
| | <meta name="twitter:card" content="summary_large_image"> |
| |
|
| | <title>ארגז הכלים שלי לבינה מלאכותית</title> |
| | <link |
| | href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" |
| | rel="stylesheet" |
| | /> |
| |
|
| | <style> |
| | @import url('https://fonts.googleapis.com/css2?family=Arimo:wght@400;500;600;700&display=swap'); |
| | |
| | body { |
| | font-family: 'Arimo', sans-serif; |
| | background-color: #f9fafb; |
| | } |
| | |
| | .tool-card:hover { |
| | transform: translateY(-5px); |
| | box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), |
| | 0 10px 10px -5px rgba(0, 0, 0, 0.04); |
| | } |
| | |
| | .category-filter .active { |
| | background-color: #3b82f6; |
| | color: white; |
| | } |
| | |
| | .gradient-text { |
| | background: linear-gradient(90deg, #3b82f6, #8b5cf6); |
| | -webkit-background-clip: text; |
| | background-clip: text; |
| | color: transparent; |
| | } |
| | |
| | |
| | [dir='rtl'] .rotate-180 { |
| | transform: rotate(180deg); |
| | } |
| | |
| | |
| | .mobile-menu { |
| | max-height: 0; |
| | overflow: hidden; |
| | transition: max-height 0.3s ease-out; |
| | } |
| | |
| | .mobile-menu.open { |
| | max-height: 500px; |
| | } |
| | .share-btn.yt { color:#FF0000; } |
| | |
| | .rating-stars { gap:4px; } |
| | |
| | |
| | .admin-badge { |
| | position: absolute; |
| | top: -8px; |
| | right: -8px; |
| | background-color: #ef4444; |
| | color: white; |
| | border-radius: 9999px; |
| | width: 20px; |
| | height: 20px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | font-size: 10px; |
| | } |
| | |
| | |
| | .profile-container { |
| | position: relative; |
| | width: 200px; |
| | height: 200px; |
| | margin: 0 auto; |
| | } |
| | |
| | .profile-image { |
| | width: 100%; |
| | height: 100%; |
| | border-radius: 50%; |
| | object-fit: cover; |
| | border: 4px solid #3b82f6; |
| | box-shadow: 0 0 20px rgba(59, 130, 246, 0.5); |
| | position: relative; |
| | z-index: 2; |
| | } |
| | |
| | .tech-circle { |
| | position: absolute; |
| | border-radius: 50%; |
| | border: 2px solid rgba(59, 130, 246, 0.7); |
| | animation: rotate infinite linear; |
| | } |
| | |
| | .tech-circle-1 { |
| | width: 220px; |
| | height: 220px; |
| | top: -10px; |
| | left: -10px; |
| | animation-duration: 15s; |
| | border-style: dashed; |
| | } |
| | |
| | .tech-circle-2 { |
| | width: 240px; |
| | height: 240px; |
| | top: -20px; |
| | left: -20px; |
| | animation-duration: 20s; |
| | animation-direction: reverse; |
| | } |
| | |
| | .tech-circle-3 { |
| | width: 260px; |
| | height: 260px; |
| | top: -30px; |
| | left: -30px; |
| | animation-duration: 25s; |
| | border-style: dotted; |
| | } |
| | |
| | @keyframes rotate { |
| | from { |
| | transform: rotate(0deg); |
| | } |
| | to { |
| | transform: rotate(360deg); |
| | } |
| | } |
| | @media (min-width: 768px) { |
| | .share-btn { |
| | width: 48px; |
| | height: 48px; |
| | font-size: 24px; |
| | } |
| | } |
| | |
| | .tech-dots { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | border-radius: 50%; |
| | z-index: 1; |
| | } |
| | |
| | .tech-dot { |
| | position: absolute; |
| | width: 8px; |
| | height: 8px; |
| | background-color: #8b5cf6; |
| | border-radius: 50%; |
| | transform: translate(-50%, -50%); |
| | } |
| | |
| | |
| | .ai-particle { |
| | position: absolute; |
| | background: linear-gradient(135deg, #3b82f6, #8b5cf6); |
| | border-radius: 50%; |
| | opacity: 0.6; |
| | filter: blur(10px); |
| | z-index: 0; |
| | } |
| | |
| | .ai-circuit { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-image: radial-gradient( |
| | circle at center, |
| | transparent 0%, |
| | #f9fafb 100% |
| | ), |
| | linear-gradient( |
| | 90deg, |
| | transparent 49%, |
| | rgba(59, 130, 246, 0.1) 50%, |
| | transparent 51% |
| | ), |
| | linear-gradient( |
| | 0deg, |
| | transparent 49%, |
| | rgba(59, 130, 246, 0.1) 50%, |
| | transparent 51% |
| | ); |
| | background-size: 20px 20px; |
| | border-radius: 50%; |
| | opacity: 0.3; |
| | } |
| | |
| | .clickable-stat:hover { |
| | cursor: pointer; |
| | background-color: #f3f4f6; |
| | } |
| | |
| | .share-buttons { |
| | display: flex; |
| | justify-content: center; |
| | gap: 24px; |
| | margin-top: 32px; |
| | } |
| | |
| | .share-btn{ |
| | width:40px;height:40px; |
| | font-size:22px; |
| | color:#fff; |
| | background:currentColor; |
| | border:none;border-radius:12px; |
| | display:flex;align-items:center;justify-content:center; |
| | box-shadow:0 4px 12px rgba(0,0,0,.08); |
| | transition:transform .2s,filter .2s; |
| | } |
| | .share-btn:hover{transform:translateY(-4px);filter:brightness(1.12);} |
| | .share-btn i{color:#fff;} |
| | |
| | |
| | .share-btn.wa{color:#25D366;} |
| | .share-btn.fb{color:#1877F2;} |
| | .share-btn.tw{color:#1DA1F2;} |
| | .share-btn.cp{color:#64748b;} |
| | |
| | .share-btn img { |
| | width: 32px; |
| | height: 32px; |
| | } |
| | |
| | @media (max-width: 768px) { |
| | .favorite-btn { |
| | |
| | right: 10px; |
| | top: 10px; |
| | } |
| | } |
| | |
| | .tool-header { |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | margin-bottom: 10px; |
| | } |
| | |
| | .tool-logo { |
| | width: 50px; |
| | height: 50px; |
| | } |
| | |
| | .tool-name { |
| | font-size: 1.5rem; |
| | font-weight: bold; |
| | text-align: center; |
| | } |
| | |
| | .category-chip { |
| | background: linear-gradient(90deg, #f3f4f6 60%, #e0e7ff 100%); |
| | color: #3730a3; |
| | border: 1px solid #a5b4fc; |
| | box-shadow: 0 2px 8px rgba(59, 130, 246, 0.07); |
| | transition: background 0.2s; |
| | } |
| | .category-chip i { |
| | color: #6366f1; |
| | } |
| | |
| | |
| | .toast-container { |
| | position: fixed; |
| | bottom: 20px; |
| | right: 20px; |
| | z-index: 9999; |
| | } |
| | |
| | .toast { |
| | background: rgba(0, 0, 0, 0.8); |
| | color: white; |
| | padding: 12px 24px; |
| | border-radius: 8px; |
| | margin-top: 10px; |
| | display: flex; |
| | align-items: center; |
| | gap: 8px; |
| | animation: slideIn 0.3s ease-out, fadeOut 0.3s ease-out 2.7s; |
| | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
| | } |
| | |
| | .toast i { |
| | color: #4ade80; |
| | } |
| | |
| | @keyframes slideIn { |
| | from { |
| | transform: translateX(100%); |
| | opacity: 0; |
| | } |
| | to { |
| | transform: translateX(0); |
| | opacity: 1; |
| | } |
| | } |
| | |
| | @keyframes fadeOut { |
| | from { |
| | opacity: 1; |
| | } |
| | to { |
| | opacity: 0; |
| | } |
| | } |
| | </style> |
| | |
| | <script |
| | async |
| | src="https://www.googletagmanager.com/gtag/js?id=G-JVF8N1DVSG" |
| | ></script> |
| | <script> |
| | window.dataLayer = window.dataLayer || []; |
| | function gtag() { |
| | dataLayer.push(arguments); |
| | } |
| | gtag('js', new Date()); |
| | |
| | gtag('config', 'G-JVF8N1DVSG'); |
| | </script> |
| | <link href="favicon-32.png" rel="icon" sizes="32x32" type="image/png" /> |
| | <link href="manifest.json" rel="manifest" /> |
| | <meta content="yes" name="apple-mobile-web-app-capable" /> |
| | <meta content="AI Tools" name="apple-mobile-web-app-title" /> |
| | <link href="icon-192.png" rel="apple-touch-icon" /> |
| | <meta content="#3b82f6" name="theme-color" /> |
| | </head> |
| | <body class="min-h-screen"> |
| | <div |
| | id="newToolBanner" |
| | class="hidden bg-green-100 text-green-800 text-center py-2 text-sm font-semibold" |
| | > |
| | 🎉 התווסף כלי חדש: <span id="newToolName"></span> – |
| | <a href="#toolsContainer" class="underline">צפו עכשיו</a> |
| | <button id="closeBanner" class="ml-4 text-green-800 font-bold">×</button> |
| | </div> |
| |
|
| | |
| | <header |
| | class="sticky top-0 z-50 backdrop-blur-md bg-white/80 shadow-md border-b border-gray-200" |
| | > |
| | <div class="container mx-auto px-4 py-4"> |
| | <div class="flex justify-between items-center"> |
| | <div class="flex items-center"> |
| | |
| | <button id="mobileMenuButton" class="md:hidden text-gray-600 mr-4"> |
| | <i class="fas fa-bars text-xl"></i> |
| | </button> |
| |
|
| | <div> |
| | <h1 class="text-2xl md:text-3xl font-bold gradient-text"> |
| | ארגז הכלים שלי ל-AI |
| | </h1> |
| | <p class="text-gray-600 text-sm md:text-base mt-1"> |
| | אוסף כלי הבינה המלאכותית המומלצים שלי |
| | </p> |
| | </div> |
| | </div> |
| |
|
| | <div class="hidden md:flex items-center space-x-4 space-x-reverse"> |
| | |
| | <button |
| | id="refreshBtn" |
| | class="px-5 py-2 rounded-xl bg-gradient-to-l from-blue-600 to-indigo-500 text-white shadow-md hover:shadow-lg hover:from-blue-700 hover:to-indigo-600 transition-all duration-300" |
| | > |
| | <i class="fas fa-sync-alt ml-2"></i> אפס תצוגה |
| | </button> |
| |
|
| | |
| | <button |
| | id="editJsonBtn" |
| | class="px-5 py-2 rounded-xl bg-gradient-to-l from-pink-500 to-purple-600 text-white shadow-md hover:shadow-lg hover:from-pink-600 hover:to-purple-700 transition-all duration-300 relative" |
| | > |
| | <i class="fas fa-edit ml-2"></i> הציעו כלי חדש |
| | <span class="admin-badge">N</span> |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="mobileMenu" class="mobile-menu md:hidden mt-4"> |
| | <div class="flex flex-col space-y-2 py-2"> |
| | <button |
| | id="refreshBtnMobile" |
| | class="px-5 py-2 rounded-xl bg-gradient-to-l from-blue-600 to-indigo-500 text-white shadow-md hover:shadow-lg hover:from-blue-700 hover:to-indigo-600 transition-all duration-300" |
| | > |
| | <i class="fas fa-sync-alt ml-2"></i> אפס תצוגה |
| | </button> |
| | |
| | <button |
| | id="editJsonBtn" |
| | class="px-5 py-2 rounded-xl bg-gradient-to-l from-pink-500 to-purple-600 text-white shadow-md hover:shadow-lg hover:from-pink-600 hover:to-purple-700 transition-all duration-300 relative" |
| | > |
| | <i class="fas fa-edit ml-2"></i> הציעו כלי חדש |
| | <span class="admin-badge">N</span> |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | </header> |
| |
|
| | |
| | <main class="container mx-auto px-4 py-8"> |
| | |
| | <div |
| | class="bg-white rounded-lg shadow-sm p-6 border border-gray-100 mb-8 relative overflow-hidden" |
| | > |
| | |
| | <div |
| | class="ai-particle" |
| | style="width: 100px; height: 100px; top: -30px; right: -30px" |
| | ></div> |
| | <div |
| | class="ai-particle" |
| | style="width: 150px; height: 150px; bottom: -50px; left: -50px" |
| | ></div> |
| | <div |
| | class="ai-particle" |
| | style="width: 80px; height: 80px; top: 50%; right: 20%" |
| | ></div> |
| |
|
| | <h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-2"> |
| | קצת עליי |
| | </h2> |
| | <div class="flex flex-col md:flex-row gap-6"> |
| | <div class="md:w-1/3"> |
| | <div class="profile-container"> |
| | <div class="ai-circuit"></div> |
| | <div class="tech-circle tech-circle-1"></div> |
| | <div class="tech-circle tech-circle-2"></div> |
| | <div class="tech-circle tech-circle-3"></div> |
| | <div class="tech-dots"> |
| | <div class="tech-dot" style="top: 10%; left: 50%"></div> |
| | <div class="tech-dot" style="top: 50%; left: 10%"></div> |
| | <div class="tech-dot" style="top: 90%; left: 50%"></div> |
| | <div class="tech-dot" style="top: 50%; left: 90%"></div> |
| | <div class="tech-dot" style="top: 30%; left: 30%"></div> |
| | <div class="tech-dot" style="top: 70%; left: 70%"></div> |
| | <div class="tech-dot" style="top: 30%; left: 70%"></div> |
| | <div class="tech-dot" style="top: 70%; left: 30%"></div> |
| | </div> |
| | <img |
| | src="https://i.imgur.com/cnlxCuj.jpeg" |
| | alt="שגיא בר און" |
| | class="profile-image" |
| | /> |
| | </div> |
| | </div> |
| | <div class="md:w-2/3"> |
| | <h3 class="text-xl font-semibold mb-4">שגיא בר און</h3> |
| | <p class="text-gray-700 mb-4"> |
| | אני חוקר ויועץ בתחום הבינה המלאכותית, מאסטר NLP, ובעל ניסיון של |
| | למעלה מ-25 שנה בתעשיית ההייטק. בעל מומחיות רחבה בפיתוח תוכנה, |
| | אוטומציה, ניהול פרויקטים ואסטרטגיה עסקית. |
| | </p> |
| | <p class="text-gray-700 mb-4"> |
| | מרצה אורח באוניברסיטת רייכמן וחבר בסגל הבוחנים של מה"ט - המכון |
| | הממשלתי להכשרה בטכנולוגיה ובמדע - לבחינות מהנדסי תוכנה |
| | באוניברסיטאות ובמכללות, וכן מנטור לAI במסגרת משרד החינוך. |
| | </p> |
| | <p class="text-gray-700 mb-4"> |
| | בעל תואר שני במנהל עסקים עם התמחות בבינה מלאכותית, תואר ראשון |
| | (BSc) במדעי המחשב והנדסאי תוכנה. ההרצאות משלבות ידע עדכני, חשיבה |
| | ביקורתית והתנסות חווייתית, מתוך מטרה להעצים אנשים ולאפשר להם |
| | להשתמש בטכנולוגיה בחוכמה ובקלות. |
| | </p> |
| | <p class="text-gray-700"> |
| | שמתי לי למטרה להנגיש, להסביר ולחבר את הטכנולוגיה בצורה פשוטה |
| | וברורה לכולם. |
| | </p> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="mb-8"> |
| | |
| | <div |
| | class="flex flex-col md:flex-row md:items-center md:justify-between gap-4" |
| | > |
| | <div class="relative w-full md:w-96"> |
| | <input |
| | type="text" |
| | id="searchInput" |
| | placeholder="חפש כלים..." |
| | class="w-full pr-10 pl-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition" |
| | /> |
| | <i class="fas fa-search absolute right-3 top-3.5 text-gray-400"></i> |
| | </div> |
| | <div class="flex items-center space-x-2 space-x-reverse"> |
| | <span class="text-gray-600 hidden md:block">סנן לפי:</span> |
| | <div class="category-filter flex flex-wrap gap-2"> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="all" |
| | > |
| | הכל |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-yellow-400 text-yellow-700 bg-yellow-100 hover:bg-yellow-200" |
| | data-category="free" |
| | > |
| | חינם |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="productivity" |
| | > |
| | פרודוקטיביות |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="writing" |
| | > |
| | כתיבה |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="design" |
| | > |
| | עיצוב |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="coding" |
| | > |
| | תכנות |
| | </button> |
| | <button |
| | class="filter-btn px-3 py-1 rounded-full border text-sm transition border-gray-300 text-gray-700 bg-white hover:bg-blue-50" |
| | data-category="video" |
| | > |
| | וידאו |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8"> |
| | |
| | <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| | <div class="flex items-center"> |
| | <div class="p-3 rounded-full bg-blue-50 text-blue-600 ml-3"> |
| | <i class="fas fa-tools text-lg"></i> |
| | </div> |
| | <div> |
| | <p class="text-gray-500 text-sm">סה"כ כלים</p> |
| | <h3 class="text-xl font-semibold" id="totalTools">0</h3> |
| | </div> |
| | </div> |
| | </div> |
| | <div |
| | id="topRatedStatBox" |
| | class="bg-white p-4 rounded-lg shadow-sm border border-gray-100 transition clickable-stat" |
| | > |
| | <div class="flex items-center"> |
| | <div class="p-3 rounded-full bg-purple-50 text-purple-600 ml-3"> |
| | <i class="fas fa-star text-lg"></i> |
| | </div> |
| | <div> |
| | <p class="text-gray-500 text-sm">מובילים בדירוג</p> |
| | <h3 class="text-xl font-semibold" id="topRated">0</h3> |
| | </div> |
| | </div> |
| | </div> |
| | <div |
| | id="newToolsStatBox" |
| | class="bg-white p-4 rounded-lg shadow-sm border border-gray-100 transition clickable-stat" |
| | > |
| | <div class="flex items-center"> |
| | <div class="p-3 rounded-full bg-green-50 text-green-600 ml-3"> |
| | <i class="fas fa-bolt text-lg"></i> |
| | </div> |
| | <div> |
| | <p class="text-gray-500 text-sm">חדשים השבוע</p> |
| | <h3 class="text-xl font-semibold" id="newTools">0</h3> |
| | </div> |
| | </div> |
| | </div> |
| | <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| | <div class="flex items-center"> |
| | <div class="p-3 rounded-full bg-yellow-50 text-yellow-600 ml-3"> |
| | <i class="fas fa-tags text-lg"></i> |
| | </div> |
| | <div> |
| | <p class="text-gray-500 text-sm">קטגוריות</p> |
| | <h3 class="text-xl font-semibold" id="totalCategories">0</h3> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div |
| | class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" |
| | id="toolsContainer" |
| | > |
| | |
| | <p id="loadingMessage" class="text-center text-gray-500 col-span-full"> |
| | טוען כלים... |
| | </p> |
| | </div> |
| |
|
| | |
| | <div class="mt-16"> |
| | |
| | <h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-2"> |
| | סרטונים נבחרים מערוץ היוטיוב שלי |
| | </h2> |
| | <div |
| | class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" |
| | id="videosContainer" |
| | > |
| | |
| | </div> |
| | </div> |
| | </main> |
| |
|
| | |
| | <div |
| | id="jsonEditorModal" |
| | class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden" |
| | > |
| | |
| | <div |
| | class="bg-white rounded-lg shadow-xl w-full max-w-4xl max-h-[90vh] flex flex-col" |
| | > |
| | <div |
| | class="px-6 py-4 border-b border-gray-200 flex justify-between items-center" |
| | > |
| | <h3 class="text-lg font-semibold">עריכת הנתונים ב-JSON</h3> |
| | <button id="closeModalBtn" class="text-gray-500 hover:text-gray-700"> |
| | <i class="fas fa-times"></i> |
| | </button> |
| | </div> |
| | <div class="p-6 flex-1 overflow-auto"> |
| | <div class="flex mb-4"> |
| | <button |
| | id="showToolsBtn" |
| | class="px-4 py-2 bg-blue-600 text-white rounded-l-lg" |
| | > |
| | כלים |
| | </button> |
| | <button |
| | id="showVideosBtn" |
| | class="px-4 py-2 bg-gray-200 text-gray-700 rounded-r-lg" |
| | > |
| | סרטונים |
| | </button> |
| | </div> |
| | <textarea |
| | id="jsonEditor" |
| | class="w-full h-96 p-4 border border-gray-300 rounded-lg font-mono text-sm" |
| | spellcheck="false" |
| | style="direction: ltr; text-align: left" |
| | ></textarea> |
| | </div> |
| | <div |
| | class="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3 space-x-reverse" |
| | > |
| | <button |
| | id="cancelEditBtn" |
| | class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition" |
| | > |
| | ביטול |
| | </button> |
| | <button |
| | id="saveJsonBtn" |
| | class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition" |
| | > |
| | שמור שינויים (באחסון המקומי) |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | <div |
| | id="suggestToolModal" |
| | class="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-[9999] px-4 hidden" |
| | > |
| | <div |
| | class="bg-white rounded-lg w-full max-w-md shadow-lg max-h-[90vh] overflow-hidden flex flex-col text-right relative" |
| | > |
| | |
| | <div class="p-6 overflow-y-auto flex-grow"> |
| | <h2 class="text-xl font-semibold mb-4">הצעת כלי חדש</h2> |
| | <form id="suggestToolForm" class="space-y-4"> |
| | <div> |
| | <label class="block mb-1">שם הכלי</label> |
| | <input |
| | type="text" |
| | name="name" |
| | required |
| | class="w-full border rounded p-2" |
| | /> |
| | </div> |
| |
|
| | <div> |
| | <label class="block mb-1">לינק</label> |
| | <input |
| | type="url" |
| | name="url" |
| | required |
| | placeholder="כתובת URL של הכלי: לדוגמא: https://linktr.ee/sagib" |
| | dir="ltr" |
| | class="w-full border rounded p-2" |
| | /> |
| | </div> |
| |
|
| | <div> |
| | <label class="block mb-1">תיאור קצר</label> |
| | <textarea |
| | name="description" |
| | required |
| | class="w-full border rounded p-2" |
| | ></textarea> |
| | </div> |
| |
|
| | <div> |
| | <label class="block mb-1">השם שלך</label> |
| | <input |
| | type="text" |
| | name="userName" |
| | placeholder="איך לקרוא לך כשנחזור אליך?" |
| | class="w-full border rounded p-2" |
| | /> |
| | </div> |
| |
|
| | <div> |
| | <label class="block mb-1">מס' טלפון נייד</label> |
| | <input |
| | type="tel" |
| | name="userPhone" |
| | placeholder="05X-XXXXXXX" |
| | class="w-full border rounded p-2" |
| | dir="ltr" |
| | /> |
| | </div> |
| |
|
| | <p class="text-sm text-gray-500"> |
| | נשמח לחזור אליך כשהכלי שצעת נוסף לאתר ❤️ |
| | </p> |
| | <p class="text-sm text-gray-500 italic"> |
| | ✦ הפרטים שלך נשמרים אצלנו רק לצורך עדכון – אין שימוש אחר. |
| | </p> |
| | </form> |
| | </div> |
| |
|
| | |
| | <div |
| | class="sticky bottom-0 bg-white/80 backdrop-blur-md px-6 py-4 border-t flex justify-center space-x-4 space-x-reverse z-10 rounded-b-lg" |
| | > |
| | <button |
| | type="button" |
| | onclick="closeSuggestModal()" |
| | class="px-5 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300" |
| | > |
| | ביטול |
| | </button> |
| | <button |
| | type="submit" |
| | form="suggestToolForm" |
| | class="px-6 py-2 bg-purple-600 text-white rounded hover:bg-purple-700" |
| | > |
| | שלח |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | <div |
| | id="videoModal" |
| | class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-[9999]" |
| | > |
| | <div |
| | class="bg-white rounded-lg overflow-hidden w-full max-w-3xl shadow-lg relative" |
| | > |
| | <button |
| | onclick="closeVideoModal()" |
| | class="absolute top-4 right-4 bg-white bg-opacity-90 border border-gray-300 rounded-full p-3 shadow-lg text-gray-900 hover:bg-red-600 hover:text-white focus:outline-none focus:ring-2 focus:ring-red-400 text-3xl z-[10000] transition-all duration-200" |
| | > |
| | <i class="fas fa-times"></i> |
| | </button> |
| | <iframe |
| | id="videoIframe" |
| | class="w-full h-[60vh]" |
| | src="" |
| | frameborder="0" |
| | allowfullscreen |
| | ></iframe> |
| | </div> |
| | </div> |
| |
|
| | |
| | <footer |
| | class="fixed bottom-0 left-0 w-full z-50 backdrop-blur-md bg-white/80 shadow-xl border-t border-gray-200" |
| | > |
| | |
| | |
| | <div id="chatBubble" class="fixed bottom-8 right-5 z-[9999]"> |
| | |
| | <div |
| | id="chatIntro" |
| | class="bg-white p-3 rounded-lg shadow-md mb-2 text-sm max-w-xs chat-intro-enter hidden" |
| | > |
| | שלום! אני עוזר AI של שגיא. אשמח לעזור לך למצוא כלי AI מתאים או לענות |
| | על שאלות. |
| | <button onclick="hideChatIntro()" class="text-gray-500 float-left"> |
| | <i class="fas fa-times"></i> |
| | </button> |
| | </div> |
| |
|
| | |
| | <button |
| | id="chatButton" |
| | onclick="toggleChatWindow()" |
| | class="chat-pulse bg-gradient-to-r from-purple-500 to-indigo-600 hover:from-purple-600 hover:to-indigo-700 text-white px-4 py-3 rounded-full shadow-xl flex items-center space-x-2 space-x-reverse" |
| | > |
| | <i class="fas fa-robot"></i> |
| | <span>צ'אט</span> |
| | </button> |
| |
|
| | |
| | <div |
| | id="chatWindow" |
| | class="hidden mt-3 w-[360px] h-[500px] bg-white rounded-2xl shadow-2xl border border-gray-200 overflow-hidden relative" |
| | > |
| | |
| | <div |
| | class="bg-gradient-to-r from-purple-500 to-indigo-600 text-white p-3 flex justify-between items-center" |
| | > |
| | <button onclick="toggleChatWindow()" class="text-white"> |
| | <i class="fas fa-times"></i> |
| | </button> |
| | <div class="chat-header"> |
| | צ'אט עם הבינה של שגיא |
| | <span |
| | id="toolsCount" |
| | class="ml-2 text-sm bg-white text-purple-600 font-semibold px-2 py-1 rounded-full shadow-sm" |
| | ></span> |
| | </div> |
| | |
| | <div class="w-6"></div> |
| | |
| | </div> |
| |
|
| | |
| | <iframe |
| | src="https://sagi-ba-sagi-ai-tools-chatbot-main-g1prqf.streamlit.app/?embed=true" |
| | class="w-full h-full border-none" |
| | ></iframe> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | function toggleChatWindow() { |
| | const chatWindow = document.getElementById('chatWindow'); |
| | chatWindow.classList.toggle('hidden'); |
| | } |
| | </script> |
| |
|
| | |
| | <div class="container mx-auto px-4 py-3 text-center space-y-2"> |
| | |
| | <div id="installAppContainer" class="flex justify-center"> |
| | <button |
| | id="installAppBtn" |
| | class="mt-2 px-4 py-2 rounded-xl bg-gradient-to-br from-green-500 to-emerald-600 text-white font-bold shadow hover:scale-105 transition" |
| | style="display: none" |
| | > |
| | 📲 התקנת האפליקציה |
| | </button> |
| | </div> |
| |
|
| | <div class="flex justify-center gap-4"> |
| | |
| | <a |
| | href="https://api.whatsapp.com/send?phone=972549995050" |
| | target="_blank" |
| | class="w-9 h-9 rounded-full bg-white shadow flex items-center justify-center text-green-500 hover:bg-green-500 hover:text-white transition" |
| | ><i class="fab fa-whatsapp"></i |
| | ></a> |
| | <a |
| | href="http://www.linkedin.com/in/sagi-bar-on" |
| | target="_blank" |
| | class="w-9 h-9 rounded-full bg-white shadow flex items-center justify-center text-blue-500 hover:bg-blue-700 hover:text-white transition" |
| | ><i class="fab fa-linkedin-in"></i |
| | ></a> |
| | <a |
| | href="https://www.facebook.com/SAGI.BARON" |
| | target="_blank" |
| | class="w-9 h-9 rounded-full bg-white shadow flex items-center justify-center text-blue-600 hover:bg-blue-800 hover:text-white transition" |
| | ><i class="fab fa-facebook-f"></i |
| | ></a> |
| | <a |
| | href="https://www.youtube.com/@SAGIBARON" |
| | target="_blank" |
| | class="w-9 h-9 rounded-full bg-white shadow flex items-center justify-center text-red-500 hover:bg-red-700 hover:text-white transition" |
| | ><i class="fab fa-youtube"></i |
| | ></a> |
| | </div> |
| | <button |
| | id="editJsonBtnFooter" |
| | class="px-4 py-2 rounded-xl bg-gradient-to-br from-pink-500 to-purple-600 text-white font-bold shadow hover:scale-105 transition" |
| | > |
| | <i class="fas fa-pen-to-square ml-2"></i> הציעו כלי חדש |
| | </button> |
| | <p class="text-gray-500 text-xs"> |
| | © 2025 כל הזכויות שמורות לשגיא בר און |
| | </p> |
| | </div> |
| | |
| | </footer> |
| |
|
| | |
| | <script src="https://cdn.tailwindcss.com"></script> |
| |
|
| | |
| | |
| | |
| | <script> |
| | // --- DOM Elements --- |
| | const toolsContainer = document.getElementById('toolsContainer'); |
| | const videosContainer = document.getElementById('videosContainer'); |
| | const searchInput = document.getElementById('searchInput'); |
| | const filterButtons = document.querySelectorAll('.filter-btn'); |
| | const totalToolsElement = document.getElementById('totalTools'); |
| | const topRatedElement = document.getElementById('topRated'); |
| | const newToolsElement = document.getElementById('newTools'); |
| | const totalCategoriesElement = document.getElementById('totalCategories'); |
| | const mobileMenuButton = document.getElementById('mobileMenuButton'); |
| | const mobileMenu = document.getElementById('mobileMenu'); |
| | const jsonEditorModal = document.getElementById('jsonEditorModal'); |
| | const jsonEditor = document.getElementById('jsonEditor'); |
| | const closeModalBtn = document.getElementById('closeModalBtn'); |
| | const cancelEditBtn = document.getElementById('cancelEditBtn'); |
| | const saveJsonBtn = document.getElementById('saveJsonBtn'); |
| | const showToolsBtn = document.getElementById('showToolsBtn'); |
| | const showVideosBtn = document.getElementById('showVideosBtn'); |
| | const refreshBtn = document.getElementById('refreshBtn'); |
| | const refreshBtnMobile = document.getElementById('refreshBtnMobile'); |
| | const loadingMessage = document.getElementById('loadingMessage'); // For showing loading status |
| | |
| | const topRatedStatBox = document.getElementById('topRatedStatBox'); |
| | const newToolsStatBox = document.getElementById('newToolsStatBox'); |
| | const totalToolsStatBox = document |
| | .querySelector('[id="totalTools"]') |
| | .closest('.bg-white'); |
| | let filteredStatView = null; // 'topRated', 'newTools', או null |
| | |
| | if (totalToolsStatBox) { |
| | totalToolsStatBox.classList.add('clickable-stat'); |
| | totalToolsStatBox.addEventListener('click', () => { |
| | filteredStatView = null; |
| | currentSearchTerm = ''; |
| | currentCategory = 'all'; |
| | document |
| | .querySelectorAll('.filter-btn') |
| | .forEach((btn) => btn.classList.remove('active')); |
| | const allBtn = document.querySelector( |
| | '.filter-btn[data-category="all"]' |
| | ); |
| | if (allBtn) allBtn.classList.add('active'); |
| | renderTools(); |
| | scrollToTools(); |
| | }); |
| | } |
| | |
| | if (topRatedStatBox) { |
| | topRatedStatBox.addEventListener('click', () => { |
| | filteredStatView = 'topRated'; |
| | currentSearchTerm = ''; |
| | currentCategory = 'all'; |
| | renderTools(); |
| | scrollToTools(); |
| | }); |
| | } |
| | |
| | if (newToolsStatBox) { |
| | newToolsStatBox.addEventListener('click', () => { |
| | filteredStatView = 'newTools'; |
| | currentSearchTerm = ''; |
| | currentCategory = 'all'; |
| | renderTools(); |
| | scrollToTools(); |
| | }); |
| | } |
| | |
| | // --- State --- |
| | let currentCategory = 'all'; |
| | let currentSearchTerm = ''; |
| | let toolsData = { tools: [], videos: [] }; // Holds the data fetched from server |
| | let currentSort = 'newest'; // 'newest', 'rating' |
| | |
| | // --- Initialize --- |
| | async function init() { |
| | showLoading('טוען נתונים...'); |
| | |
| | try { |
| | await loadData(); // המתנה לטעינת הנתונים |
| | |
| | renderTools(); |
| | renderVideos(); |
| | updateStats(); |
| | setupEventListeners(); |
| | updateCategoryFilters(); // הוספת כפתור המועדפים |
| | |
| | // הגדרת כפתור "הכל" כפעיל בהתחלה |
| | const allFilterBtn = document.querySelector( |
| | '.filter-btn[data-category="all"]' |
| | ); |
| | if (allFilterBtn) { |
| | allFilterBtn.classList.add('active'); |
| | } |
| | |
| | hideLoading(); |
| | } catch (error) { |
| | console.error('שגיאה באתחול:', error); |
| | showError('אירעה שגיאה בטעינת האתר. אנא נסה לרענן את הדף.'); |
| | hideLoading(); |
| | } |
| | } |
| | |
| | const speechSynthesis = window.speechSynthesis; |
| | let currentUtterance = null; |
| | |
| | // פונקציה להקראת טקסט בעברית |
| | function speakText(text) { |
| | // עצירת הקראה קודמת אם יש |
| | if (speechSynthesis.speaking) { |
| | speechSynthesis.cancel(); |
| | |
| | // אם זאת אותה הקראה שכבר פועלת, פשוט נעצור ונצא |
| | if (currentUtterance && currentUtterance.text === text) { |
| | currentUtterance = null; |
| | return; |
| | } |
| | } |
| | |
| | // יצירת הקראה חדשה |
| | const utterance = new SpeechSynthesisUtterance(text); |
| | |
| | // הגדרת השפה לעברית |
| | utterance.lang = 'he-IL'; |
| | |
| | // בדיקת קולות זמינים ובחירת קול עברי |
| | const voices = speechSynthesis.getVoices(); |
| | |
| | if (voices.length) { |
| | const hebrewVoice = voices.find((voice) => |
| | /he|hebrew|ivrit/i.test(voice.lang) |
| | ); |
| | |
| | if (hebrewVoice) { |
| | utterance.voice = hebrewVoice; |
| | console.log('נבחר קול עברי:', hebrewVoice.name); |
| | } else { |
| | console.log('לא נמצא קול עברי, משתמש בקול ברירת מחדל'); |
| | } |
| | // ננסה להשתמש בקול גוגל |
| | const googleVoice = voices.find( |
| | (voice) => |
| | voice.name.includes('Google') && |
| | (voice.name.includes('US') || voice.name.includes('UK')) |
| | ); |
| | |
| | if (googleVoice) { |
| | utterance.voice = googleVoice; |
| | } |
| | } else { |
| | // במקרה שהקולות עדיין לא נטענו, נגדיר מאזין חד-פעמי |
| | speechSynthesis.addEventListener( |
| | 'voiceschanged', |
| | function loadVoice() { |
| | const voices = speechSynthesis.getVoices(); |
| | const hebrewVoice = voices.find((voice) => |
| | /he|hebrew|ivrit/i.test(voice.lang) |
| | ); |
| | if (hebrewVoice) { |
| | utterance.voice = hebrewVoice; |
| | console.log('נבחר קול עברי (מדחייה):', hebrewVoice.name); |
| | } |
| | // הסרת המאזין אחרי הפעלה ראשונה |
| | speechSynthesis.removeEventListener('voiceschanged', loadVoice); |
| | }, |
| | { once: true } |
| | ); |
| | } |
| | |
| | // שמירת ההקראה הנוכחית |
| | currentUtterance = utterance; |
| | |
| | // טיפול באירוע סיום הקראה |
| | utterance.onend = () => { |
| | currentUtterance = null; |
| | |
| | // עדכון כל הכפתורים למצב "לא מנגן" |
| | document.querySelectorAll('.speak-button').forEach((btn) => { |
| | btn.innerHTML = '<i class="fas fa-volume-up"></i>'; |
| | btn.classList.remove('speaking'); |
| | }); |
| | }; |
| | |
| | // הפעלת ההקראה |
| | speechSynthesis.speak(utterance); |
| | } |
| | |
| | // --- UI Feedback --- |
| | function showLoading() { |
| | if (loadingMessage) loadingMessage.style.display = 'block'; |
| | if (toolsContainer) toolsContainer.innerHTML = ''; // Clear container while loading |
| | } |
| | |
| | function hideLoading() { |
| | if (loadingMessage) loadingMessage.style.display = 'none'; |
| | } |
| | |
| | function showError(message) { |
| | if (toolsContainer) { |
| | toolsContainer.innerHTML = `<p class="text-center text-red-600 col-span-full">${message}</p>`; |
| | } |
| | hideLoading(); // Make sure loading message is hidden |
| | } |
| | |
| | // --- Data Fetching --- |
| | async function fetchDefaultData() { |
| | console.log('Fetching latest data from server...'); // Log fetching attempt |
| | try { |
| | const [toolsResponse, videosResponse] = await Promise.all([ |
| | fetch('tools.json?cacheBust=' + Date.now()), // Cache busting |
| | fetch('videos.json?cacheBust=' + Date.now()), // Cache busting |
| | ]); |
| | |
| | if (!toolsResponse.ok || !videosResponse.ok) { |
| | const errorMsg = `HTTP error! Status: Tools ${toolsResponse.status}, Videos ${videosResponse.status}`; |
| | console.error(errorMsg); |
| | throw new Error('שגיאה בקבלת נתונים מהשרת.'); // User-friendly error |
| | } |
| | |
| | const [toolsArray, videosArray] = await Promise.all([ |
| | toolsResponse.json(), |
| | videosResponse.json(), |
| | ]); |
| | |
| | // --- Process fetched data with defaults --- |
| | const processedTools = toolsArray.map((tool) => ({ |
| | ...tool, |
| | rating: tool.rating ?? 0, |
| | isNew: tool.isNew ?? false, |
| | category: tool.category ?? 'general', |
| | icon: tool.icon || 'fas fa-tools', |
| | url: tool.url || '#', |
| | description: tool.description || 'אין תיאור זמין.', |
| | name: tool.name || 'שם לא ידוע', |
| | })); |
| | const processedVideos = videosArray.map((video) => ({ |
| | ...video, |
| | url: video.url || '#', |
| | title: video.title || 'כותרת חסרה', |
| | description: video.description || 'אין תיאור זמין.', |
| | date: video.date ?? new Date().toISOString(), |
| | })); |
| | |
| | console.log('Data fetched successfully.'); |
| | return { tools: processedTools, videos: processedVideos }; |
| | } catch (error) { |
| | console.error('Failed to fetch default data:', error); |
| | // Re-throw the error or a more specific one to be caught by the caller |
| | throw new Error( |
| | 'כשל בטעינת נתוני ברירת המחדל מהשרת. בדוק את הקבצים tools.json ו-videos.json.' |
| | ); |
| | } |
| | } |
| | |
| | // --- Data Loading Strategy --- |
| | // MODIFIED: Always fetch fresh data from the server on load. |
| | async function loadData() { |
| | console.log('loadData called - fetching fresh data.'); |
| | try { |
| | toolsData = await fetchDefaultData(); |
| | // Note: We are NOT saving to localStorage here anymore for loading purposes. |
| | // saveData(); // Removed - No longer saving fetched data automatically |
| | } catch (error) { |
| | console.error('Error in loadData:', error); |
| | toolsData = { tools: [], videos: [] }; // Set empty data on error |
| | // Re-throw error so init() can handle UI feedback |
| | throw error; |
| | } |
| | } |
| | |
| | // --- Save Data to Local Storage (Used ONLY by JSON Editor) --- |
| | // Note: Data saved here will be overwritten on next page load/refresh. |
| | function saveData() { |
| | try { |
| | // Save the current state (potentially modified by editor) to localStorage |
| | localStorage.setItem('aiToolsData', JSON.stringify(toolsData)); |
| | console.log( |
| | 'Data saved to localStorage (by editor). Will be overwritten on next load.' |
| | ); |
| | } catch (e) { |
| | console.error('Error saving data to localStorage:', e); |
| | alert('שגיאה בשמירת הנתונים באחסון המקומי.'); |
| | } |
| | } |
| | |
| | // --- Sorting Function (Unchanged) --- |
| | function sortTools(sortBy) { |
| | if (!toolsData || !Array.isArray(toolsData.tools)) return; |
| | console.log('Sorting by:', sortBy); |
| | currentSort = sortBy; |
| | toolsData.tools.sort((a, b) => { |
| | if (sortBy === 'newest') { |
| | const aIsNew = a.isNew || false; |
| | const bIsNew = b.isNew || false; |
| | if (aIsNew !== bIsNew) return bIsNew - aIsNew; |
| | return 0; |
| | } else if (sortBy === 'rating') { |
| | const aRating = a.rating || 0; |
| | const bRating = b.rating || 0; |
| | if (bRating !== aRating) return bRating - aRating; |
| | return 0; |
| | } |
| | return 0; |
| | }); |
| | } |
| | function scrollToTools() { |
| | const toolsSection = document.getElementById('toolsContainer'); |
| | if (toolsSection) { |
| | toolsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| | } |
| | } |
| | |
| | // --- Rendering Functions (Unchanged logic, but added hideLoading) --- |
| | function renderTools() { |
| | hideLoading(); // Ensure loading message is hidden before rendering |
| | if (!toolsContainer || !toolsData || !Array.isArray(toolsData.tools)) { |
| | console.error( |
| | 'Cannot render tools: Missing container or invalid data.' |
| | ); |
| | showError('שגיאה בהצגת הכלים.'); |
| | return; |
| | } |
| | const filteredTools = filterTools(); |
| | toolsContainer.innerHTML = ''; // Clear previous content |
| | |
| | if ( |
| | filteredTools.length === 0 && |
| | currentSearchTerm === '' && |
| | currentCategory === 'all' |
| | ) { |
| | // Show specific message if no tools loaded at all |
| | showError('לא נטענו כלים. בדוק את קובץ tools.json או נסה לרענן.'); |
| | } else if (filteredTools.length === 0) { |
| | toolsContainer.innerHTML = |
| | '<p class="text-center text-gray-500 col-span-full">לא נמצאו כלים התואמים את החיפוש, הסינון והמיון.</p>'; |
| | } else { |
| | filteredTools.forEach((tool, index) => { |
| | const toolCard = document.createElement('div'); |
| | toolCard.className = `relative tool-card bg-white rounded-lg shadow-sm border border-gray-100 p-6 transition duration-300 ${ |
| | tool.isFeatured ? 'ring-2 ring-blue-500' : '' |
| | }`; |
| | |
| | // הכנת הטקסט להקראה - שילוב של שם הכלי והתיאור שלו |
| | const speakableText = `${tool.name}. ${tool.description}`; |
| | |
| | // הוספת מאפיין לסינון כלים חינמיים |
| | if (tool.free) { |
| | toolCard.setAttribute('data-free', 'true'); |
| | toolCard.classList.add( |
| | 'border', |
| | 'border-yellow-400', |
| | 'shadow-md' |
| | ); |
| | } |
| | toolCard.setAttribute('data-tool', 'true'); |
| | |
| | toolCard.innerHTML = ` |
| | <div class="relative flex items-center gap-3 mb-2"> |
| | |
| | <button title="הקרא תיאור" onclick="event.stopPropagation(); speakText('${tool.description.replace(/'/g, "\\'")}')" class="speak-button absolute top-0 left-0 z-10 p-2 text-blue-600 hover:text-blue-800 focus:outline-none" style="background:rgba(255,255,255,0.8); border-radius:50%;"> |
| | <i class="fas fa-volume-up"></i> |
| | </button> |
| | <img src="${tool.logo}" alt="${tool.name} Logo" class="tool-logo w-14 h-14 object-contain rounded bg-white p-1 shadow-sm ml-2"> |
| | <h3 class="tool-name text-2xl font-bold text-gray-800">${tool.name}</h3> |
| | </div> |
| | <div class="flex items-center gap-2 mb-3"> |
| | <span class="category-chip px-3 py-1 rounded-full font-semibold text-sm shadow-sm ${getCategoryBadgeColor(tool.category)} flex items-center gap-1"> |
| | <i class="${tool.icon} text-base"></i> |
| | ${getCategoryName(tool.category)} |
| | </span> |
| | </div> |
| | <div class="flex items-center gap-2 mb-3"> |
| | ${tool.isNew ? `<span class="text-xs bg-green-500 text-white px-2 py-1 rounded-full animate-pulse">חדש!</span>` : ''} |
| | ${tool.free ? `<span class="text-xs bg-yellow-400 text-black px-2 py-1 rounded-full">חינם</span>` : ''} |
| | <button class="favorite-btn p-2 text-yellow-400 hover:text-yellow-500 transition" data-id="${index}" title="הוסף למועדפים"> |
| | <i class="far fa-star text-xl"></i> |
| | </button> |
| | </div> |
| | <p class="text-gray-700 mb-4 text-base leading-relaxed min-h-[60px]">${tool.description}</p> |
| | <div class="flex justify-center items-center gap-1 mb-4"> |
| | ${renderRatingStars(tool.rating)} |
| | </div> |
| | <a href="${tool.url}" target="_blank" rel="noopener noreferrer" |
| | class="inline-block w-full text-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition ${tool.url === '#' ? 'opacity-50 cursor-not-allowed' : ''}"> |
| | <i class="fas fa-external-link-alt ml-2"></i> ${tool.url !== '#' ? 'גישה לכלי' : 'אין קישור'} |
| | </a> |
| | ${renderShareButtons(tool)} |
| | `; |
| | |
| | toolsContainer.appendChild(toolCard); |
| | |
| | // אנימציה של הופעה חלקה |
| | requestAnimationFrame(() => { |
| | toolCard.classList.add('visible'); |
| | }); |
| | }); |
| | |
| | // הפעלת פונקציות האתחול לאחר הוספת הכלים לדף |
| | setupFavorites(); |
| | setupShareButtons(); |
| | |
| | // הוספת סגנון CSS לכפתור הקראה פעיל |
| | if (!document.getElementById('speakButtonStyle')) { |
| | const style = document.createElement('style'); |
| | style.id = 'speakButtonStyle'; |
| | style.innerHTML = ` |
| | .speak-button.speaking { color: #4f46e5; animation: pulse 1.5s infinite; } |
| | @keyframes pulse { |
| | 0% { transform: scale(1); } |
| | 50% { transform: scale(1.2); } |
| | 100% { transform: scale(1); } |
| | } |
| | `; |
| | document.head.appendChild(style); |
| | } |
| | } |
| | } |
| | |
| | // פונקציה נוספת שמוחקת את ההקראה בעת עזיבת העמוד |
| | function cleanupSpeech() { |
| | if (speechSynthesis) { |
| | speechSynthesis.cancel(); |
| | } |
| | } |
| | |
| | // הוספת אירוע לניקוי ההקראה בעת עזיבת העמוד |
| | window.addEventListener('beforeunload', cleanupSpeech); |
| | |
| | // עדכון אירוע הקלקה על כפתור הקראה - להוסיף מחלקת CSS ולשנות אייקון |
| | document.addEventListener('click', function (e) { |
| | if (e.target.closest('.speak-button')) { |
| | const button = e.target.closest('.speak-button'); |
| | |
| | // החלפת האייקון והוספת מחלקת CSS מהבהבת |
| | if (speechSynthesis.speaking && currentUtterance) { |
| | // אם יש הקראה פעילה, נעדכן את כל הכפתורים |
| | document.querySelectorAll('.speak-button').forEach((btn) => { |
| | btn.innerHTML = '<i class="fas fa-volume-up"></i>'; |
| | btn.classList.remove('speaking'); |
| | }); |
| | |
| | // ואז נעדכן את הכפתור הנוכחי אם ההקראה ממשיכה |
| | if (!button.classList.contains('speaking')) { |
| | button.innerHTML = '<i class="fas fa-volume-mute"></i>'; |
| | button.classList.add('speaking'); |
| | } |
| | } else { |
| | button.innerHTML = '<i class="fas fa-volume-up"></i>'; |
| | button.classList.remove('speaking'); |
| | } |
| | } |
| | }); |
| | |
| | function renderVideos() { |
| | // ... (renderVideos logic remains the same as before) ... |
| | if ( |
| | !videosContainer || |
| | !toolsData || |
| | !Array.isArray(toolsData.videos) |
| | ) { |
| | console.error( |
| | 'Cannot render videos: Missing container or invalid data.' |
| | ); |
| | if (videosContainer) |
| | videosContainer.innerHTML = |
| | '<p class="text-center text-red-500 col-span-full">שגיאה בהצגת הסרטונים.</p>'; |
| | return; |
| | } |
| | videosContainer.innerHTML = ''; |
| | if (toolsData.videos.length === 0) { |
| | videosContainer.innerHTML = |
| | '<p class="text-center text-gray-500 col-span-full">אין סרטונים להצגה.</p>'; |
| | } else { |
| | toolsData.videos.forEach((video) => { |
| | const videoId = getYouTubeID(video.url); |
| | const embedUrl = videoId |
| | ? `https://www.youtube.com/embed/${videoId}` |
| | : '#'; |
| | const videoCard = document.createElement('div'); |
| | videoCard.className = |
| | 'bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden'; |
| | videoCard.innerHTML = ` |
| | <div class="relative pt-[56.25%] ${ |
| | !videoId |
| | ? 'bg-gray-200 flex items-center justify-center' |
| | : '' |
| | }"> |
| | ${ |
| | videoId |
| | ? `<iframe class="absolute top-0 left-0 w-full h-full" src="${embedUrl}" frameborder="0" title="${video.title}" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>` |
| | : `<p class="text-gray-500 text-sm p-4">${ |
| | video.url === '#' |
| | ? 'אין קישור וידאו' |
| | : 'קישור וידאו לא תקין' |
| | }</p>` |
| | } |
| | </div> |
| | <div class="p-4"> |
| | <h3 class="text-lg font-semibold mb-2">${ |
| | video.title |
| | }</h3> |
| | <p class="text-gray-600 text-sm mb-3 min-h-[40px]">${ |
| | video.description |
| | }</p> |
| | <p class="text-gray-500 text-xs">${formatDate( |
| | video.date |
| | )}</p> |
| | </div>`; |
| | videosContainer.appendChild(videoCard); |
| | }); |
| | } |
| | } |
| | |
| | // --- Helper Functions (Filtering, Stats, Stars, Category, Date - Unchanged) --- |
| | function filterTools() { |
| | if (!toolsData || !Array.isArray(toolsData.tools)) return []; |
| | |
| | let filtered = toolsData.tools; |
| | |
| | // Filter by favorites if selected |
| | if (currentCategory === 'favorites') { |
| | filtered = filtered.filter((_, index) => |
| | favorites.includes(index.toString()) |
| | ); |
| | } else if (currentCategory === 'free') { |
| | filtered = filtered.filter((tool) => tool.free === true); |
| | } else if (currentCategory !== 'all') { |
| | filtered = filtered.filter( |
| | (tool) => tool.category === currentCategory |
| | ); |
| | } |
| | |
| | // Search by name and description |
| | if (currentSearchTerm) { |
| | const searchLower = currentSearchTerm.toLowerCase(); |
| | filtered = filtered.filter((tool) => { |
| | const nameMatch = |
| | tool.name && tool.name.toLowerCase().includes(searchLower); |
| | const descMatch = |
| | tool.description && |
| | tool.description.toLowerCase().includes(searchLower); |
| | return nameMatch || descMatch; // Include both name and description in search |
| | }); |
| | } |
| | |
| | // Check for filteredStatView |
| | if (filteredStatView === 'topRated') { |
| | filtered = filtered.filter((tool) => tool.rating >= 5); |
| | } else if (filteredStatView === 'newTools') { |
| | filtered = filtered.filter((tool) => tool.isNew); |
| | } |
| | |
| | return filtered; |
| | } |
| | |
| | function updateStats() { |
| | /* ... (unchanged) ... */ |
| | const toolsCount = |
| | toolsData && Array.isArray(toolsData.tools) |
| | ? toolsData.tools.length |
| | : 0; |
| | totalToolsElement.textContent = toolsCount; |
| | const topRatedCount = |
| | toolsCount > 0 |
| | ? toolsData.tools.filter((tool) => tool.rating >= 5).length |
| | : 0; |
| | topRatedElement.textContent = topRatedCount; |
| | const newToolsCount = |
| | toolsCount > 0 |
| | ? toolsData.tools.filter((tool) => tool.isNew).length |
| | : 0; |
| | newToolsElement.textContent = newToolsCount; |
| | const categories = |
| | toolsCount > 0 |
| | ? new Set(toolsData.tools.map((tool) => tool.category)) |
| | : new Set(); |
| | totalCategoriesElement.textContent = categories.size; |
| | } |
| | |
| | function renderRatingStars(rating) { |
| | /* ... (unchanged) ... */ |
| | let stars = ''; |
| | const filledStars = Math.max(0, Math.min(5, Math.round(rating))); |
| | for (let i = 1; i <= 5; i++) { |
| | stars += `<i class="${ |
| | i <= filledStars |
| | ? 'fas fa-star text-yellow-400' |
| | : 'far fa-star text-gray-300' |
| | } ml-1"></i>`; |
| | } |
| | return stars; |
| | } |
| | function getCategoryName(category) { |
| | /* ... (unchanged) ... */ |
| | const categories = { |
| | 'ai-agents': 'סוכן AI', |
| | productivity: 'פרודוקטיביות', |
| | writing: 'כתיבה', |
| | design: 'עיצוב', |
| | coding: 'תכנות', |
| | video: 'וידאו', |
| | image: 'תמונה', |
| | education: 'חינוך', |
| | data: 'נתונים', |
| | search: 'חיפוש', |
| | builder: 'בנייה', |
| | 'customer-support': 'תמיכה', |
| | automation: 'אוטומציה', |
| | hosting: 'אחסון', |
| | agents: 'סוכנים', |
| | directory: 'אינדקס', |
| | utility: 'כלי עזר', |
| | platform: 'פלטפורמה', |
| | media: 'מדיה', |
| | presentation: 'מצגות', |
| | audio: 'שמע', |
| | infrastructure: 'תשתיות', |
| | nlp: 'עיבוד שפה', |
| | accessibility: 'נגישות', |
| | general: 'כללי', |
| | }; |
| | return categories[category] || category || 'כללי'; |
| | } |
| | function getCategoryColor(category) { |
| | /* ... (unchanged) ... */ |
| | const colors = { |
| | productivity: 'bg-blue-600', |
| | writing: 'bg-purple-600', |
| | design: 'bg-pink-600', |
| | coding: 'bg-green-600', |
| | video: 'bg-red-600', |
| | image: 'bg-yellow-600', |
| | education: 'bg-indigo-600', |
| | data: 'bg-cyan-600', |
| | search: 'bg-teal-600', |
| | builder: 'bg-orange-600', |
| | 'customer-support': 'bg-lime-600', |
| | automation: 'bg-sky-600', |
| | hosting: 'bg-amber-600', |
| | agents: 'bg-violet-600', |
| | directory: 'bg-fuchsia-600', |
| | utility: 'bg-rose-600', |
| | platform: 'bg-emerald-600', |
| | media: 'bg-stone-600', |
| | presentation: 'bg-red-500', |
| | audio: 'bg-blue-500', |
| | infrastructure: 'bg-gray-700', |
| | nlp: 'bg-purple-500', |
| | accessibility: 'bg-green-500', |
| | general: 'bg-gray-600', |
| | }; |
| | return colors[category] || 'bg-gray-600'; |
| | } |
| | function getCategoryBadgeColor(category) { |
| | /* ... (unchanged) ... */ |
| | const colors = { |
| | productivity: 'bg-blue-100 text-blue-800', |
| | writing: 'bg-purple-100 text-purple-800', |
| | design: 'bg-pink-100 text-pink-800', |
| | coding: 'bg-green-100 text-green-800', |
| | video: 'bg-red-100 text-red-800', |
| | image: 'bg-yellow-100 text-yellow-800', |
| | education: 'bg-indigo-100 text-indigo-800', |
| | data: 'bg-cyan-100 text-cyan-800', |
| | search: 'bg-teal-100 text-teal-800', |
| | builder: 'bg-orange-100 text-orange-800', |
| | 'customer-support': 'bg-lime-100 text-lime-800', |
| | automation: 'bg-sky-100 text-sky-800', |
| | hosting: 'bg-amber-100 text-amber-800', |
| | agents: 'bg-violet-100 text-violet-800', |
| | directory: 'bg-fuchsia-100 text-fuchsia-800', |
| | utility: 'bg-rose-100 text-rose-800', |
| | platform: 'bg-emerald-100 text-emerald-800', |
| | media: 'bg-stone-100 text-stone-800', |
| | presentation: 'bg-red-100 text-red-800', |
| | audio: 'bg-blue-100 text-blue-800', |
| | infrastructure: 'bg-gray-200 text-gray-800', |
| | nlp: 'bg-purple-100 text-purple-800', |
| | accessibility: 'bg-green-100 text-green-800', |
| | general: 'bg-gray-100 text-gray-800', |
| | }; |
| | return colors[category] || 'bg-gray-100 text-gray-800'; |
| | } |
| | function getYouTubeID(url) { |
| | /* ... (unchanged) ... */ |
| | if (!url || url === '#') return ''; |
| | const match = url.match( |
| | /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/|v\/|shorts\/))([^?&\/\s]+)/ |
| | ); |
| | return match ? match[1] : ''; |
| | } |
| | function formatDate(dateString) { |
| | /* ... (unchanged, includes fallback parsing) ... */ |
| | if (!dateString) return 'תאריך לא זמין'; |
| | try { |
| | const date = new Date(dateString); |
| | if (isNaN(date.getTime())) { |
| | console.warn('Could not parse date directly:', dateString); |
| | const parts = dateString.match( |
| | /(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/ |
| | ); |
| | if (parts) { |
| | const isoDate = new Date( |
| | `${parts[3]}-${parts[2].padStart(2, '0')}-${parts[1].padStart( |
| | 2, |
| | '0' |
| | )}T00:00:00Z` |
| | ); |
| | if (!isNaN(isoDate.getTime())) |
| | return isoDate.toLocaleDateString('he-IL', { |
| | year: 'numeric', |
| | month: 'long', |
| | day: 'numeric', |
| | }); |
| | } |
| | return 'תאריך לא תקין'; |
| | } |
| | return date.toLocaleDateString('he-IL', { |
| | year: 'numeric', |
| | month: 'long', |
| | day: 'numeric', |
| | }); |
| | } catch (e) { |
| | console.error('Error formatting date:', dateString, e); |
| | return 'תאריך לא תקין'; |
| | } |
| | } |
| | |
| | // --- Event Listeners Setup --- |
| | function setupEventListeners() { |
| | // Search input |
| | searchInput.addEventListener('input', (e) => { |
| | currentSearchTerm = e.target.value; |
| | localStorage.setItem('searchTerm', currentSearchTerm); // שמור בזיכרון הדפדפן |
| | renderTools(); |
| | }); |
| | |
| | // Filter buttons |
| | filterButtons.forEach((button) => { |
| | button.addEventListener('click', () => { |
| | filterButtons.forEach((btn) => btn.classList.remove('active')); |
| | button.classList.add('active'); |
| | currentCategory = button.dataset.category; |
| | renderTools(); |
| | }); |
| | }); |
| | |
| | // Mobile menu toggle |
| | mobileMenuButton.addEventListener('click', () => { |
| | mobileMenu.classList.toggle('open'); |
| | }); |
| | |
| | // Uncomment if using admin buttons |
| | const editJsonBtn = document.getElementById('editJsonBtn'); |
| | const editJsonBtnMobile = document.getElementById('editJsonBtnMobile'); |
| | document |
| | .getElementById('editJsonBtnFooter') |
| | ?.addEventListener('click', openEditorHandler); |
| | |
| | if (editJsonBtn) |
| | editJsonBtn.addEventListener('click', openEditorHandler); |
| | if (editJsonBtnMobile) |
| | editJsonBtnMobile.addEventListener('click', openEditorHandler); |
| | |
| | closeModalBtn.addEventListener('click', () => |
| | jsonEditorModal.classList.add('hidden') |
| | ); |
| | cancelEditBtn.addEventListener('click', () => |
| | jsonEditorModal.classList.add('hidden') |
| | ); |
| | |
| | // JSON Editor Save Button -> Saves ONLY to localStorage for the current session |
| | saveJsonBtn.addEventListener('click', () => { |
| | try { |
| | const rawData = JSON.parse(jsonEditor.value); |
| | if (!Array.isArray(rawData)) |
| | throw new Error('Data must be an array.'); |
| | if (showToolsBtn.classList.contains('bg-blue-600')) { |
| | // Update in-memory data and sort |
| | toolsData.tools = rawData.map((tool) => ({ |
| | /* add defaults */ ...tool, |
| | rating: tool.rating ?? 0, |
| | isNew: tool.isNew ?? false, |
| | category: tool.category ?? 'general', |
| | icon: tool.icon || 'fas fa-tools', |
| | url: tool.url || '#', |
| | description: tool.description || 'אין תיאור זמין.', |
| | name: tool.name || 'שם לא ידוע', |
| | })); |
| | sortTools(currentSort); |
| | } else { |
| | toolsData.videos = rawData.map((video) => ({ |
| | /* add defaults */ ...video, |
| | url: video.url || '#', |
| | title: video.title || 'כותרת חסרה', |
| | description: video.description || 'אין תיאור זמין.', |
| | date: video.date ?? new Date().toISOString(), |
| | })); |
| | } |
| | // Save the edited data to localStorage (will be lost on next full load) |
| | saveData(); |
| | // Re-render the UI with the edited data for the current session |
| | renderTools(); |
| | renderVideos(); |
| | updateStats(); |
| | jsonEditorModal.classList.add('hidden'); |
| | alert( |
| | 'הנתונים נשמרו זמנית (באחסון המקומי). הם יתעדכנו מחדש מהשרת בטעינה הבאה של הדף.' |
| | ); |
| | } catch (e) { |
| | alert('JSON לא תקין או שגיאה בשמירה:\n' + e.message); |
| | console.error('JSON Save Error:', e); |
| | } |
| | }); |
| | |
| | showToolsBtn.addEventListener('click', () => { |
| | /* ... (unchanged editor tab logic) ... */ |
| | showToolsBtn.classList.add('bg-blue-600', 'text-white'); |
| | showToolsBtn.classList.remove('bg-gray-200', 'text-gray-700'); |
| | showVideosBtn.classList.add('bg-gray-200', 'text-gray-700'); |
| | showVideosBtn.classList.remove('bg-blue-600', 'text-white'); |
| | jsonEditor.value = JSON.stringify(toolsData.tools, null, 2); // Show current in-memory tools data |
| | }); |
| | showVideosBtn.addEventListener('click', () => { |
| | /* ... (unchanged editor tab logic) ... */ |
| | showVideosBtn.classList.add('bg-blue-600', 'text-white'); |
| | showVideosBtn.classList.remove('bg-gray-200', 'text-gray-700'); |
| | showToolsBtn.classList.add('bg-gray-200', 'text-gray-700'); |
| | showToolsBtn.classList.remove('bg-blue-600', 'text-white'); |
| | jsonEditor.value = JSON.stringify(toolsData.videos, null, 2); // Show current in-memory videos data |
| | }); |
| | |
| | // Sorting stat boxes (Unchanged) |
| | if (topRatedStatBox) { |
| | topRatedStatBox.addEventListener('click', () => { |
| | sortTools('rating'); |
| | renderTools(); |
| | scrollToTools(); // גלילה חלקה לתחילת רשימת הכלים |
| | }); |
| | } |
| | |
| | if (newToolsStatBox) { |
| | newToolsStatBox.addEventListener('click', () => { |
| | sortTools('newest'); |
| | renderTools(); |
| | scrollToTools(); // גלילה חלקה לתחילת רשימת הכלים |
| | }); |
| | } |
| | |
| | // Refresh buttons - Now primarily resets filters/sort/search and re-renders current data |
| | // It *could* re-fetch, but init() already does that on load. |
| | // Let's make it just reset the view state. |
| | const resetViewHandler = () => { |
| | console.log('Resetting view state (filters, sort, search)...'); |
| | // Reset filters and search |
| | searchInput.value = ''; |
| | currentSearchTerm = ''; |
| | filterButtons.forEach((btn) => btn.classList.remove('active')); |
| | const allFilterBtn = document.querySelector( |
| | '.filter-btn[data-category="all"]' |
| | ); |
| | if (allFilterBtn) allFilterBtn.classList.add('active'); |
| | currentCategory = 'all'; |
| | |
| | // Reset sort to newest and re-render |
| | sortTools('newest'); |
| | renderTools(); |
| | // No need to re-render videos unless their source changes, which this button doesn't do |
| | // No need to update stats as the underlying data hasn't changed |
| | alert('התצוגה אופסה (פילטרים, מיון וחיפוש נוקו).'); |
| | }; |
| | |
| | refreshBtn.addEventListener('click', resetViewHandler); |
| | refreshBtnMobile.addEventListener('click', resetViewHandler); |
| | } |
| | |
| | // --- Initialize the app --- |
| | document.addEventListener('DOMContentLoaded', init); |
| | |
| | // JSON Editor Modal Logic (If admin buttons are enabled) |
| | const openEditorHandler = () => { |
| | document.getElementById('suggestToolModal').classList.remove('hidden'); |
| | }; |
| | |
| | const closeSuggestModal = () => { |
| | document.getElementById('suggestToolModal').classList.add('hidden'); |
| | }; |
| | |
| | document |
| | .getElementById('suggestToolForm') |
| | .addEventListener('submit', async (e) => { |
| | e.preventDefault(); |
| | const form = e.target; |
| | const rawPhone = form.userPhone.value; |
| | const cleanedPhone = rawPhone.replace(/\D/g, ''); // מסיר כל תו שאינו ספרה |
| | // alert(cleanedPhone) |
| | const data = { |
| | name: form.name.value, |
| | url: form.url.value, |
| | description: form.description.value, |
| | userName: form.userName.value, |
| | userPhone: cleanedPhone, |
| | }; |
| | |
| | try { |
| | await fetch( |
| | 'https://hook.eu2.make.com/lgo6nh36dk804dq1msfmwxo34nzf4o3y', |
| | { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json', |
| | }, |
| | body: JSON.stringify(data), |
| | } |
| | ); |
| | |
| | alert('הכלי נשלח בהצלחה!'); |
| | closeSuggestModal(); |
| | form.reset(); |
| | } catch (err) { |
| | alert('שגיאה בשליחה 😢'); |
| | console.error(err); |
| | } |
| | }); |
| | function openVideoModal(url) { |
| | const modal = document.getElementById('videoModal'); |
| | const iframe = document.getElementById('videoIframe'); |
| | |
| | // Check if the URL is a YouTube link |
| | if (url.includes('youtube.com') || url.includes('youtu.be')) { |
| | iframe.src = url; |
| | } else { |
| | // For direct video URLs, create a video element |
| | iframe.outerHTML = ` |
| | <video id="videoIframe" controls class="w-full h-[60vh]"> |
| | <source src="${url}" type="video/mp4"> |
| | Your browser does not support the video tag. |
| | </video> |
| | `; |
| | } |
| | modal.classList.remove('hidden'); |
| | } |
| | |
| | function closeVideoModal() { |
| | const modal = document.getElementById('videoModal'); |
| | const videoElement = document.getElementById('videoIframe'); |
| | |
| | if (videoElement.tagName === 'VIDEO') { |
| | videoElement.pause(); |
| | videoElement.outerHTML = '<iframe id="videoIframe" class="w-full h-[60vh]" frameborder="0" allowfullscreen></iframe>'; |
| | } else { |
| | videoElement.src = ''; |
| | } |
| | modal.classList.add('hidden'); |
| | } |
| | fetch('tools.json') |
| | .then((res) => res.json()) |
| | .then((tools) => { |
| | const count = tools.length; |
| | document.getElementById( |
| | 'toolsCount' |
| | ).textContent = `${count} כלים זמינים`; |
| | }) |
| | .catch((err) => { |
| | console.error('שגיאה בטעינת כמות הכלים:', err); |
| | document.getElementById('toolsCount').textContent = 'טעינה נכשלה'; |
| | }); |
| | function showNewToolBanner() { |
| | const tools = toolsData.tools; |
| | const lastSeenToolDate = localStorage.getItem('lastSeenToolDate'); |
| | const newTools = tools.filter((tool) => tool.isNew); |
| | if (newTools.length === 0) return; |
| | |
| | const latestTool = newTools.reduce((latest, tool) => { |
| | return new Date(tool.dateAdded) > new Date(latest.dateAdded) |
| | ? tool |
| | : latest; |
| | }, newTools[0]); |
| | |
| | if ( |
| | !lastSeenToolDate || |
| | new Date(latestTool.dateAdded) > new Date(lastSeenToolDate) |
| | ) { |
| | const banner = document.getElementById('newToolBanner'); |
| | const toolNameSpan = document.getElementById('newToolName'); |
| | const closeBtn = document.getElementById('closeBanner'); |
| | |
| | toolNameSpan.textContent = latestTool.name; |
| | banner.classList.remove('hidden'); |
| | const viewNowLink = banner.querySelector('a'); |
| | viewNowLink.addEventListener('click', (e) => { |
| | e.preventDefault(); |
| | filteredStatView = 'newTools'; |
| | currentCategory = 'all'; |
| | currentSearchTerm = ''; |
| | renderTools(); |
| | scrollToTools(); |
| | }); |
| | |
| | closeBtn.addEventListener('click', () => { |
| | banner.classList.add('hidden'); |
| | localStorage.setItem('lastSeenToolDate', latestTool.dateAdded); |
| | }); |
| | } |
| | } |
| | |
| | if ('serviceWorker' in navigator) { |
| | navigator.serviceWorker |
| | .register('/service-worker.js') |
| | .then(() => console.log('Service Worker registered successfully.')) |
| | .catch((err) => |
| | console.error('Service Worker registration failed:', err) |
| | ); |
| | } |
| | |
| | let deferredPrompt; |
| | const installBtn = document.getElementById('installAppBtn'); |
| | const container = document.getElementById('installAppContainer'); |
| | |
| | window.addEventListener('beforeinstallprompt', (e) => { |
| | e.preventDefault(); |
| | deferredPrompt = e; |
| | installBtn.style.display = 'inline-block'; |
| | |
| | installBtn.addEventListener('click', () => { |
| | deferredPrompt.prompt(); |
| | deferredPrompt.userChoice.then((choiceResult) => { |
| | if (choiceResult.outcome === 'accepted') { |
| | console.log('User accepted the A2HS prompt'); |
| | installBtn.style.display = 'none'; |
| | } else { |
| | console.log('User dismissed the A2HS prompt'); |
| | } |
| | deferredPrompt = null; |
| | }); |
| | }); |
| | }); |
| | // פונקציה להצגת כפתורי שיתוף |
| | function renderShareButtons(tool) { |
| | if (!tool.url || tool.url === '#') return ''; |
| | |
| | const encodedToolName = encodeURIComponent(tool.name); |
| | const encodedDesc = encodeURIComponent(`בדקו את הכלי ${tool.name} - ${tool.description}`); |
| | const encodedUrl = encodeURIComponent(tool.url); |
| | |
| | // וודא שיש URL תקין לשיתוף |
| | if (!tool.url || tool.url === '#') { |
| | return ''; // לא מציג כפתורי שיתוף אם אין URL תקין |
| | } |
| | |
| | return ` |
| | <div class="share-buttons mt-4"> |
| | |
| | |
| | <div class="flex justify-center items-center mb-4"> |
| | <div class="flex items-center gap-3"> |
| | |
| | ${ |
| | tool.video |
| | ? ` |
| | <button class="share-btn yt" |
| | data-platform="youtube" |
| | data-video="${tool.video}" |
| | aria-label="צפייה בהדרכה" |
| | title="צפייה בהדרכה"> |
| | <i class="fab fa-youtube"></i> |
| | </button>` |
| | : '' |
| | } |
| | </div> |
| | </div> |
| | |
| | <button class="share-btn fb" data-platform="facebook" data-url="${tool.url}" data-title="${tool.name}" aria-label="שיתוף בפייסבוק" title="לחצו כדי לשתף את הכלי"> |
| | <i class="fab fa-facebook-f"></i> |
| | </button> |
| | <button class="share-btn wa" data-platform="whatsapp" data-url="${tool.url}" data-title="${tool.name}" aria-label="שיתוף ב-WhatsApp" title="לחצו כדי לשתף את הכלי"> |
| | <i class="fab fa-whatsapp"></i> |
| | </button> |
| | <button class="share-btn tw" data-platform="twitter" data-url="${tool.url}" data-title="${tool.name}" aria-label="שיתוף בטוויטר" title="לחצו כדי לשתף את הכלי"> |
| | <i class="fab fa-twitter"></i> |
| | </button> |
| | <button class="share-btn cp" data-platform="copy" data-url="${tool.url}" aria-label="העתקת קישור" title="לחצו כדי לשתף את הכלי"> |
| | <i class="fas fa-link"></i> |
| | </button> |
| | </div>`; |
| | } |
| | // פונקציה לטיפול בלחיצה על כפתור המועדפים |
| | function setupFavorites() { |
| | // טעינת המועדפים מה-localStorage |
| | let favorites = JSON.parse( |
| | localStorage.getItem('aiToolsFavorites') || '[]' |
| | ); |
| | |
| | // הוספת אירוע לחיצה לכפתורי המועדפים |
| | document.querySelectorAll('.favorite-btn').forEach((btn) => { |
| | const toolId = btn.dataset.id; |
| | |
| | // עדכון המראה הראשוני בהתאם למצב |
| | if (favorites.includes(toolId)) { |
| | btn.classList.add('active'); |
| | btn.querySelector('i').classList.remove('far'); |
| | btn.querySelector('i').classList.add('fas'); |
| | } |
| | |
| | btn.addEventListener('click', (e) => { |
| | e.preventDefault(); |
| | e.stopPropagation(); |
| | |
| | const isFavorite = favorites.includes(toolId); |
| | |
| | if (isFavorite) { |
| | // הסרה מהמועדפים |
| | favorites = favorites.filter((id) => id !== toolId); |
| | btn.classList.remove('active'); |
| | btn.querySelector('i').classList.remove('fas'); |
| | btn.querySelector('i').classList.add('far'); |
| | } else { |
| | // הוספה למועדפים |
| | favorites.push(toolId); |
| | btn.classList.add('active'); |
| | btn.querySelector('i').classList.remove('far'); |
| | btn.querySelector('i').classList.add('fas'); |
| | } |
| | |
| | // שמירת המועדפים המעודכנים |
| | localStorage.setItem('aiToolsFavorites', JSON.stringify(favorites)); |
| | }); |
| | }); |
| | } |
| | // עדכון של פונקציית filterTools הקיימת להוספת תמיכה במועדפים |
| | function filterTools() { |
| | if (!toolsData || !Array.isArray(toolsData.tools)) return []; |
| | |
| | // טעינת המועדפים מה-localStorage |
| | const favorites = JSON.parse( |
| | localStorage.getItem('aiToolsFavorites') || '[]' |
| | ); |
| | |
| | let filtered = toolsData.tools; |
| | |
| | // סינון לפי מועדפים (אם מסומן) |
| | if (currentCategory === 'favorites') { |
| | filtered = filtered.filter((_, index) => |
| | favorites.includes(index.toString()) |
| | ); |
| | } |
| | // סינון לפי קטגוריה (כפי שהיה קודם) |
| | else if (currentCategory === 'free') { |
| | filtered = filtered.filter((tool) => tool.free === true); |
| | } else if (currentCategory !== 'all') { |
| | filtered = filtered.filter( |
| | (tool) => tool.category === currentCategory |
| | ); |
| | } |
| | |
| | // סינון לפי חיפוש (נשאר כפי שהיה) |
| | if (currentSearchTerm) { |
| | const searchLower = currentSearchTerm.toLowerCase(); |
| | filtered = filtered.filter((tool) => { |
| | const nameMatch = |
| | tool.name && tool.name.toLowerCase().includes(searchLower); |
| | const descMatch = |
| | tool.description && |
| | tool.description.toLowerCase().includes(searchLower); |
| | const categoryNameMatch = getCategoryName(tool.category) |
| | .toLowerCase() |
| | .includes(searchLower); |
| | return nameMatch || descMatch || categoryNameMatch; |
| | }); |
| | } |
| | |
| | // בדיקה אם יש סינון לפי סטטוס נתונים (filteredStatView) |
| | if (filteredStatView === 'topRated') { |
| | filtered = filtered.filter((tool) => tool.rating >= 5); |
| | } else if (filteredStatView === 'newTools') { |
| | filtered = filtered.filter((tool) => tool.isNew); |
| | } |
| | |
| | return filtered; |
| | } |
| | |
| | // פונקציה לטיפול בכפתורי שיתוף |
| | function setupShareButtons() { |
| | const TOOLBOX_LINK = 'https://bit.ly/a-tools'; |
| | |
| | // Create toast container if it doesn't exist |
| | if (!document.querySelector('.toast-container')) { |
| | const toastContainer = document.createElement('div'); |
| | toastContainer.className = 'toast-container'; |
| | document.body.appendChild(toastContainer); |
| | } |
| | |
| | // Function to show toast |
| | function showToast(message) { |
| | const toastContainer = document.querySelector('.toast-container'); |
| | const toast = document.createElement('div'); |
| | toast.className = 'toast'; |
| | toast.innerHTML = ` |
| | <i class="fas fa-check-circle"></i> |
| | <span>${message}</span> |
| | `; |
| | toastContainer.appendChild(toast); |
| | |
| | // Remove toast after animation |
| | setTimeout(() => { |
| | toast.remove(); |
| | }, 3000); |
| | } |
| | |
| | document.querySelectorAll('.share-btn').forEach((btn) => { |
| | btn.addEventListener('click', async () => { |
| | const platform = btn.dataset.platform; // facebook | twitter | whatsapp | copy |
| | const toolUrl = btn.dataset.url; // קישור ישיר לכלי |
| | const title = btn.dataset.title || 'כלי AI'; // שם הכלי עם ברירת מחדל |
| | |
| | /* === טקסט שיתוף אחד לכל הערוצים === */ |
| | const shareMsg = |
| | `בדקו את הכלי ${title} בלינק: ${toolUrl}\n\n` + |
| | `מארגז הכלים של שגיא בר-און בכתובת: ${TOOLBOX_LINK}`; |
| | |
| | /* ---------- העתקה ללוח ---------- */ |
| | if (platform === 'copy') { |
| | try { |
| | await navigator.clipboard.writeText(shareMsg); |
| | showToast('הטקסט הועתק בהצלחה! 📋'); |
| | } catch { |
| | alert('לא הצלחנו להעתיק – נסה ידנית'); |
| | } |
| | return; |
| | } |
| | if (platform === 'youtube') { |
| | openVideoModal(btn.dataset.video); // כבר קיים בקובץ |
| | return; |
| | } |
| | /* ---------- קידוד וכתובת שיתוף ---------- */ |
| | const encodedMsg = encodeURIComponent(shareMsg); |
| | const encodedUrl = encodeURIComponent(toolUrl); // ל‑Preview |
| | let shareUrl = ''; |
| | |
| | switch (platform) { |
| | case 'facebook': // כרטיס של הכלי, טקסט מלא |
| | shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}"e=${encodedMsg}`; |
| | break; |
| | case 'twitter': // כרטיס + טקסט |
| | shareUrl = `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedMsg}`; |
| | break; |
| | case 'whatsapp': // הכול בטקסט אחד – וואטסאפ מציג שני הלינקים |
| | shareUrl = `https://api.whatsapp.com/send?text=${encodedMsg}`; |
| | break; |
| | default: |
| | shareUrl = encodedUrl; |
| | } |
| | |
| | window.open(shareUrl, '_blank', 'noopener,noreferrer,width=600,height=500'); |
| | }); |
| | }); |
| | } |
| | |
| | |
| | |
| | // פונקציה להוספת כפתור המועדפים לאזור הקטגוריות |
| | function updateCategoryFilters() { |
| | const categoryFilterDiv = document.querySelector('.category-filter'); |
| | |
| | // בודק אם כפתור המועדפים כבר קיים |
| | if (!document.querySelector('[data-category="favorites"]')) { |
| | const favoritesBtn = document.createElement('button'); |
| | favoritesBtn.className = |
| | 'filter-btn px-3 py-1 rounded-full border border-gray-300 text-sm hover:bg-gray-100 transition'; |
| | favoritesBtn.dataset.category = 'favorites'; |
| | favoritesBtn.innerHTML = |
| | '<i class="fas fa-star text-yellow-400 ml-1"></i> מועדפים'; |
| | |
| | // הוספת הכפתור לאחר כפתור "הכל" |
| | const allBtn = document.querySelector('[data-category="all"]'); |
| | if (allBtn && allBtn.parentNode) { |
| | allBtn.parentNode.insertBefore(favoritesBtn, allBtn.nextSibling); |
| | |
| | // הוספת אירוע לחיצה |
| | favoritesBtn.addEventListener('click', () => { |
| | document |
| | .querySelectorAll('.filter-btn') |
| | .forEach((btn) => btn.classList.remove('active')); |
| | favoritesBtn.classList.add('active'); |
| | currentCategory = 'favorites'; |
| | renderTools(); |
| | }); |
| | } |
| | } |
| | } |
| | </script> |
| |
|
| | |
| | |
| | |
| | </body> |
| | </html> |
| |
|