Skip to content

https://github.com/desi-programmer/money_manager/issues/2 #2

@lironb12

Description

@lironb12
<title>ניהול כספים אישיים</title> <script src="https://cdn.tailwindcss.com"></script> <style> @import url('https://fonts.googleapis.com/css2?family=Heebo:wght@300;400;500;600;700&display=swap'); body { font-family: 'Heebo', sans-serif; } .receipt-preview { max-width: 200px; max-height: 200px; object-fit: cover; } </style>

💰 ניהול כספים אישיים

        <!-- Monthly Summary -->
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
            <div class="bg-gradient-to-r from-green-400 to-green-600 rounded-xl p-4 text-white">
                <h3 class="text-sm font-medium opacity-90">מגבלה חודשית</h3>
                <p class="text-2xl font-bold" id="monthlyLimit">₪5,000</p>
            </div>
            <div class="bg-gradient-to-r from-blue-400 to-blue-600 rounded-xl p-4 text-white">
                <h3 class="text-sm font-medium opacity-90">הוצאות החודש</h3>
                <p class="text-2xl font-bold" id="monthlySpent">₪0</p>
            </div>
            <div class="bg-gradient-to-r from-purple-400 to-purple-600 rounded-xl p-4 text-white">
                <h3 class="text-sm font-medium opacity-90">נותר</h3>
                <p class="text-2xl font-bold" id="remaining">₪5,000</p>
            </div>
        </div>

        <!-- Progress Bar -->
        <div class="mb-4">
            <div class="flex justify-between text-sm text-gray-600 mb-2">
                <span>התקדמות חודשית</span>
                <span id="progressPercent">0%</span>
            </div>
            <div class="w-full bg-gray-200 rounded-full h-3">
                <div class="bg-gradient-to-r from-blue-500 to-purple-500 h-3 rounded-full transition-all duration-300" id="progressBar" style="width: 0%"></div>
            </div>
        </div>

        <!-- Warning Alert -->
        <div id="warningAlert" class="hidden bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-4">
            <strong>⚠️ אזהרה!</strong> <span id="warningText"></span>
        </div>
    </div>

    <!-- Settings Panel -->
    <div class="bg-white rounded-2xl shadow-lg p-6 mb-6">
        <h2 class="text-xl font-semibold text-gray-800 mb-4">⚙️ הגדרות</h2>
        <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">מגבלה חודשית (₪)</label>
                <input type="number" id="limitInput" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" value="5000">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">אחוז אזהרה (%)</label>
                <input type="number" id="warningPercent" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" value="80" min="1" max="100">
            </div>
        </div>
        <button onclick="updateSettings()" class="mt-4 bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-medium transition-colors">
            עדכן הגדרות
        </button>
    </div>

    <!-- Add Expense -->
    <div class="bg-white rounded-2xl shadow-lg p-6 mb-6">
        <h2 class="text-xl font-semibold text-gray-800 mb-4">➕ הוסף הוצאה</h2>
        <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">תיאור ההוצאה</label>
                <input type="text" id="expenseDescription" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="למשל: קניות בסופר">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">סכום (₪)</label>
                <input type="number" id="expenseAmount" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="0" step="0.01">
            </div>
        </div>
        <div class="mb-4">
            <label class="block text-sm font-medium text-gray-700 mb-2">צלם חשבונית (אופציונלי)</label>
            <input type="file" id="receiptFile" accept="image/*" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
            <div id="receiptPreview" class="mt-2 hidden">
                <img id="previewImage" class="receipt-preview rounded-lg border">
            </div>
        </div>
        <button onclick="addExpense()" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg font-medium transition-colors">
            הוסף הוצאה
        </button>
    </div>

    <!-- Expenses History -->
    <div class="bg-white rounded-2xl shadow-lg p-6">
        <h2 class="text-xl font-semibold text-gray-800 mb-4">📊 היסטוריית הוצאות</h2>
        
        <!-- Month Filter -->
        <div class="mb-4">
            <select id="monthFilter" onchange="filterExpenses()" class="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
                <option value="current">החודש הנוכחי</option>
                <option value="all">כל ההיסטוריה</option>
            </select>
        </div>

        <div id="expensesList" class="space-y-3">
            <div class="text-center text-gray-500 py-8">
                עדיין לא הוספת הוצאות החודש 📝
            </div>
        </div>
    </div>
