Silver Aluminum
מרכז הניהול הפנימי — כניסה למורשים בלבד
לידים אחרונים
| חומר | מחיר | יחידה | מגמה | הערה |
|---|---|---|---|---|
| 🔩 אלומיניום (LME) | $2,480 | טון | ▲ יציב | 99.7% פריים |
| 🔧 פלדה HRC | $510 | טון | ▼ לחץ סיני | Hot Rolled Coil |
| ⚙️ ברזל גרוטאות | $340 | טון | ◼ יציב | HMS 1&2 |
| 🚢 מכולה סין→ישראל | ~$3,200 | FCL 20ft | ◼ יציב | כולל ביטוח |

מעל 1,700 סוגי פרופילי אלומיניום זמינים במלאי מיידי — מסדרה 7000, 9000, פרגולות, גדרות ועוד.
📦 משלוחים בכל רחבי הארץ · ✅ תו תקן ישראלי
87 דגמי פרגולות זמינים — מסדרה בסיסית ועד פרגולות ביוקלימטיות מתקדמות. מתאים לבתים פרטיים, וילות ומסחרי.
📞 077-444-7375 לפרטים והצעת מחיר
Silver Aluminum — שותף הייצור המוביל WACANG, תו תקן ישראלי, ומחסן ענק באשקלון.
🏭 הזגג 8, א.ת. דרומי אשקלון · שעות: א-ה 8:00–17:00
סטטיסטיקות דף (Demo)
🟢 הזמנות סילבר — ניתוח AI
קבוצת וואטסאפ • Green API • ניתוח בזמן אמת
📅 נתונים מ-07/07/2026 | 28 הודעות מנותחות
40×60
20×70
60×60
40×40
150×50
| לקוח / שולח | טלפון | מס׳ הזמנות | סה״כ יחידות |
|---|---|---|---|
| לחץ "רענן הודעות" לניתוח | |||
| מק״ט | תיאור | קטגוריה | משקל (ק״ג/מ׳) | אורך (ס״מ) |
|---|---|---|---|---|
| טוען נתונים… | ||||
// ===== CONFIG ===== const USERS = { roy: { pass: '123456', name: 'רועי ארביב', role: 'מנהל' } }; const API = 'https://base44.app/api/apps/6a085068cb4e5789e0b23a1a/functions/silverPortalData'; const API_TASKS = 'https://app-e0b23a1a.base44.app/functions/silverPortalTasks'; const API_LOGINLOG = 'https://app-e0b23a1a.base44.app/functions/silverPortalLoginLog'; const API_DEBT = 'https://app-e0b23a1a.base44.app/functions/silverDebtReminders'; const API_BOARD = 'https://app-e0b23a1a.base44.app/functions/silverBoardNotes'; const FINANCE_CODE = '1111'; const PAGE_TITLES = { facebook:'פייסבוק — Silver Aluminum', dashboard:'לוח בקרה', tasks:'משימות', forex:'שערי מט"ח וחומרים', leads:'לידים', links:'קישורים מהירים', loginlog:'לוג כניסות', finance:'הנהלת חשבונות וכספים' };
let currentUser = null; let tasks = []; let financeUnlocked = false; let rates = { usdIls:0, eurIls:0, cnyIls:0, usdCny:0 }; let leadsCache = null;
// ===== LOGIN ===== function doLogin() { var uEl = document.getElementById('inp-user'); var pEl = document.getElementById('inp-pass'); const u = (uEl.value || uEl.getAttribute('value') || window._autoLoginUser || '').trim().toLowerCase(); const p = (pEl.value || pEl.getAttribute('value') || window._autoLoginPass || '').trim(); console.log('[doLogin] user='+u+' pass='+p); const err = document.getElementById('login-err'); if (USERS[u] && USERS[u].pass === p) { currentUser = { id: u, ...USERS[u] }; err.style.display = 'none'; var ls = document.getElementById('login-screen'); var pt = document.getElementById('portal'); ls.style.cssText = 'display:none !important; visibility:hidden !important; opacity:0 !important; pointer-events:none !important; z-index:-1 !important;'; ls.classList.add('hidden'); ls.setAttribute('hidden', ''); ls.style.setProperty('display', 'none', 'important'); pt.style.cssText = 'display:block !important; visibility:visible !important;'; pt.classList.add('visible'); pt.removeAttribute('hidden'); pt.style.setProperty('display', 'block', 'important'); document.getElementById('sb-name').textContent = currentUser.name; document.getElementById('sb-role').textContent = currentUser.role; document.getElementById('top-user').textContent = currentUser.name; document.getElementById('wlc-name').textContent = currentUser.name; startClock(); loadData(); loadTasks(); loadBoardNotes(); loadDashboardUrgent(); fetch(API_LOGINLOG, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'record', username: currentUser.id, user_agent: navigator.userAgent }) }).catch(()=>{}); } else { err.style.display = 'block'; } } window.doLogin = doLogin;
// Failsafe: bind login button multiple times at different stages document.addEventListener('DOMContentLoaded', function() { var btn = document.getElementById('login-submit-btn'); if (btn) { btn.onclick = null; btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); window.doLogin(); }, true); } var u = document.getElementById('inp-user'); var p = document.getElementById('inp-pass'); if(u) u.addEventListener('keydown', function(e){ if(e.key==='Enter') window.doLogin(); }); if(p) p.addEventListener('keydown', function(e){ if(e.key==='Enter') window.doLogin(); }); }); // Use addEventListener for reliable cross-framework compatibility (function() { function bindLogin() { var btn = document.getElementById('login-submit-btn'); var passInput = document.getElementById('inp-pass'); var userInput = document.getElementById('inp-user'); if (btn) { btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); doLogin(); }, true); } if (passInput) passInput.addEventListener('keydown', function(e) { if(e.key==='Enter') doLogin(); }, true); if (userInput) userInput.addEventListener('keydown', function(e) { if(e.key==='Enter') doLogin(); }, true); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', bindLogin); } else { bindLogin(); } })();
function doLogout() { document.getElementById('login-screen').style.display = 'flex'; document.getElementById('portal').style.display = 'none'; document.getElementById('inp-user').value = ''; document.getElementById('inp-pass').value = ''; financeUnlocked = false; const fg = document.getElementById('finance-gate-card'); if (fg) fg.style.display = 'block'; const fc = document.getElementById('finance-content'); if (fc) fc.style.display = 'none'; const fcode = document.getElementById('finance-code'); if (fcode) fcode.value = ''; }
// ===== CLOCK ===== function startClock() { function tick() { const now = new Date(); const t = now.toLocaleTimeString('he-IL', {hour:'2-digit',minute:'2-digit',second:'2-digit'}); const d = now.toLocaleDateString('he-IL', {weekday:'long', year:'numeric', month:'long', day:'numeric'}); document.getElementById('big-time').textContent = t; document.getElementById('big-date').textContent = d; document.getElementById('top-clock').textContent = t + ' | ' + now.toLocaleDateString('he-IL'); document.getElementById('wlc-date').textContent = d; } tick(); setInterval(tick, 1000); }
// ===== NAV ===== function nav(el, id) { document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active')); document.querySelectorAll('.section').forEach(s => s.classList.remove('active')); el.classList.add('active'); document.getElementById('sec-'+id).classList.add('active'); document.getElementById('page-title').textContent = PAGE_TITLES[id] || ''; }
// ===== LOAD DATA (forex + leads) ===== async function loadData() { try { const res = await fetch(API + '?action=all'); const data = await res.json();
// Forex if (data.forex) { rates = data.forex; const fmtRate = (v, d=4) => '₪' + parseFloat(v).toFixed(d); // Dashboard document.getElementById('f-usd').textContent = fmtRate(rates.usdIls, 3); document.getElementById('f-eur').textContent = fmtRate(rates.eurIls, 3); document.getElementById('f-cny').textContent = fmtRate(rates.cnyIls, 4); document.getElementById('f-usdcny').textContent = '¥' + parseFloat(rates.usdCny).toFixed(4); // Forex section document.getElementById('fx-usd').textContent = fmtRate(rates.usdIls, 3); document.getElementById('fx-eur').textContent = fmtRate(rates.eurIls, 3); document.getElementById('fx-cny').textContent = fmtRate(rates.cnyIls, 4); document.getElementById('fx-usdcny').textContent = '¥' + parseFloat(rates.usdCny).toFixed(4); }
// Leads if (data.leads) { leadsCache = data.leads; const L = data.leads; // Dashboard KPIs document.getElementById('kpi-total').textContent = L.total; document.getElementById('kpi-new').textContent = L.new; document.getElementById('kpi-handling').textContent = L.inProgress; document.getElementById('kpi-quoted').textContent = L.quoted; // Leads section stats document.getElementById('ls-total').textContent = L.total; document.getElementById('ls-new').textContent = L.new; document.getElementById('ls-handle').textContent = L.inProgress; document.getElementById('ls-irrel').textContent = L.irrelevant; // Badge document.getElementById('badge-leads').textContent = L.new || 0; // Render tables renderLeadsTable(L.items, 'dash-leads-table', 5); renderLeadsTable(L.items, 'leads-table-wrap', 999); } } catch(e) { console.error('loadData error:', e); ['kpi-total','kpi-new','kpi-handling','kpi-quoted','ls-total','ls-new','ls-handle','ls-irrel'].forEach(id => { const el = document.getElementById(id); if(el) el.textContent = '—'; }); const errMsg = '
'; document.getElementById('dash-leads-table').innerHTML = errMsg; document.getElementById('leads-table-wrap').innerHTML = errMsg; } }
function renderLeadsTable(items, containerId, maxRows) { const el = document.getElementById(containerId); if (!el) return; const rows = (items || []).slice(0, maxRows); if (!rows.length) { el.innerHTML = '
'; return; } el.innerHTML = `
| # | שם | טלפון | מהות פניה | סוג לקוח | אזור | מקור | קבוצה |
|---|---|---|---|---|---|---|---|
| ${i+1} | ${r.name || '—'} | ${r.phone || '—'} | ${r.inquiry || '—'} | ${r.clientType || '—'} | ${r.region || '—'} | ${r.source || '—'} | ${r.group||'—'} |
`; }
// ===== CALCULATOR ===== function calcImport() { const cny = parseFloat(document.getElementById('c-cny').value) || 0; if (!cny || !rates.cnyIls) return; const ils = cny * rates.cnyIls; const usd = cny / rates.usdCny; const withTax = ils * 1.32; document.getElementById('c-usd').value = '$' + usd.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,','); document.getElementById('c-ils').value = '₪' + ils.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,','); document.getElementById('c-tax').value = '₪' + withTax.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g,','); }
// ===== TASKS (server-persisted) ===== async function loadTasks() { try { const resp = await fetch(API_TASKS, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'list' }), cache: 'no-store' }); const data = await resp.json(); tasks = (data.tasks || []).map(t => { let msgs = []; try { msgs = JSON.parse(t.messages || '[]'); } catch (e) { msgs = []; } return { id: t.id, title: t.title, desc: t.notes || '', assign: t.assign || '', visibility: t.visibility || 'public', status: t.status === 'הושלם' ? 'done' : 'open', by: t.created_by_name || '—', date: t.created_date ? new Date(t.created_date).toLocaleDateString('he-IL') : '', msgs }; }); } catch (e) { tasks = []; } renderTasks(); }
async function addTask() { const title = document.getElementById('t-title').value.trim(); if (!title) return alert('נא להזין כותרת'); const notes = document.getElementById('t-desc').value.trim(); const assign = document.getElementById('t-assign').value; const visibility = document.getElementById('t-vis').value; document.getElementById('t-title').value = ''; document.getElementById('t-desc').value = ''; try { await fetch(API_TASKS, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'create', title, notes, assign, visibility, created_by_name: currentUser?.name || '—' }) }); } catch (e) {} await loadTasks(); }
async function toggleDone(id) { const t = tasks.find(t=>t.id===id); if (!t) return; const newStatus = t.status==='done' ? 'פתוח' : 'הושלם'; try { await fetch(API_TASKS, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'update', id, status: newStatus }) }); } catch (e) {} await loadTasks(); } async function deleteTask(id) { if (!confirm('למחוק?')) return; try { await fetch(API_TASKS, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'delete', id }) }); } catch (e) {} await loadTasks(); } function toggleDisc(id) { const el = document.getElementById('disc-'+id); if (el) el.classList.toggle('open'); } async function sendMsg(id) { const inp = document.getElementById('dinp-'+id); const text = inp?.value?.trim(); if (!text) return; inp.value = ''; try { await fetch(API_TASKS, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'add_message', id, user: currentUser?.name||'—', text }) }); } catch (e) {} await loadTasks(); setTimeout(()=>{ document.getElementById('disc-'+id)?.classList.add('open'); }, 10); }
// ===== LOGIN LOG ===== async function loadLoginLog() { const wrap = document.getElementById('loginlog-wrap'); if (!wrap) return; wrap.innerHTML = '
'; try { const resp = await fetch(API_LOGINLOG, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'list' }), cache: 'no-store' }); const data = await resp.json(); const logs = data.logs || []; if (!logs.length) { wrap.innerHTML = '
'; return; } wrap.innerHTML = `
| # | משתמש | תאריך ושעה | כתובת IP |
|---|---|---|---|
| ${i+1} | ${l.username||'—'} | ${dateStr} | ${l.ip_address||'—'} |
`; } catch (e) { wrap.innerHTML = '
'; } }
// ===== FINANCE / DEBT REMINDERS ===== let debtRows = []; function checkFinanceCode() { const val = document.getElementById('finance-code').value.trim(); const err = document.getElementById('finance-err'); if (val === FINANCE_CODE) { financeUnlocked = true; document.getElementById('finance-gate-card').style.display = 'none'; document.getElementById('finance-content').style.display = 'block'; err.style.display = 'none'; loadDebtHistory(); } else { err.style.display = 'block'; } }
function switchDebtTab(tab) { const bulkBtn = document.getElementById('debt-tab-btn-bulk'); const singleBtn = document.getElementById('debt-tab-btn-single'); const bulkPanel = document.getElementById('debt-tab-bulk'); const singlePanel = document.getElementById('debt-tab-single'); if (tab === 'single') { singlePanel.style.display = 'block'; bulkPanel.style.display = 'none'; singleBtn.style.color = '#0d2159'; singleBtn.style.borderBottomColor = '#0d2159'; bulkBtn.style.color = '#aaa'; bulkBtn.style.borderBottomColor = 'transparent'; } else { bulkPanel.style.display = 'block'; singlePanel.style.display = 'none'; bulkBtn.style.color = '#0d2159'; bulkBtn.style.borderBottomColor = '#0d2159'; singleBtn.style.color = '#aaa'; singleBtn.style.borderBottomColor = 'transparent'; } }
function switchFinancePage(page) { const debtBtn = document.getElementById('fin-page-btn-debt'); const billingBtn = document.getElementById('fin-page-btn-billing'); const debtPanel = document.getElementById('fin-page-debt'); const billingPanel = document.getElementById('fin-page-billing'); if (page === 'billing') { billingPanel.style.display = 'block'; debtPanel.style.display = 'none'; billingBtn.style.color = '#0d2159'; billingBtn.style.borderBottomColor = '#0d2159'; debtBtn.style.color = '#aaa'; debtBtn.style.borderBottomColor = 'transparent'; loadBillingHistory(); } else { debtPanel.style.display = 'block'; billingPanel.style.display = 'none'; debtBtn.style.color = '#0d2159'; debtBtn.style.borderBottomColor = '#0d2159'; billingBtn.style.color = '#aaa'; billingBtn.style.borderBottomColor = 'transparent'; } }
// ===== BILLING BREAKDOWN (פירוט חיובים) ===== const API_BILLING = 'https://app-e0b23a1a.base44.app/functions/silverBillingOCR'; let billingPendingFiles = [];
function isSpreadsheetFile(file) { const name = (file.name || '').toLowerCase(); return name.endsWith('.xlsx') || name.endsWith('.xls') || name.endsWith('.csv'); }
function handleBillingFiles(event) { const files = Array.from(event.target.files || []); billingPendingFiles = []; const wrap = document.getElementById('billing-preview-wrap'); wrap.innerHTML = ''; const btn = document.getElementById('billing-process-btn'); document.getElementById('billing-results-wrap').innerHTML = ''; document.getElementById('billing-process-status').innerHTML = ''; if (!files.length) { btn.style.display = 'none'; return; } let loaded = 0; files.forEach((file) => { if (isSpreadsheetFile(file)) { const reader = new FileReader(); reader.onload = (e) => { try { const wb = XLSX.read(e.target.result, { type: 'array' }); const sheet = wb.Sheets[wb.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet, { defval: '' }); billingPendingFiles.push({ filename: file.name, type: 'sheet', rows }); const badge = document.createElement('div'); badge.style.cssText = 'width:90px;height:90px;border:1px solid #ddd;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:11px;text-align:center;padding:4px;background:#f7f8fc;'; badge.innerHTML = '📊
'; wrap.appendChild(badge); } catch (err) { alert('שגיאה בקריאת קובץ האקסל: ' + file.name); } loaded++; if (loaded === files.length) { btn.style.display = 'inline-block'; } }; reader.readAsArrayBuffer(file); } else { const reader = new FileReader(); reader.onload = (e) => { billingPendingFiles.push({ filename: file.name, type: 'image', dataUrl: e.target.result }); const img = document.createElement('img'); img.src = e.target.result; img.style.cssText = 'width:90px;height:90px;object-fit:cover;border:1px solid #ddd;'; wrap.appendChild(img); loaded++; if (loaded === files.length) { btn.style.display = 'inline-block'; } }; reader.readAsDataURL(file); } }); }
async function processBillingFiles() { if (!billingPendingFiles.length) return; const statusEl = document.getElementById('billing-process-status'); const btn = document.getElementById('billing-process-btn'); btn.disabled = true; statusEl.innerHTML = '
'; try { const imagesPayload = billingPendingFiles.filter(f => f.type === 'image').map(f => ({ filename: f.filename, dataUrl: f.dataUrl })); const sheetsPayload = billingPendingFiles.filter(f => f.type === 'sheet').map(f => ({ filename: f.filename, rows: f.rows })); const resp = await fetch(API_BILLING, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'process', images: imagesPayload, sheets: sheetsPayload, uploaded_by: currentUser?.name || '—' }) }); const data = await resp.json(); if (data.error) { statusEl.innerHTML = '
'; btn.disabled = false; return; } renderBillingResults(data.results || []); statusEl.innerHTML = '
'; billingPendingFiles = []; document.getElementById('billing-file-input').value = ''; document.getElementById('billing-preview-wrap').innerHTML = ''; btn.style.display = 'none'; loadBillingHistory(); } catch (e) { statusEl.innerHTML = '
'; } btn.disabled = false; }
function billingFmtNum(n) { if (n === null || n === undefined || n === '') return '—'; const num = parseFloat(n); if (isNaN(num)) return '—'; return '₪' + num.toLocaleString('he-IL', {minimumFractionDigits:2, maximumFractionDigits:2}); }
function billingMerchantCell(it) { const biz = it.merchant_business_name && it.merchant_business_name !== 'null' ? it.merchant_business_name : ''; const desc = it.merchant_description && it.merchant_description !== 'null' ? it.merchant_description : ''; const addr = it.merchant_address && it.merchant_address !== 'null' ? it.merchant_address : ''; const site = it.merchant_website && it.merchant_website !== 'null' ? it.merchant_website : ''; if (!biz && !desc && !addr && !site) return '—'; let html = ''; if (biz) html += '
'; if (desc) html += '
'; if (addr) html += '
'; if (site) { const href = site.startsWith('http') ? site : 'https://'+site; html += '
'; } return html; }
function renderBillingResults(results) { const wrap = document.getElementById('billing-results-wrap'); if (!results.length) { wrap.innerHTML = ''; return; } let grandTotal = 0; const cards = results.map((r) => { const items = Array.isArray(r.items) ? r.items : []; if (typeof r.total === 'number') grandTotal += r.total; const rows = items.length ? items.map(it => '
'+ '
'+ '
'+ '
'+ '
' ).join('') : '
'; return '
'+ '
'+ '
'+ '
'+ '
| תיאור בדוח | 🔎 מי חייב אותך | כמות | מחיר יח\' | סה"כ |
|---|
'+ '
'+ '
'+ '
'+ '
'+ (r.notes ? '
' : '')+ '
'; }).join(''); const summary = results.length > 1 ? '
' : ''; wrap.innerHTML = summary + cards; }
async function loadBillingHistory() { const wrap = document.getElementById('billing-history-wrap'); wrap.innerHTML = '
'; try { const resp = await fetch(API_BILLING, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'list' }), cache: 'no-store' }); const data = await resp.json(); const records = data.records || []; if (!records.length) { wrap.innerHTML = '
'; return; } const rowsHtml = records.map(r => { const dateStr = new Date(r.created_date).toLocaleString('he-IL', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}); return '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'; }).join(''); wrap.innerHTML = '
| ספק | תאריך חשבונית | סכום | קובץ | הועלה |
|---|
'; } catch (e) { wrap.innerHTML = '
'; } }
async function deleteBillingRecord(id) { if (!confirm('למחוק רשומה זו?')) return; try { await fetch(API_BILLING, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'delete', id }) }); loadBillingHistory(); } catch (e) {} }
async function sendSingleDebtReminder() { const name = document.getElementById('single-name').value.trim(); const phone = document.getElementById('single-phone').value.trim(); const amount = document.getElementById('single-amount').value.trim(); const resultEl = document.getElementById('single-send-result'); if (!phone) { alert('נא להזין מספר טלפון'); return; } if (!confirm(`לשלוח התרעת חוב ל-${name || phone}?`)) return; resultEl.innerHTML = '
'; try { const resp = await fetch(API_DEBT, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'send', contacts: [{ name, phone, amount }] }) }); const data = await resp.json(); if (data.sent > 0) { resultEl.innerHTML = '
'; document.getElementById('single-name').value = ''; document.getElementById('single-phone').value = ''; document.getElementById('single-amount').value = ''; loadDebtHistory(); } else { resultEl.innerHTML = '
'; } } catch (e) { resultEl.innerHTML = '
'; } }
function handleDebtFile(evt) { const file = evt.target.files && evt.target.files[0]; const wrap = document.getElementById('debt-preview-wrap'); const btn = document.getElementById('debt-send-btn'); if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); const wb = XLSX.read(data, { type: 'array' }); const sheet = wb.Sheets[wb.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet, { defval: '' }); debtRows = rows.map(r => { const keys = Object.keys(r); const findKey = (opts) => keys.find(k => opts.some(o => k.toString().trim().includes(o))); const nameKey = findKey(['שם','name','Name']); const phoneKey = findKey(['טלפון','phone','Phone','נייד']); const amountKey = findKey(['סכום','amount','Amount','חוב']); return { name: nameKey ? String(r[nameKey]).trim() : '', phone: phoneKey ? String(r[phoneKey]).trim() : '', amount: amountKey ? String(r[amountKey]).trim() : '' }; }).filter(r => r.phone); renderDebtPreview(); } catch (err) { wrap.innerHTML = '
'; } }; reader.readAsArrayBuffer(file); }
function renderDebtPreview() { const wrap = document.getElementById('debt-preview-wrap'); const btn = document.getElementById('debt-send-btn'); if (!debtRows.length) { wrap.innerHTML = '
'; btn.style.display = 'none'; return; } wrap.innerHTML = `
| # | שם | טלפון | סכום |
|---|---|---|---|
| ${i+1} | ${r.name||'—'} | ${r.phone} | ${r.amount||'—'} |
`; btn.style.display = 'inline-block'; }
async function sendDebtReminders() { if (!debtRows.length) return; if (!confirm(`לשלוח התרעת חוב ל-${debtRows.length} אנשי קשר?`)) return; const btn = document.getElementById('debt-send-btn'); const resultEl = document.getElementById('debt-send-result'); btn.disabled = true; btn.textContent = '⏳ שולח...'; resultEl.innerHTML = ''; try { const resp = await fetch(API_DEBT, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'send', contacts: debtRows }) }); const data = await resp.json(); resultEl.innerHTML = `
`; debtRows = []; document.getElementById('debt-file-input').value = ''; document.getElementById('debt-preview-wrap').innerHTML = ''; btn.style.display = 'none'; loadDebtHistory(); } catch (e) { resultEl.innerHTML = '
'; } finally { btn.disabled = false; btn.textContent = '📨 שלח התרעות עכשיו'; } }
async function loadDebtHistory() { const wrap = document.getElementById('debt-history-wrap'); if (!wrap) return; wrap.innerHTML = '
'; try { const resp = await fetch(API_DEBT, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'history' }), cache: 'no-store' }); const data = await resp.json(); const logs = data.logs || []; if (!logs.length) { wrap.innerHTML = '
'; return; } wrap.innerHTML = `
| # | שם | טלפון | סכום | סטטוס | תאריך |
|---|---|---|---|---|---|
| ${i+1} | ${l.name||'—'} | ${l.phone||'—'} | ${l.amount||'—'} | ${l.status||'—'} | ${dateStr} |
`; } catch (e) { wrap.innerHTML = '
'; } }
function renderTasks() { const el = document.getElementById('tasks-list'); if (!el) return; if (!tasks.length) { el.innerHTML = '
'; return; } el.innerHTML = tasks.map(t => `
${t.desc ? `
` : ''}
${m.text}
`).join('')}
`).join(''); document.getElementById('badge-tasks').textContent = tasks.filter(t=>t.status==='open').length; }
renderTasks();
// ===== TEAM BOARD NOTES ===== async function loadBoardNotes() { const list = document.getElementById('board-notes-list'); try { const resp = await fetch(API_BOARD, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'list' }) }); const data = await resp.json(); renderBoardNotes(data.notes || []); } catch (e) { list.innerHTML = '
'; } } function renderBoardNotes(notes) { const list = document.getElementById('board-notes-list'); if (!notes.length) { list.innerHTML = '
'; return; } const sorted = notes.slice().sort((a,b) => (b.pinned?1:0) - (a.pinned?1:0) || new Date(b.created_date) - new Date(a.created_date)); list.innerHTML = sorted.map(n => { const d = new Date(n.created_date); const time = d.toLocaleDateString('he-IL',{day:'2-digit',month:'2-digit'}) + ' ' + d.toLocaleTimeString('he-IL',{hour:'2-digit',minute:'2-digit'}); const pin = n.pinned ? '📌 ' : ''; return '
' + '
' + '' + '
'; }).join(''); } async function postBoardNote() { const input = document.getElementById('board-note-input'); const message = input.value.trim(); if (!message) return; try { await fetch(API_BOARD, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'create', message, author_name: currentUser?.name || '—' }) }); input.value = ''; loadBoardNotes(); } catch (e) { alert('שגיאה בפרסום ההודעה'); } } async function deleteBoardNote(id) { if (!confirm('למחוק את ההודעה?')) return; try { await fetch(API_BOARD, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ action: 'delete', id }) }); loadBoardNotes(); } catch (e) { alert('שגיאה במחיקה'); } }
// ===== HOME DASHBOARD URGENT WIDGET ===== async function loadDashboardUrgent(){ const card=document.getElementById('home-urgent-card'); const list=document.getElementById('home-urgent-list'); try{ const resp=await fetch('https://app-e0b23a1a.base44.app/functions/getWAMessages?t='+Date.now(),{ method:'GET', cache:'no-store' }); const data=await resp.json(); const msgs=[]; for(const m of(Array.isArray(data)?data:[])){ const type=m.typeMessage; let text='',isImage=false; if(type==='textMessage')text=m.textMessage||''; else if(type==='extendedTextMessage')text=(m.extendedTextMessage||{}).text||m.textMessage||''; else if(type==='quotedMessage')text=(m.extendedTextMessage||{}).text||(m.quotedMessage||{}).textMessage||''; else if(type==='imageMessage'){isImage=true;text=m.ocrText||m.caption||'';} const sender=m.senderName||m.senderContactName||(m.type==='outgoing'?'אני':''); const phone=(m.senderId||'').replace('@c.us','')||''; msgs.push({sender,phone,text,ts:m.timestamp,dir:m.type,isImage}); } const urgent=msgs.filter(m=>waClassify(m)==='urgent').sort((a,b)=>b.ts-a.ts).slice(0,5); if(!urgent.length){card.style.display='none';return;} card.style.display='block'; list.innerHTML=urgent.map(m=>{ const phoneHtml=m.phone?' • 📞 '+m.phone:''; const txt=(m.text||'').replace(//g,'>'); return '
'; }).join(''); }catch(e){card.style.display='none';} }
// ===== WHATSAPP AI MODULE ===== let waMessages=[]; const waColors={}; const waPalette=['#0d2159','#e67e22','#27ae60','#9b59b6','#e74c3c','#2980b9','#16a085','#d35400']; let waColorIdx=0; function waGetColor(n){if(!waColors[n])waColors[n]=waPalette[waColorIdx++%waPalette.length];return waColors[n];} function waExtractCustomerLabel(text){ if(!text)return null; if(text.includes('?')||text.includes('؟'))return null; const blacklist=['טופל','הצעת','הוקלד','אין אצלנו','יש במלאי','בדיקה','לא להתייחס','מה זה','תודה','בסדר','אוקיי','קיבלתי','אין לי','מצטער','הזמנה חדשה','לחתוך','תשלום','לאסוף','יבוא']; let lines=text.split(/\n/).map(l=>l.trim()).filter(l=>l.length); let count=1; lines=lines.filter(l=>{ if(/^בס[\"׳']?ד$/.test(l))return false; const cm=l.match(/^(\d+)\s*הזמנות$/); if(cm){count=parseInt(cm[1],10);return false;} return true; }); if(lines.length!==1)return null; const line=lines[0]; if(line.length<2||line.length>30)return null; if(!/^[\u0590-\u05FF0-9\s".׳'\-\.]+$/.test(line))return null; if(/\d{1,4}\s*[xX×]\s*\d{1,4}/.test(line))return null; const low=line.toLowerCase(); if(blacklist.some(w=>low.includes(w)))return null; return {name:line,count}; } function waAttachCustomers(msgs){ let pending=null; msgs.forEach(m=>{ if(m.dir==='outgoing')return; const label=waExtractCustomerLabel(m.text); if(label){pending={name:label.name,sender:m.sender,remaining:label.count};return;} if(pending&&m.sender===pending.sender&&pending.remaining>0){ const lines=waParseOrderLines(m.text); const isOrderish=lines.length>0||waClassify(m)==='order'; if(isOrderish){ m.customer=pending.name; pending.remaining--; if(pending.remaining<=0)pending=null; } }else if(pending){ if(m.sender!==pending.sender){pending=null;} } }); } function waClassify(m){ const t=(m.text||'').toLowerCase(); if(m.isImage)return 'image-msg'; if(t.includes('דחוף')||t.includes('דחופה')||t.includes('דחופים')||t.includes('בהול')||t.includes('אקספרס')||t.includes('למחר'))return 'urgent'; if(t.startsWith('הוקלד'))return 'hoklad'; if(t.includes('הזמנה')||t.includes('יחידות')||t.includes('פרופיל')||t.includes('מטר')||t.includes('צבע')||t.includes("יח'"))return 'order'; return ''; } function waFmtTime(ts){ const d=new Date(ts*1000); return d.toLocaleDateString('he-IL',{day:'2-digit',month:'2-digit'})+' '+d.toLocaleTimeString('he-IL',{hour:'2-digit',minute:'2-digit'}); } async function loadWAData(){ const feed=document.getElementById('wa-feed'); feed.innerHTML='
'; try{ const resp=await fetch('https://app-e0b23a1a.base44.app/functions/getWAMessages?t='+Date.now(),{ method:'GET', cache:'no-store' }); const data=await resp.json(); waMessages=[]; for(const m of(Array.isArray(data)?data:[])){ const type=m.typeMessage; let text='',isImage=false,imgUrl=''; if(type==='textMessage')text=m.textMessage||''; else if(type==='extendedTextMessage')text=(m.extendedTextMessage||{}).text||m.textMessage||''; else if(type==='quotedMessage')text=(m.extendedTextMessage||{}).text||(m.quotedMessage||{}).textMessage||''; else if(type==='imageMessage'){isImage=true;imgUrl=m.downloadUrl||'';text=m.ocrText||m.caption||'';} const sender=m.senderName||m.senderContactName||(m.type==='outgoing'?'אני':''); const phone=(m.senderId||'').replace('@c.us','')||(m.type==='outgoing'?'':''); waMessages.push({sender,phone,text,ts:m.timestamp,dir:m.type,isImage,imgUrl}); } waAttachCustomers(waMessages); waRenderFeed(waMessages);waRenderStats(waMessages);waRenderInsights(waMessages);waRenderSenders(waMessages);waRenderUrgent(waMessages);waRenderCustomerOrders(waMessages); }catch(e){feed.innerHTML='
';} } function waParseOrderLines(text){ if(!text) return []; const lines=String(text).split(/\n/); const results=[]; lines.forEach(line=>{ let m; // Format A: "70*20 6 מטר 12 יח'" — size x size, length in meters, qty const re1=/(\d{1,4})\s*[\*xX×\/]\s*(\d{1,4})\D{0,12}?(\d+(?:\.\d+)?)\s*(?:מטר|מ['\u05f3]\b)[^0-9]{0,15}?(\d+)\s*(?:יח['\u05f3]?|יחיד)/; if((m=line.match(re1))){ results.push({size:m[1]+'×'+m[2], length:m[3]+' מ׳', qty:m[4], raw:line.trim()}); return; } // Format B: "- 22 יח' - 70x50" — qty first, size after (common in OCR bullet lists) const re2=/(\d+)\s*(?:יח['\u05f3]?|יחיד)[^0-9]{0,10}(\d{1,4})\s*[xX×\/]\s*(\d{1,4})/; if((m=line.match(re2))){ results.push({size:m[2]+'×'+m[3], length:'—', qty:m[1], raw:line.trim()}); return; } }); return results; } function waRenderFeed(msgs){ const feed=document.getElementById('wa-feed'); if(!msgs.length){feed.innerHTML='
';return;}
feed.innerHTML=msgs.map(m=>{
const cls=waClassify(m);
const color=waGetColor(m.sender);
const initials=(m.sender||'?').slice(0,2);
let tags='';
if(cls==='order')tags+='📦 הזמנה';
if(cls==='urgent')tags+='🔴 דחוף';
if(cls==='hoklad')tags+='✅ הוקלד';
if(cls==='image-msg')tags+='🖼 תמונה';
if(m.customer)tags+='🏷 '+m.customer+'';
const imgHtml=m.isImage&&m.imgUrl?'':'';
const txt=m.text?'
':''; const phoneHtml=m.phone?'📞 '+m.phone+'':''; const parsedLines=waParseOrderLines(m.text); let orderBreakdown=''; if(parsedLines.length){ const totalQty=parsedLines.reduce((s,l)=>s+(parseInt(l.qty,10)||0),0); orderBreakdown='
'+ '
| מידה (מ״מ) | אורך | כמות |
| '+l.size+' | '+l.length+' | '+l.qty+' יח׳ |
'; } return '
'+(m.sender||'—')+''+phoneHtml+''+waFmtTime(m.ts)+'
'+txt+imgHtml+orderBreakdown+'
'; }).join(''); } function waRenderUrgent(msgs){ const card=document.getElementById('wa-urgent-card'); const list=document.getElementById('wa-urgent-list'); const urgent=msgs.filter(m=>waClassify(m)==='urgent').slice().sort((a,b)=>b.ts-a.ts); if(!urgent.length){card.style.display='none';return;} card.style.display='block'; list.innerHTML=urgent.map(m=>{ const phoneHtml=m.phone?' • 📞 '+m.phone:''; const txt=(m.text||'').replace(//g,'>'); return '
'; }).join(''); } function waRenderStats(msgs){ document.getElementById('wa-total').textContent=msgs.length; document.getElementById('wa-orders').textContent=msgs.filter(m=>waClassify(m)==='order').length; document.getElementById('wa-images').textContent=msgs.filter(m=>m.isImage).length; document.getElementById('wa-hoklad').textContent=msgs.filter(m=>waClassify(m)==='hoklad').length; document.getElementById('wa-urgent').textContent=msgs.filter(m=>waClassify(m)==='urgent').length; } function waTopProfiles(msgs){ const counts={}; msgs.forEach(m=>{ const lines=waParseOrderLines(m.text); lines.forEach(l=>{ const key=(l.length&&l.length!=='—')?(l.size+' | '+l.length):(l.size+' | אורך לא צוין'); counts[key]=(counts[key]||0)+(parseInt(l.qty,10)||1); }); }); return Object.entries(counts).sort((a,b)=>b[1]-a[1]).slice(0,5); }
// ===== CATALOG (ITEMS) TAB ===== const API_CATALOG = 'https://base44.app/api/apps/6a085068cb4e5789e0b23a1a/functions/silverCatalogData'; let catalogCache = []; let catalogCats = [];
async function loadCatalogData() { const listEl = document.getElementById('catalog-body'); const catSel = document.getElementById('catalog-cat-filter'); if (!listEl) return; listEl.innerHTML = '
';
try { const r = await fetch(API_CATALOG, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'list' }) }); const d = await r.json(); catalogCache = d.items || []; catalogCats = Object.keys(d.categories || {}).sort();
// Populate category filter catSel.innerHTML = '' + catalogCats.map(c => ``).join('');
renderCatalog(catalogCache);
// Stats const statsEl = document.getElementById('catalog-stats'); if (statsEl) { const total = d.total || catalogCache.length; const cats = Object.keys(d.categories||{}).length; statsEl.innerHTML = `
`; } } catch(e) { listEl.innerHTML = `
`; } }
function renderCatalog(items) { const listEl = document.getElementById('catalog-body'); if (!items.length) { listEl.innerHTML = '
'; return; } listEl.innerHTML = items.map(it => `
`).join(''); }
function filterCatalog() { const q = (document.getElementById('catalog-search')?.value || '').toLowerCase(); const cat = document.getElementById('catalog-cat-filter')?.value || ''; let filtered = catalogCache; if (cat) filtered = filtered.filter(it => it.category === cat); if (q) filtered = filtered.filter(it => (it.name||'').toLowerCase().includes(q) || (it.sku||'').toLowerCase().includes(q) || (it.category||'').toLowerCase().includes(q) ); renderCatalog(filtered); } // ===== END CATALOG TAB =====
// ===== ORDERS TAB ===== const API_ORDERS = 'https://base44.app/api/apps/6a085068cb4e5789e0b23a1a/functions/silverOrdersData'; let ordersCache = [];
async function loadOrdersData() { const listEl = document.getElementById('orders-list'); const statsEl = document.getElementById('orders-stats'); if (!listEl) return; listEl.innerHTML = '
';
try { const r = await fetch(API_ORDERS, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'list' }) }); const d = await r.json(); ordersCache = d.orders || [];
// Stats bar statsEl.innerHTML = `
${d.urgentOrders ? `
` : ''} `;
renderOrdersList(ordersCache); } catch(e) { listEl.innerHTML = `
`; } }
function renderOrdersList(orders) { const listEl = document.getElementById('orders-list'); if (!orders.length) { listEl.innerHTML = '
'; return; }
const sourceIcon = { text: '💬', image: '🖼️', voice: '🎙️' };
listEl.innerHTML = orders.map(o => { const itemsHtml = (o.items && o.items.length) ? `
| מוצר | מידה | אורך | כמות | מק"ט | משקל |
|---|---|---|---|---|---|
| ${it.product||it.catalog_name||'—'} | ${it.size||'—'} | ${it.length_m ? it.length_m+'מ\'' : '—'} | ${it.qty||'—'} | ${it.sku_silver ? `${it.sku_silver}` : '—'} | ${it.weight_kg ? it.weight_kg.toFixed(1)+'ק"ג' : '—'} |
` : `
`;
const urgentBadge = o.urgent ? '🔴 דחוף' : ''; const srcIcon = sourceIcon[o.source_type] || '📄';
return `
`; }).join(''); }
function filterOrders() { const q = (document.getElementById('orders-search')?.value || '').toLowerCase(); if (!q) { renderOrdersList(ordersCache); return; } const filtered = ordersCache.filter(o => (o.sender_name||'').toLowerCase().includes(q) || (o.sender_phone||'').includes(q) || (o.items||[]).some(it => (it.product||it.catalog_name||'').toLowerCase().includes(q) || (it.sku_silver||'').toLowerCase().includes(q)) ); renderOrdersList(filtered); } // ===== END ORDERS TAB =====
function waRenderCustomerOrders(msgs){ const info={}; msgs.forEach(m=>{ if(!m.sender||m.dir==='outgoing')return; const isOrder=waClassify(m)==='order'; const lines=waParseOrderLines(m.text); if(!isOrder&&!lines.length)return; // prefer the actual end-customer name extracted from the order text (e.g. "רנסנס", "ליבי סוככים"); // fall back to the WhatsApp sender (usually a Silver staff member relaying the order) when no name was tagged const key=m.customer?('c:'+m.customer):('s:'+(m.phone||m.sender)); if(!info[key])info[key]={orders:0,units:0,phone:m.customer?'':(m.phone||''),name:m.customer||m.sender,isCustomer:!!m.customer}; if(isOrder)info[key].orders++; lines.forEach(l=>{info[key].units+=(parseInt(l.qty,10)||0);}); if(!m.customer){ if(!info[key].phone&&m.phone)info[key].phone=m.phone; const curIsNumeric=/^\d+$/.test(info[key].name||''); const newIsNumeric=/^\d+$/.test(m.sender||''); if(curIsNumeric&&!newIsNumeric)info[key].name=m.sender; } }); const sorted=Object.entries(info).sort((a,b)=>b[1].units-a[1].units); const tbody=document.getElementById('wa-customer-orders'); if(!sorted.length){tbody.innerHTML='
';return;} tbody.innerHTML=sorted.map(([key,d])=> '
' ).join(''); } function waRenderInsights(msgs){ const orders=msgs.filter(m=>waClassify(m)==='order').length; const images=msgs.filter(m=>m.isImage).length; const hoklad=msgs.filter(m=>waClassify(m)==='hoklad').length; const urgent=msgs.filter(m=>waClassify(m)==='urgent').length; const pct=msgs.length?Math.round(orders/msgs.length*100):0; const top=waTopProfiles(msgs); const topHtml=top.length?top.map(([size,c])=>''+size+' ('+c+')').join(''):'אין עדיין מספיק נתונים'; document.getElementById('wa-insights').innerHTML= '
'+ '
'+ '
'+ '
'+ '
'; } function waRenderSenders(msgs){ const info={}; msgs.forEach(m=>{ if(!m.sender||m.dir==='outgoing')return; if(!info[m.sender])info[m.sender]={total:0,images:0,texts:0,phone:m.phone||''}; info[m.sender].total++; if(m.isImage)info[m.sender].images++;else info[m.sender].texts++; if(!info[m.sender].phone&&m.phone)info[m.sender].phone=m.phone; }); const sorted=Object.entries(info).sort((a,b)=>b[1].total-a[1].total).slice(0,6); const max=sorted[0]?.[1].total||1; document.getElementById('wa-senders').innerHTML=sorted.map(([name,d])=>{ const color=waGetColor(name); const pct=Math.round(d.total/max*100); const phoneHtml=d.phone?' 📞 '+d.phone+'':''; return '
'; }).join(''); } function filterWAMsgs(){ const search=document.getElementById('wa-search').value.toLowerCase(); const filter=document.getElementById('wa-filter').value; let filtered=waMessages; if(filter!=='all')filtered=filtered.filter(m=>filter==='image'?m.isImage:waClassify(m)===filter); if(search)filtered=filtered.filter(m=>(m.text||'').toLowerCase().includes(search)||(m.sender||'').toLowerCase().includes(search)); waRenderFeed(filtered); }