| |
| class UnifiedWalletApp { |
| constructor() { |
| this.currentUser = null; |
| this.currentScreen = 'login'; |
| this.wallets = []; |
| this.transactions = []; |
| this.isBalanceHidden = false; |
|
|
| |
| this.authManager = new AuthenticationManager(); |
| this.walletManager = new WalletManager(); |
| this.notificationManager = new NotificationManager(); |
|
|
| this.init(); |
| } |
|
|
| |
| init() { |
| this.setupEventListeners(); |
| this.loadUserData(); |
| this.showLoadingScreen(); |
| |
| |
| setTimeout(() => { |
| this.hideLoadingScreen(); |
| this.checkAuthStatus(); |
| }, 2000); |
| } |
|
|
| |
| setupEventListeners() { |
| |
| document.getElementById('login-btn').addEventListener('click', () => this.handleLogin()); |
| document.getElementById('biometric-login').addEventListener('click', () => this.handleBiometricLogin()); |
| document.getElementById('register-link').addEventListener('click', (e) => { |
| e.preventDefault(); |
| this.showRegisterModal(); |
| }); |
|
|
| |
| document.getElementById('refresh-balance').addEventListener('click', () => this.refreshBalances()); |
| document.getElementById('hide-balance').addEventListener('click', () => this.toggleBalanceVisibility()); |
| |
| |
| document.getElementById('transfer-btn').addEventListener('click', () => this.showTransferScreen()); |
| document.getElementById('pay-bills-btn').addEventListener('click', () => this.showBillsScreen()); |
| document.getElementById('recharge-btn').addEventListener('click', () => this.showRechargeScreen()); |
| document.getElementById('qr-scan-btn').addEventListener('click', () => this.startQRScan()); |
|
|
| |
| document.querySelectorAll('.nav-item').forEach(item => { |
| item.addEventListener('click', () => { |
| const screen = item.dataset.screen; |
| this.switchScreen(screen); |
| }); |
| }); |
|
|
| |
| document.getElementById('settings-btn').addEventListener('click', () => this.showSettings()); |
| document.getElementById('notifications-btn').addEventListener('click', () => this.showNotifications()); |
|
|
| |
| document.getElementById('manage-wallets').addEventListener('click', () => this.showWalletManagement()); |
| document.getElementById('view-all-transactions').addEventListener('click', () => this.showAllTransactions()); |
|
|
| |
| document.getElementById('transfer-back').addEventListener('click', () => this.switchScreen('main')); |
|
|
| |
| this.setupTransferScreen(); |
|
|
| |
| document.getElementById('phone-number').addEventListener('input', this.formatPhoneNumber); |
| document.getElementById('pin-code').addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') this.handleLogin(); |
| }); |
| } |
|
|
| |
| showLoadingScreen() { |
| document.getElementById('loading-screen').style.display = 'flex'; |
| } |
|
|
| |
| hideLoadingScreen() { |
| document.getElementById('loading-screen').style.display = 'none'; |
| } |
|
|
| |
| checkAuthStatus() { |
| const savedUser = localStorage.getItem('unifiedWallet_user'); |
| if (savedUser) { |
| this.currentUser = JSON.parse(savedUser); |
| this.switchScreen('main'); |
| this.loadUserWallets(); |
| } else { |
| this.switchScreen('login'); |
| } |
| } |
|
|
| |
| async handleLogin() { |
| const phoneNumber = document.getElementById('phone-number').value; |
| const pinCode = document.getElementById('pin-code').value; |
|
|
| if (!phoneNumber || !pinCode) { |
| this.showAlert('يرجى إدخال رقم الهاتف ورمز PIN', 'error'); |
| return; |
| } |
|
|
| this.showLoading('جاري تسجيل الدخول...'); |
|
|
| try { |
| const user = await this.authManager.loginWithPin(phoneNumber, pinCode); |
| this.currentUser = user; |
|
|
| this.hideLoading(); |
| this.showAlert('تم تسجيل الدخول بنجاح', 'success'); |
| this.switchScreen('main'); |
| await this.loadUserWallets(); |
|
|
| } catch (error) { |
| this.hideLoading(); |
| this.showAlert(error.message, 'error'); |
| } |
| } |
|
|
| |
| async handleBiometricLogin() { |
| this.showLoading('جاري التحقق من البصمة...'); |
|
|
| try { |
| const user = await this.authManager.loginWithBiometric(); |
| this.currentUser = user; |
|
|
| this.hideLoading(); |
| this.showAlert('تم تسجيل الدخول بالبصمة بنجاح', 'success'); |
| this.switchScreen('main'); |
| await this.loadUserWallets(); |
|
|
| } catch (error) { |
| this.hideLoading(); |
| this.showAlert(error.message, 'error'); |
| } |
| } |
|
|
| |
| async loadUserWallets() { |
| this.showLoading('جاري تحميل المحافظ...'); |
|
|
| try { |
| |
| this.wallets = this.walletManager.getUserWallets(); |
|
|
| |
| if (this.wallets.length === 0) { |
| await this.createDemoWallets(); |
| } |
|
|
| |
| await this.walletManager.updateAllBalances(); |
| this.wallets = this.walletManager.getUserWallets(); |
|
|
| this.loadRecentTransactions(); |
| this.updateUI(); |
| this.hideLoading(); |
|
|
| } catch (error) { |
| this.hideLoading(); |
| this.showAlert('فشل في تحميل المحافظ', 'error'); |
| } |
| } |
|
|
| |
| async createDemoWallets() { |
| const demoWallets = [ |
| { id: 'jawali', accountNumber: '777123456', pin: '1234' }, |
| { id: 'onecash', accountNumber: '777234567', pin: '1234' }, |
| { id: 'cash', accountNumber: '777345678', pin: '1234' }, |
| { id: 'jaib', accountNumber: '777456789', pin: '1234' }, |
| { id: 'mfloos', accountNumber: '777567890', pin: '1234' }, |
| { id: 'mobilemoney', accountNumber: '777678901', pin: '1234' } |
| ]; |
|
|
| for (const wallet of demoWallets) { |
| try { |
| await this.walletManager.addWallet(wallet.id, wallet.accountNumber, wallet.pin); |
| } catch (error) { |
| console.warn(`فشل في إضافة محفظة ${wallet.id}:`, error.message); |
| } |
| } |
| } |
|
|
| |
| loadRecentTransactions() { |
| this.transactions = [ |
| { |
| id: 1, |
| type: 'send', |
| title: 'تحويل إلى أحمد محمد', |
| wallet: 'جوالي', |
| amount: -500, |
| time: '10:30 ص', |
| date: new Date().toISOString() |
| }, |
| { |
| id: 2, |
| type: 'receive', |
| title: 'استلام من سارة أحمد', |
| wallet: 'ONE Cash', |
| amount: 1200, |
| time: '09:15 ص', |
| date: new Date().toISOString() |
| }, |
| { |
| id: 3, |
| type: 'bill', |
| title: 'فاتورة كهرباء', |
| wallet: 'Cash', |
| amount: -350, |
| time: 'أمس', |
| date: new Date(Date.now() - 86400000).toISOString() |
| }, |
| { |
| id: 4, |
| type: 'send', |
| title: 'شحن رصيد', |
| wallet: 'Jaib', |
| amount: -100, |
| time: 'أمس', |
| date: new Date(Date.now() - 86400000).toISOString() |
| } |
| ]; |
| } |
|
|
| |
| updateUI() { |
| this.updateUserInfo(); |
| this.updateTotalBalance(); |
| this.renderWallets(); |
| this.renderTransactions(); |
| } |
|
|
| |
| updateUserInfo() { |
| if (this.currentUser) { |
| document.getElementById('user-name').textContent = `مرحباً ${this.currentUser.name}`; |
| document.getElementById('user-phone').textContent = this.currentUser.phone; |
| } |
| } |
|
|
| |
| updateTotalBalance() { |
| const total = this.wallets.reduce((sum, wallet) => sum + wallet.balance, 0); |
| const balanceElement = document.getElementById('total-balance'); |
| |
| if (this.isBalanceHidden) { |
| balanceElement.textContent = '••••• ر.ي'; |
| } else { |
| balanceElement.textContent = `${total.toLocaleString()} ر.ي`; |
| } |
| } |
|
|
| |
| renderWallets() { |
| const walletsList = document.getElementById('wallets-list'); |
| walletsList.innerHTML = ''; |
|
|
| this.wallets.forEach(wallet => { |
| const walletCard = this.createWalletCard(wallet); |
| walletsList.appendChild(walletCard); |
| }); |
| } |
|
|
| |
| createWalletCard(wallet) { |
| const card = document.createElement('div'); |
| card.className = 'wallet-card'; |
| card.onclick = () => this.openWalletDetails(wallet); |
|
|
| const balanceDisplay = this.isBalanceHidden ? |
| '•••••' : wallet.balance.toLocaleString(); |
|
|
| card.innerHTML = ` |
| <div class="wallet-info"> |
| <div class="wallet-icon"> |
| <img src="${wallet.icon}" alt="${wallet.name}" onerror="this.style.display='none'"> |
| </div> |
| <div class="wallet-details"> |
| <h4>${wallet.name}</h4> |
| <p>${wallet.provider}</p> |
| </div> |
| </div> |
| <div class="wallet-balance"> |
| <div class="amount">${balanceDisplay}</div> |
| <div class="currency">ر.ي</div> |
| </div> |
| `; |
|
|
| return card; |
| } |
|
|
| |
| renderTransactions() { |
| const transactionsList = document.getElementById('transactions-list'); |
| transactionsList.innerHTML = ''; |
|
|
| this.transactions.slice(0, 5).forEach(transaction => { |
| const transactionItem = this.createTransactionItem(transaction); |
| transactionsList.appendChild(transactionItem); |
| }); |
| } |
|
|
| |
| createTransactionItem(transaction) { |
| const item = document.createElement('div'); |
| item.className = 'transaction-item'; |
|
|
| const iconClass = transaction.type === 'send' ? 'fas fa-arrow-up' : |
| transaction.type === 'receive' ? 'fas fa-arrow-down' : |
| 'fas fa-file-invoice-dollar'; |
|
|
| const amountClass = transaction.amount > 0 ? 'positive' : 'negative'; |
| const amountSign = transaction.amount > 0 ? '+' : ''; |
|
|
| item.innerHTML = ` |
| <div class="transaction-info"> |
| <div class="transaction-icon ${transaction.type}"> |
| <i class="${iconClass}"></i> |
| </div> |
| <div class="transaction-details"> |
| <h5>${transaction.title}</h5> |
| <p>${transaction.wallet}</p> |
| </div> |
| </div> |
| <div class="transaction-amount ${amountClass}"> |
| <div class="amount">${amountSign}${Math.abs(transaction.amount).toLocaleString()} ر.ي</div> |
| <div class="time">${transaction.time}</div> |
| </div> |
| `; |
|
|
| return item; |
| } |
|
|
| |
| simulateAPICall(delay = 1000) { |
| return new Promise((resolve) => { |
| setTimeout(resolve, delay); |
| }); |
| } |
|
|
| |
| showAlert(message, type = 'info') { |
| this.notificationManager.showToast(message, type); |
| } |
|
|
| |
| showLoading(message = 'جاري التحميل...') { |
| this.loadingModal = this.notificationManager.showLoading(message); |
| } |
|
|
| |
| hideLoading() { |
| this.notificationManager.hideLoading(); |
| } |
|
|
| |
| switchScreen(screenName) { |
| document.querySelectorAll('.screen').forEach(screen => { |
| screen.classList.remove('active'); |
| }); |
| |
| document.getElementById(`${screenName}-screen`).classList.add('active'); |
| |
| |
| document.querySelectorAll('.nav-item').forEach(item => { |
| item.classList.remove('active'); |
| }); |
| |
| const activeNavItem = document.querySelector(`[data-screen="${screenName}"]`); |
| if (activeNavItem) { |
| activeNavItem.classList.add('active'); |
| } |
| |
| this.currentScreen = screenName; |
| } |
|
|
| |
| formatPhoneNumber(e) { |
| let value = e.target.value.replace(/\D/g, ''); |
| if (value.length > 9) { |
| value = value.slice(0, 9); |
| } |
| e.target.value = value; |
| } |
|
|
| |
| async refreshBalances() { |
| this.showLoading('جاري تحديث الأرصدة...'); |
| await this.simulateAPICall(1500); |
| this.updateTotalBalance(); |
| this.renderWallets(); |
| this.hideLoading(); |
| this.showAlert('تم تحديث الأرصدة بنجاح', 'success'); |
| } |
|
|
| |
| toggleBalanceVisibility() { |
| this.isBalanceHidden = !this.isBalanceHidden; |
| const hideBtn = document.getElementById('hide-balance'); |
| const icon = hideBtn.querySelector('i'); |
| |
| if (this.isBalanceHidden) { |
| icon.className = 'fas fa-eye'; |
| hideBtn.innerHTML = '<i class="fas fa-eye"></i> إظهار'; |
| } else { |
| icon.className = 'fas fa-eye-slash'; |
| hideBtn.innerHTML = '<i class="fas fa-eye-slash"></i> إخفاء'; |
| } |
| |
| this.updateTotalBalance(); |
| this.renderWallets(); |
| } |
|
|
| |
| loadUserData() { |
| |
| const savedSettings = localStorage.getItem('unifiedWallet_settings'); |
| if (savedSettings) { |
| const settings = JSON.parse(savedSettings); |
| this.isBalanceHidden = settings.hideBalance || false; |
| } |
| } |
|
|
| |
| saveUserData() { |
| const settings = { |
| hideBalance: this.isBalanceHidden |
| }; |
| localStorage.setItem('unifiedWallet_settings', JSON.stringify(settings)); |
| } |
|
|
| |
| setupTransferScreen() { |
| this.currentTransferStep = 1; |
| this.selectedSourceWallet = null; |
| this.transferData = {}; |
|
|
| |
| document.getElementById('transfer-next-btn').addEventListener('click', () => this.nextTransferStep()); |
| document.getElementById('transfer-prev-btn').addEventListener('click', () => this.prevTransferStep()); |
| document.getElementById('transfer-confirm-btn').addEventListener('click', () => this.confirmTransfer()); |
|
|
| |
| document.querySelectorAll('.amount-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| document.getElementById('transfer-amount').value = btn.dataset.amount; |
| }); |
| }); |
| } |
|
|
| |
| showTransferScreen() { |
| this.switchScreen('transfer'); |
| this.resetTransferForm(); |
| this.renderSourceWallets(); |
| } |
|
|
| |
| resetTransferForm() { |
| this.currentTransferStep = 1; |
| this.selectedSourceWallet = null; |
| this.transferData = {}; |
|
|
| |
| this.updateTransferSteps(); |
|
|
| |
| document.getElementById('destination-wallet').value = ''; |
| document.getElementById('recipient-number').value = ''; |
| document.getElementById('transfer-amount').value = ''; |
| document.getElementById('transfer-note').value = ''; |
| document.getElementById('transfer-pin').value = ''; |
| } |
|
|
| |
| updateTransferSteps() { |
| for (let i = 1; i <= 3; i++) { |
| const step = document.getElementById(`transfer-step-${i}`); |
| const content = document.getElementById(`transfer-step-content-${i}`); |
|
|
| if (i < this.currentTransferStep) { |
| step.classList.add('completed'); |
| step.classList.remove('active'); |
| } else if (i === this.currentTransferStep) { |
| step.classList.add('active'); |
| step.classList.remove('completed'); |
| } else { |
| step.classList.remove('active', 'completed'); |
| } |
|
|
| content.classList.toggle('active', i === this.currentTransferStep); |
| } |
|
|
| |
| const prevBtn = document.getElementById('transfer-prev-btn'); |
| const nextBtn = document.getElementById('transfer-next-btn'); |
| const confirmBtn = document.getElementById('transfer-confirm-btn'); |
|
|
| prevBtn.style.display = this.currentTransferStep > 1 ? 'block' : 'none'; |
| nextBtn.style.display = this.currentTransferStep < 3 ? 'block' : 'none'; |
| confirmBtn.style.display = this.currentTransferStep === 3 ? 'block' : 'none'; |
| } |
|
|
| |
| renderSourceWallets() { |
| const container = document.getElementById('source-wallet-selection'); |
| container.innerHTML = ''; |
|
|
| this.wallets.forEach(wallet => { |
| const option = this.createWalletOption(wallet); |
| container.appendChild(option); |
| }); |
| } |
|
|
| |
| createWalletOption(wallet) { |
| const option = document.createElement('div'); |
| option.className = 'wallet-option'; |
| option.onclick = () => this.selectSourceWallet(wallet); |
|
|
| const balanceDisplay = this.isBalanceHidden ? |
| '•••••' : wallet.balance.toLocaleString(); |
|
|
| option.innerHTML = ` |
| <div class="wallet-option-info"> |
| <div class="wallet-option-icon"> |
| <img src="${wallet.icon}" alt="${wallet.name}" onerror="this.style.display='none'"> |
| </div> |
| <div class="wallet-option-details"> |
| <h4>${wallet.name}</h4> |
| <p>${wallet.provider}</p> |
| </div> |
| </div> |
| <div class="wallet-option-balance"> |
| <div class="amount">${balanceDisplay}</div> |
| <div class="currency">ر.ي</div> |
| </div> |
| `; |
|
|
| return option; |
| } |
|
|
| |
| selectSourceWallet(wallet) { |
| this.selectedSourceWallet = wallet; |
|
|
| |
| document.querySelectorAll('.wallet-option').forEach(option => { |
| option.classList.remove('selected'); |
| }); |
| event.currentTarget.classList.add('selected'); |
|
|
| this.transferData.sourceWallet = wallet; |
| } |
|
|
| |
| nextTransferStep() { |
| if (this.validateCurrentStep()) { |
| this.currentTransferStep++; |
| this.updateTransferSteps(); |
|
|
| if (this.currentTransferStep === 3) { |
| this.updateTransferSummary(); |
| } |
| } |
| } |
|
|
| |
| prevTransferStep() { |
| this.currentTransferStep--; |
| this.updateTransferSteps(); |
| } |
|
|
| |
| validateCurrentStep() { |
| switch (this.currentTransferStep) { |
| case 1: |
| if (!this.selectedSourceWallet) { |
| this.showAlert('يرجى اختيار المحفظة المرسلة', 'error'); |
| return false; |
| } |
| return true; |
|
|
| case 2: |
| const destinationWallet = document.getElementById('destination-wallet').value; |
| const recipientNumber = document.getElementById('recipient-number').value; |
| const amount = parseFloat(document.getElementById('transfer-amount').value); |
|
|
| if (!destinationWallet) { |
| this.showAlert('يرجى اختيار المحفظة المستقبلة', 'error'); |
| return false; |
| } |
|
|
| if (!recipientNumber || recipientNumber.length !== 9) { |
| this.showAlert('يرجى إدخال رقم المستقبل صحيح (9 أرقام)', 'error'); |
| return false; |
| } |
|
|
| if (!amount || amount <= 0) { |
| this.showAlert('يرجى إدخال مبلغ صحيح', 'error'); |
| return false; |
| } |
|
|
| if (amount > this.selectedSourceWallet.balance) { |
| this.showAlert('المبلغ أكبر من الرصيد المتاح', 'error'); |
| return false; |
| } |
|
|
| |
| this.transferData.destinationWallet = destinationWallet; |
| this.transferData.recipientNumber = recipientNumber; |
| this.transferData.amount = amount; |
| this.transferData.note = document.getElementById('transfer-note').value; |
|
|
| return true; |
|
|
| default: |
| return true; |
| } |
| } |
|
|
| |
| updateTransferSummary() { |
| const sourceWalletInfo = this.walletManager.getWalletInfo(this.transferData.sourceWallet.id); |
| const destinationWalletInfo = this.walletManager.getWalletInfo(this.transferData.destinationWallet); |
|
|
| const fee = sourceWalletInfo.fees.transfer; |
| const total = this.transferData.amount + fee; |
|
|
| document.getElementById('summary-from-wallet').textContent = this.transferData.sourceWallet.name; |
| document.getElementById('summary-to-wallet').textContent = destinationWalletInfo.name; |
| document.getElementById('summary-recipient').textContent = `+967${this.transferData.recipientNumber}`; |
| document.getElementById('summary-amount').textContent = `${this.transferData.amount.toLocaleString()} ر.ي`; |
| document.getElementById('summary-fee').textContent = `${fee.toLocaleString()} ر.ي`; |
| document.getElementById('summary-total').textContent = `${total.toLocaleString()} ر.ي`; |
| } |
|
|
| |
| async confirmTransfer() { |
| const pin = document.getElementById('transfer-pin').value; |
|
|
| if (!pin) { |
| this.showAlert('يرجى إدخال رمز PIN', 'error'); |
| return; |
| } |
|
|
| this.showLoading('جاري تنفيذ التحويل...'); |
|
|
| try { |
| const result = await this.walletManager.executeTransfer( |
| this.transferData.sourceWallet.id, |
| this.transferData.destinationWallet, |
| this.transferData.amount, |
| this.transferData.recipientNumber, |
| pin |
| ); |
|
|
| this.hideLoading(); |
| this.showTransferSuccess(result); |
|
|
| |
| await this.loadUserWallets(); |
|
|
| } catch (error) { |
| this.hideLoading(); |
| this.showAlert(error.message, 'error'); |
| } |
| } |
|
|
| |
| showTransferSuccess(result) { |
| const message = ` |
| تم التحويل بنجاح! |
| |
| رقم المعاملة: ${result.transactionId} |
| المبلغ المحول: ${result.amount.toLocaleString()} ر.ي |
| الرسوم: ${result.fee.toLocaleString()} ر.ي |
| الرصيد المتبقي: ${result.newBalance.toLocaleString()} ر.ي |
| `; |
|
|
| this.showAlert(message, 'success'); |
| this.switchScreen('main'); |
| } |
|
|
| |
| showBillsScreen() { console.log('عرض شاشة الفواتير'); } |
| showRechargeScreen() { console.log('عرض شاشة الشحن'); } |
| startQRScan() { console.log('بدء مسح QR'); } |
| showSettings() { console.log('عرض الإعدادات'); } |
| showNotifications() { |
| this.notificationManager.showNotificationsList(); |
| } |
| showWalletManagement() { console.log('إدارة المحافظ'); } |
| showAllTransactions() { console.log('عرض جميع المعاملات'); } |
| openWalletDetails(wallet) { console.log('تفاصيل المحفظة:', wallet.name); } |
| showRegisterModal() { |
| this.notificationManager.showModal( |
| 'إنشاء حساب جديد', |
| ` |
| <div class="form-group"> |
| <label>رقم الهاتف</label> |
| <div class="input-group"> |
| <span class="input-prefix">+967</span> |
| <input type="tel" id="register-phone" placeholder="7xxxxxxxx" maxlength="9"> |
| </div> |
| </div> |
| <div class="form-group"> |
| <label>رمز PIN</label> |
| <input type="password" id="register-pin" placeholder="أدخل رمز PIN" maxlength="6"> |
| </div> |
| <div class="form-group"> |
| <label>تأكيد رمز PIN</label> |
| <input type="password" id="register-pin-confirm" placeholder="أعد إدخال رمز PIN" maxlength="6"> |
| </div> |
| `, |
| [ |
| { |
| text: 'إنشاء الحساب', |
| class: 'btn-primary', |
| action: () => this.handleRegister() |
| }, |
| { |
| text: 'إلغاء', |
| class: 'btn-secondary' |
| } |
| ] |
| ); |
| } |
|
|
| |
| handleRegister() { |
| const phone = document.getElementById('register-phone').value; |
| const pin = document.getElementById('register-pin').value; |
| const pinConfirm = document.getElementById('register-pin-confirm').value; |
|
|
| if (!phone || !pin || !pinConfirm) { |
| this.showAlert('يرجى ملء جميع الحقول', 'error'); |
| return; |
| } |
|
|
| if (pin !== pinConfirm) { |
| this.showAlert('رمز PIN غير متطابق', 'error'); |
| return; |
| } |
|
|
| if (phone.length !== 9) { |
| this.showAlert('رقم الهاتف يجب أن يكون 9 أرقام', 'error'); |
| return; |
| } |
|
|
| if (pin.length < 4) { |
| this.showAlert('رمز PIN يجب أن يكون 4 أرقام على الأقل', 'error'); |
| return; |
| } |
|
|
| |
| this.showAlert('تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول', 'success'); |
|
|
| |
| document.getElementById('phone-number').value = phone; |
| document.getElementById('pin-code').value = pin; |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| window.app = new UnifiedWalletApp(); |
| }); |
|
|