</div>

<script>
    // Data storage
    let expenses = JSON.parse(localStorage.getItem('expenses') || '[]');
    let monthlyLimit = parseFloat(localStorage.getItem('monthlyLimit') || '5000');
    let warningThreshold = parseFloat(localStorage.getItem('warningThreshold') || '80');

    // Initialize
    document.getElementById('limitInput').value = monthlyLimit;
    document.getElementById('warningPercent').value = warningThreshold;
    updateDisplay();
    displayExpenses();

    // Receipt preview
    document.getElementById('receiptFile').addEventListener('change', function(e) {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(e) {
                document.getElementById('previewImage').src = e.target.result;
                document.getElementById('receiptPreview').classList.remove('hidden');
            };
            reader.readAsDataURL(file);
        }
    });

    function updateSettings() {
        monthlyLimit = parseFloat(document.getElementById('limitInput').value) || 5000;
        warningThreshold = parseFloat(document.getElementById('warningPercent').value) || 80;
        
        localStorage.setItem('monthlyLimit', monthlyLimit.toString());
        localStorage.setItem('warningThreshold', warningThreshold.toString());
        
        updateDisplay();
        
        // Success feedback
        const button = event.target;
        const originalText = button.textContent;
        button.textContent = '✅ נשמר!';
        button.classList.add('bg-green-600');
        setTimeout(() => {
            button.textContent = originalText;
            button.classList.remove('bg-green-600');
        }, 2000);
    }

    function addExpense() {
        const description = document.getElementById('expenseDescription').value.trim();
        const amount = parseFloat(document.getElementById('expenseAmount').value);
        const receiptFile = document.getElementById('receiptFile').files[0];

        if (!description || !amount || amount <= 0) {
            alert('אנא מלא את כל השדות הנדרשים');
            return;
        }

        const expense = {
            id: Date.now(),
            description,
            amount,
            date: new Date().toISOString(),
            month: new Date().getMonth(),
            year: new Date().getFullYear(),
            receipt: null
        };

        // Handle receipt image
        if (receiptFile) {
            const reader = new FileReader();
            reader.onload = function(e) {
                expense.receipt = e.target.result;
                saveExpense(expense);
            };
            reader.readAsDataURL(receiptFile);
        } else {
            saveExpense(expense);
        }
    }

    function saveExpense(expense) {
        expenses.push(expense);
        localStorage.setItem('expenses', JSON.stringify(expenses));
        
        // Clear form
        document.getElementById('expenseDescription').value = '';
        document.getElementById('expenseAmount').value = '';
        document.getElementById('receiptFile').value = '';
        document.getElementById('receiptPreview').classList.add('hidden');
        
        updateDisplay();
        displayExpenses();
    }

    function deleteExpense(id) {
        if (confirm('האם אתה בטוח שברצונך למחוק הוצאה זו?')) {
            expenses = expenses.filter(expense => expense.id !== id);
            localStorage.setItem('expenses', JSON.stringify(expenses));
            updateDisplay();
            displayExpenses();
        }
    }

    function updateDisplay() {
        const currentMonth = new Date().getMonth();
        const currentYear = new Date().getFullYear();
        
        const monthlyExpenses = expenses.filter(expense => 
            expense.month === currentMonth && expense.year === currentYear
        );
        
        const totalSpent = monthlyExpenses.reduce((sum, expense) => sum + expense.amount, 0);
        const remaining = monthlyLimit - totalSpent;
        const percentage = (totalSpent / monthlyLimit) * 100;

        document.getElementById('monthlyLimit').textContent = `₪${monthlyLimit.toLocaleString()}`;
        document.getElementById('monthlySpent').textContent = `₪${totalSpent.toLocaleString()}`;
        document.getElementById('remaining').textContent = `₪${remaining.toLocaleString()}`;
        document.getElementById('progressPercent').textContent = `${Math.round(percentage)}%`;
        document.getElementById('progressBar').style.width = `${Math.min(percentage, 100)}%`;

        // Update progress bar color based on percentage
        const progressBar = document.getElementById('progressBar');
        if (percentage >= 100) {
            progressBar.className = 'bg-gradient-to-r from-red-500 to-red-600 h-3 rounded-full transition-all duration-300';
        } else if (percentage >= warningThreshold) {
            progressBar.className = 'bg-gradient-to-r from-yellow-500 to-orange-500 h-3 rounded-full transition-all duration-300';
        } else {
            progressBar.className = 'bg-gradient-to-r from-blue-500 to-purple-500 h-3 rounded-full transition-all duration-300';
        }

        // Show warnings
        const warningAlert = document.getElementById('warningAlert');
        const warningText = document.getElementById('warningText');
        
        if (percentage >= 100) {
            warningAlert.classList.remove('hidden');
            warningAlert.className = 'bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-4';
            warningText.textContent = `חרגת מהמגבלה החודשית ב-₪${(totalSpent - monthlyLimit).toLocaleString()}!`;
        } else if (percentage >= warningThreshold) {
            warningAlert.classList.remove('hidden');
            warningAlert.className = 'bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded-lg mb-4';
            warningText.textContent = `אתה מתקרב למגבלה החודשית! נותרו ₪${remaining.toLocaleString()}`;
        } else {
            warningAlert.classList.add('hidden');
        }
    }

    function displayExpenses() {
        const filter = document.getElementById('monthFilter').value;
        const currentMonth = new Date().getMonth();
        const currentYear = new Date().getFullYear();
        
        let filteredExpenses = expenses;
        if (filter === 'current') {
            filteredExpenses = expenses.filter(expense => 
                expense.month === currentMonth && expense.year === currentYear
            );
        }

        const expensesList = document.getElementById('expensesList');
        
        if (filteredExpenses.length === 0) {
            expensesList.innerHTML = `
                <div class="text-center text-gray-500 py-8">
                    ${filter === 'current' ? 'עדיין לא הוספת הוצאות החודש 📝' : 'אין הוצאות בהיסטוריה 📝'}
                </div>
            `;
            return;
        }

        // Sort by date (newest first)
        filteredExpenses.sort((a, b) => new Date(b.date) - new Date(a.date));

        expensesList.innerHTML = filteredExpenses.map(expense => {
            const date = new Date(expense.date);
            const formattedDate = date.toLocaleDateString('he-IL');
            const formattedTime = date.toLocaleTimeString('he-IL', { hour: '2-digit', minute: '2-digit' });
            
            return `
                <div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
                    <div class="flex justify-between items-start">
                        <div class="flex-1">
                            <h3 class="font-medium text-gray-800">${expense.description}</h3>
                            <p class="text-sm text-gray-600">${formattedDate} בשעה ${formattedTime}</p>
                            ${expense.receipt ? '<p class="text-xs text-blue-600 mt-1">📎 יש חשבונית מצורפת</p>' : ''}
                        </div>
                        <div class="text-left mr-4">
                            <p class="text-lg font-semibold text-red-600">₪${expense.amount.toLocaleString()}</p>
                            <button onclick="deleteExpense(${expense.id})" class="text-red-500 hover:text-red-700 text-sm mt-1">
                                🗑️ מחק
                            </button>
                        </div>
                    </div>
                    ${expense.receipt ? `
                        <div class="mt-3">
                            <img src="${expense.receipt}" class="receipt-preview rounded-lg border cursor-pointer" onclick="showReceiptModal('${expense.receipt}')">
                        </div>
                    ` : ''}
                </div>
            `;
        }).join('');
    }

    function filterExpenses() {
        displayExpenses();
    }

    function showReceiptModal(imageSrc) {
        const modal = document.createElement('div');
        modal.className = 'fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-4';
        modal.onclick = () => modal.remove();
        
        modal.innerHTML = `
            <div class="max-w-4xl max-h-full">
                <img src="${imageSrc}" class="max-w-full max-h-full object-contain rounded-lg">
                <button class="absolute top-4 right-4 text-white text-2xl hover:text-gray-300" onclick="this.parentElement.parentElement.remove()">✕</button>
            </div>
        `;
        
        document.body.appendChild(modal);
    }

    // Initialize display
    updateDisplay();
    displayExpenses();
</script>
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'96cb0f5673efae7c',t:'MTc1NDc4MjQyMS4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions