diff --git a/wordpress-plugins/falah-shortcodes.zip b/wordpress-plugins/falah-shortcodes.zip index 23902bd..6cf0765 100644 Binary files a/wordpress-plugins/falah-shortcodes.zip and b/wordpress-plugins/falah-shortcodes.zip differ diff --git a/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.css b/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.css index 0994f55..1783e57 100644 --- a/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.css +++ b/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.css @@ -541,6 +541,350 @@ background: var(--f-gold-light); } +/* ── Wallet widget ────────────────────────────────────────────────────────── */ +.falah-wallet-widget .falah-widget-header { justify-content: flex-start; } +.falah-wallet-currency-badge { + margin-left: auto; + background: rgba(255,255,255,0.15); + border: 1px solid rgba(255,255,255,0.3); + border-radius: 20px; + padding: 2px 10px; + font-size: 0.72rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: #fff; +} + +.falah-wallet-body { padding: 24px; } + +.falah-wallet-balance-card { + background: linear-gradient(135deg, var(--f-navy) 0%, var(--f-navy-mid) 100%); + border-radius: var(--f-radius); + padding: 28px 24px 24px; + color: #fff; + position: relative; + overflow: hidden; + margin-bottom: 24px; +} +.falah-wallet-balance-card::before { + content: ''; + position: absolute; + top: -30px; + right: -30px; + width: 140px; + height: 140px; + border-radius: 50%; + background: rgba(200,146,26,0.12); + pointer-events: none; +} +.falah-wallet-balance-label { + font-size: 0.75rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: rgba(255,255,255,0.55); + margin-bottom: 8px; +} +.falah-wallet-balance-amount { + font-family: var(--f-font-display); + font-size: clamp(2rem, 5vw, 3rem); + font-weight: 600; + letter-spacing: -0.02em; + line-height: 1; + margin-bottom: 6px; + min-height: 1em; +} +.falah-wallet-balance-sub { + font-size: 0.82rem; + color: rgba(255,255,255,0.45); + margin-bottom: 20px; +} +.falah-wallet-actions { + display: flex; + gap: 12px; +} +.falah-wallet-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 9px 18px; + border-radius: var(--f-radius-sm); + border: none; + cursor: pointer; + font-family: var(--f-font-body); + font-size: 0.85rem; + font-weight: 600; + transition: opacity var(--f-fast) var(--f-ease), transform var(--f-fast) var(--f-ease), box-shadow var(--f-fast) var(--f-ease); +} +.falah-wallet-btn svg { width: 15px; height: 15px; flex-shrink: 0; } +.falah-wallet-btn:disabled { opacity: 0.45; cursor: not-allowed; } +.falah-wallet-btn:not(:disabled):hover { opacity: 0.88; transform: translateY(-1px); box-shadow: var(--f-shadow-gold); } +.falah-wallet-btn-send { + background: var(--f-gold); + color: #fff; +} +.falah-wallet-btn-receive { + background: rgba(255,255,255,0.12); + color: #fff; + border: 1px solid rgba(255,255,255,0.2); +} +.falah-wallet-btn-full { width: 100%; justify-content: center; margin-top: 8px; } + +/* Loading dots */ +.falah-wallet-loading-dots { + display: inline-flex; + gap: 5px; + align-items: center; + padding: 4px 0; +} +.falah-wallet-loading-dots span { + width: 6px; + height: 6px; + border-radius: 50%; + background: rgba(255,255,255,0.6); + animation: falah-wallet-dot-pulse 1.2s ease-in-out infinite; +} +.falah-wallet-loading-dots span:nth-child(2) { animation-delay: 0.2s; } +.falah-wallet-loading-dots span:nth-child(3) { animation-delay: 0.4s; } +@keyframes falah-wallet-dot-pulse { + 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); } + 40% { opacity: 1; transform: scale(1); } +} + +/* Login prompt */ +.falah-wallet-login-prompt { + text-align: center; + padding: 20px; + color: var(--f-text-muted); + font-size: 0.9rem; +} +.falah-wallet-login-link { + display: inline-block; + margin-top: 8px; + padding: 8px 20px; + background: var(--f-emerald); + color: #fff; + border-radius: var(--f-radius-sm); + text-decoration: none; + font-weight: 600; + font-size: 0.85rem; + transition: background var(--f-fast) var(--f-ease); +} +.falah-wallet-login-link:hover { background: var(--f-emerald-mid); } + +/* Transaction list */ +.falah-wallet-tx-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} +.falah-wallet-tx-title { + font-size: 0.78rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--f-text-muted); +} +.falah-wallet-tx-viewall { + font-size: 0.78rem; + color: var(--f-gold); + text-decoration: none; + font-weight: 600; +} +.falah-wallet-tx-viewall:hover { text-decoration: underline; } +.falah-wallet-tx-list { display: flex; flex-direction: column; gap: 2px; } +.falah-wallet-tx-item { + display: flex; + align-items: center; + gap: 12px; + padding: 11px 14px; + border-radius: var(--f-radius-sm); + transition: background var(--f-fast); +} +.falah-wallet-tx-item:hover { background: var(--f-cream); } +.falah-wallet-tx-icon { + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.9rem; + flex-shrink: 0; +} +.falah-wallet-tx-icon.sent { background: #FEE2E2; } +.falah-wallet-tx-icon.received { background: var(--f-emerald-light); } +.falah-wallet-tx-info { flex: 1; min-width: 0; } +.falah-wallet-tx-desc { + font-size: 0.88rem; + font-weight: 500; + color: var(--f-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.falah-wallet-tx-date { + font-size: 0.72rem; + color: var(--f-text-muted); + margin-top: 2px; +} +.falah-wallet-tx-amount { + font-size: 0.88rem; + font-weight: 600; + flex-shrink: 0; +} +.falah-wallet-tx-amount.sent { color: #DC2626; } +.falah-wallet-tx-amount.received { color: var(--f-emerald); } +.falah-wallet-tx-empty { + text-align: center; + padding: 20px; + color: var(--f-text-muted); + font-size: 0.85rem; +} + +/* Skeleton loader */ +.falah-wallet-tx-skeleton { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; } +.falah-wallet-tx-skel-row { + height: 44px; + border-radius: var(--f-radius-sm); + background: linear-gradient(90deg, var(--f-border) 25%, rgba(10,22,40,0.04) 50%, var(--f-border) 75%); + background-size: 200% 100%; + animation: falah-skel-shimmer 1.4s ease-in-out infinite; +} +@keyframes falah-skel-shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +/* Modal */ +.falah-wallet-modal { + position: fixed; + inset: 0; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; +} +.falah-wallet-modal-backdrop { + position: absolute; + inset: 0; + background: rgba(10,22,40,0.55); + backdrop-filter: blur(4px); +} +.falah-wallet-modal-box { + position: relative; + background: var(--f-surface); + border-radius: var(--f-radius); + box-shadow: var(--f-shadow-lg); + width: 100%; + max-width: 400px; + overflow: hidden; + animation: falah-modal-in var(--f-normal) var(--f-ease); +} +@keyframes falah-modal-in { + from { opacity: 0; transform: translateY(12px) scale(0.97); } + to { opacity: 1; transform: translateY(0) scale(1); } +} +.falah-wallet-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 18px 20px; + border-bottom: 1px solid var(--f-border); +} +.falah-wallet-modal-header h4 { + font-family: var(--f-font-display); + font-size: 1.15rem; + font-weight: 600; + color: var(--f-navy); + margin: 0; +} +.falah-wallet-modal-close { + background: none; + border: none; + cursor: pointer; + color: var(--f-text-muted); + font-size: 1rem; + line-height: 1; + padding: 4px; + border-radius: 4px; + transition: color var(--f-fast), background var(--f-fast); +} +.falah-wallet-modal-close:hover { color: var(--f-text); background: var(--f-border); } +.falah-wallet-modal-body { padding: 20px; display: flex; flex-direction: column; gap: 10px; } +.falah-wallet-field-label { + font-size: 0.78rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--f-text-muted); + margin-bottom: 2px; +} +.falah-wallet-field { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--f-border); + border-radius: var(--f-radius-sm); + font-family: var(--f-font-body); + font-size: 0.9rem; + color: var(--f-text); + background: var(--f-cream); + transition: border-color var(--f-fast); + box-sizing: border-box; +} +.falah-wallet-field:focus { outline: none; border-color: var(--f-emerald-mid); } +.falah-wallet-modal-err { + font-size: 0.8rem; + color: #DC2626; + min-height: 1em; +} + +/* Receive modal */ +.falah-wallet-receive-body { align-items: center; } +.falah-wallet-qr-placeholder { + width: 180px; + height: 180px; + border: 2px solid var(--f-border-gold); + border-radius: var(--f-radius); + display: flex; + align-items: center; + justify-content: center; + background: var(--f-cream); +} +.falah-wallet-qr-inner { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; } +.falah-wallet-qr-inner img { width: 100%; height: 100%; border-radius: calc(var(--f-radius) - 4px); } +.falah-wallet-address-box { + display: flex; + align-items: center; + gap: 8px; + background: var(--f-cream); + border: 1px solid var(--f-border); + border-radius: var(--f-radius-sm); + padding: 10px 12px; + width: 100%; + box-sizing: border-box; +} +.falah-wallet-address-text { + flex: 1; + font-size: 0.72rem; + color: var(--f-text-muted); + word-break: break-all; + font-family: monospace; +} +.falah-wallet-copy-btn { + background: none; + border: none; + cursor: pointer; + color: var(--f-text-muted); + padding: 2px; + flex-shrink: 0; + transition: color var(--f-fast); +} +.falah-wallet-copy-btn svg { width: 16px; height: 16px; display: block; } +.falah-wallet-copy-btn:hover { color: var(--f-emerald); } + /* ── Responsive ───────────────────────────────────────────────────────────── */ @media (max-width: 600px) { .falah-prayer-grid { grid-template-columns: repeat(3, 1fr); } @@ -551,4 +895,6 @@ .falah-compass { width: 180px; height: 180px; } .falah-verse-arabic { font-size: 1.45rem; } .falah-dashboard-header h2 { font-size: 1.6rem; } + .falah-wallet-actions { flex-direction: column; } + .falah-wallet-balance-amount { font-size: 2rem; } } diff --git a/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.js b/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.js index 79000a7..be1676b 100644 --- a/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.js +++ b/wordpress-plugins/falah-shortcodes/assets/falah-shortcodes.js @@ -273,4 +273,150 @@ }) .catch(() => {}); + // ── Falah Wallet ────────────────────────────────────────────────────────── + + const walletWidget = document.getElementById('falah-wallet-widget'); + if (walletWidget) { + const balanceEl = document.getElementById('falah-wallet-balance'); + const balanceFiat = document.getElementById('falah-wallet-balance-fiat'); + const btnSend = document.getElementById('falah-wallet-btn-send'); + const btnReceive = document.getElementById('falah-wallet-btn-receive'); + const txList = document.getElementById('falah-wallet-tx-list'); + const loginPrompt = document.getElementById('falah-wallet-login-prompt'); + + const sendModal = document.getElementById('falah-wallet-send-modal'); + const receiveModal = document.getElementById('falah-wallet-receive-modal'); + + function openModal(el) { el.style.display = 'flex'; document.body.style.overflow = 'hidden'; } + function closeModal(el) { el.style.display = 'none'; document.body.style.overflow = ''; } + + if (btnSend) btnSend.addEventListener('click', () => sendModal && openModal(sendModal)); + if (btnReceive) btnReceive.addEventListener('click', () => receiveModal && openModal(receiveModal)); + + document.getElementById('falah-wallet-send-backdrop')?.addEventListener('click', () => closeModal(sendModal)); + document.getElementById('falah-wallet-send-close')?.addEventListener('click', () => closeModal(sendModal)); + document.getElementById('falah-wallet-receive-backdrop')?.addEventListener('click', () => closeModal(receiveModal)); + document.getElementById('falah-wallet-receive-close')?.addEventListener('click', () => closeModal(receiveModal)); + + // Copy address + document.getElementById('falah-wallet-copy-addr')?.addEventListener('click', function () { + const addr = document.getElementById('falah-wallet-address-text')?.textContent || ''; + if (!addr) return; + navigator.clipboard?.writeText(addr).then(() => { + this.title = 'Copied!'; + setTimeout(() => { this.title = 'Copy address'; }, 1500); + }); + }); + + // Send form + document.getElementById('falah-wallet-send-submit')?.addEventListener('click', function () { + const to = document.getElementById('falah-wallet-send-to')?.value.trim(); + const amount = parseFloat(document.getElementById('falah-wallet-send-amount')?.value); + const errEl = document.getElementById('falah-wallet-send-err'); + + if (!to || !to.startsWith('0x') || to.length < 10) { + if (errEl) errEl.textContent = 'Please enter a valid recipient address.'; + return; + } + if (!amount || amount <= 0) { + if (errEl) errEl.textContent = 'Please enter a valid amount.'; + return; + } + if (errEl) errEl.textContent = ''; + + // Redirect to wallet app with prefilled params + const base = (cfg.mobileUrl || 'https://falahos.my/mobile').replace(/\/$/, ''); + const url = `${base}/wallet/send?to=${encodeURIComponent(to)}&amount=${encodeURIComponent(amount)}`; + window.open(url, '_blank', 'noopener,noreferrer'); + closeModal(sendModal); + }); + + // Fetch wallet data from Falah Mobile REST API + function fetchWalletData() { + const base = (cfg.mobileUrl || 'https://falahos.my/mobile').replace(/\/$/, ''); + + fetch(`${base}/api/wallet/balance`, { + credentials: 'include', + headers: { 'X-WP-Nonce': cfg.nonce || '' }, + }) + .then(r => { + if (r.status === 401 || r.status === 403) throw new Error('unauthenticated'); + if (!r.ok) throw new Error('api_error'); + return r.json(); + }) + .then(data => { + if (balanceEl) balanceEl.textContent = (data.balance || '0') + ' ' + (data.currency || 'FLH'); + if (balanceFiat && data.fiat_value) balanceFiat.textContent = '≈ ' + data.fiat_value; + if (btnSend) btnSend.disabled = false; + if (btnReceive) btnReceive.disabled = false; + + // Render receive address + QR + if (data.address) { + const addrEl = document.getElementById('falah-wallet-address-text'); + if (addrEl) addrEl.textContent = data.address; + const qrInner = document.getElementById('falah-wallet-qr-inner'); + if (qrInner) { + const img = document.createElement('img'); + img.alt = 'Wallet QR Code'; + img.src = `https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(data.address)}&size=160x160&margin=4`; + img.width = 160; img.height = 160; + qrInner.innerHTML = ''; + qrInner.appendChild(img); + } + } + + return fetch(`${base}/api/wallet/transactions?limit=5`, { + credentials: 'include', + headers: { 'X-WP-Nonce': cfg.nonce || '' }, + }); + }) + .then(r => r && r.ok ? r.json() : null) + .then(data => { + if (!txList || !data) return; + const items = Array.isArray(data.transactions) ? data.transactions : []; + txList.innerHTML = ''; + if (!items.length) { + txList.innerHTML = '
No transactions yet
'; + return; + } + items.forEach(tx => { + const type = tx.type === 'sent' ? 'sent' : 'received'; + const icon = type === 'sent' ? '↑' : '↓'; + const sign = type === 'sent' ? '−' : '+'; + const date = tx.date ? new Date(tx.date).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' }) : ''; + const desc = tx.description || (type === 'sent' ? 'Sent' : 'Received'); + const amount = (tx.amount || '0') + ' ' + (tx.currency || 'FLH'); + + const row = document.createElement('div'); + row.className = 'falah-wallet-tx-item'; + row.innerHTML = ` +
${icon}
+
+
${escapeHtml(desc)}
+
${escapeHtml(date)}
+
+
${sign}${escapeHtml(amount)}
+ `; + txList.appendChild(row); + }); + }) + .catch(err => { + if (err.message === 'unauthenticated') { + if (balanceEl) balanceEl.textContent = '—'; + if (loginPrompt) loginPrompt.style.display = ''; + if (txList) txList.innerHTML = ''; + } else { + if (balanceEl) balanceEl.textContent = '—'; + if (txList) txList.innerHTML = '
Could not load wallet. Open wallet app →
'; + } + }); + } + + fetchWalletData(); + } + + function escapeHtml(str) { + return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); + } + })(); diff --git a/wordpress-plugins/falah-shortcodes/falah-shortcodes.php b/wordpress-plugins/falah-shortcodes/falah-shortcodes.php index 0e1fbca..e45e725 100644 --- a/wordpress-plugins/falah-shortcodes/falah-shortcodes.php +++ b/wordpress-plugins/falah-shortcodes/falah-shortcodes.php @@ -315,7 +315,106 @@ add_shortcode( 'nur_ai_chat', function ( $atts ) { // ─── [falah_wallet] ─────────────────────────────────────────────────────────── add_shortcode( 'falah_wallet', function ( $atts ) { - return falah_iframe_widget( 'Falah Wallet', '💰', '/wallet', 'Loading Falah Wallet…' ); + falah_sc_enqueue(); + $atts = shortcode_atts( [ + 'currency' => 'FLH', + 'show_tx' => 'true', + ], $atts ); + + $currency = esc_html( $atts['currency'] ); + $show_tx = filter_var( $atts['show_tx'], FILTER_VALIDATE_BOOLEAN ); + + ob_start(); + ?> +
+
+ 💰 +

Falah Wallet

+ +
+ +
+
+
Total Balance
+
+ +
+
+
+ + +
+
+ + + + +
+
+ Recent Transactions + View all → +
+
+
+
+
+
+
+
+
+ +
+ + + + +
+ post_content ?? '', 'souq_marketplace' ) ) { + wp_enqueue_style( + 'falah-souq', + FALAH_SOUQ_URL . 'assets/falah-souq.css', + [], + FALAH_SOUQ_VERSION + ); + } +} +add_action( 'wp_enqueue_scripts', 'falah_souq_enqueue' ); + +function falah_souq_enqueue_always() { + wp_enqueue_style( + 'falah-souq', + FALAH_SOUQ_URL . 'assets/falah-souq.css', + [], + FALAH_SOUQ_VERSION + ); +} +add_action( 'wp_enqueue_scripts', 'falah_souq_enqueue_always' ); + +register_activation_hook( __FILE__, function () { + falah_souq_register_post_type(); + flush_rewrite_rules(); +} ); diff --git a/wordpress-plugins/falah-souq/includes/post-type.php b/wordpress-plugins/falah-souq/includes/post-type.php new file mode 100644 index 0000000..2b0e1dc --- /dev/null +++ b/wordpress-plugins/falah-souq/includes/post-type.php @@ -0,0 +1,51 @@ + [ + 'name' => __( 'Services', 'falah-souq' ), + 'singular_name' => __( 'Service', 'falah-souq' ), + 'add_new_item' => __( 'Add New Service', 'falah-souq' ), + 'edit_item' => __( 'Edit Service', 'falah-souq' ), + ], + 'public' => true, + 'has_archive' => true, + 'rewrite' => [ 'slug' => 'souq' ], + 'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ], + 'menu_icon' => 'dashicons-store', + 'show_in_rest' => true, + ] ); + + register_post_meta( 'souq_service', '_souq_price', [ + 'type' => 'number', + 'single' => true, + 'show_in_rest' => true, + ] ); + register_post_meta( 'souq_service', '_souq_delivery_days', [ + 'type' => 'integer', + 'single' => true, + 'show_in_rest' => true, + ] ); + register_post_meta( 'souq_service', '_souq_category', [ + 'type' => 'string', + 'single' => true, + 'show_in_rest' => true, + ] ); + register_post_meta( 'souq_service', '_souq_seller_name', [ + 'type' => 'string', + 'single' => true, + 'show_in_rest' => true, + ] ); + register_post_meta( 'souq_service', '_souq_rating', [ + 'type' => 'number', + 'single' => true, + 'show_in_rest' => true, + ] ); + register_post_meta( 'souq_service', '_souq_reviews_count', [ + 'type' => 'integer', + 'single' => true, + 'show_in_rest' => true, + ] ); +} +add_action( 'init', 'falah_souq_register_post_type' ); diff --git a/wordpress-plugins/falah-souq/includes/shortcode.php b/wordpress-plugins/falah-souq/includes/shortcode.php new file mode 100644 index 0000000..fb24613 --- /dev/null +++ b/wordpress-plugins/falah-souq/includes/shortcode.php @@ -0,0 +1,153 @@ + '', + 'limit' => 12, + 'columns' => 3, + ], $atts, 'souq_marketplace' ); + + $query_args = [ + 'post_type' => 'souq_service', + 'post_status' => 'publish', + 'posts_per_page' => (int) $atts['limit'], + 'orderby' => 'date', + 'order' => 'DESC', + ]; + + if ( $atts['category'] ) { + $query_args['meta_query'] = [ [ + 'key' => '_souq_category', + 'value' => sanitize_text_field( $atts['category'] ), + ] ]; + } + + $services = new WP_Query( $query_args ); + + ob_start(); + ?> +
+
+ + + + + + +
+ + have_posts() ) : ?> +
+ have_posts() ) : $services->the_post(); + $price = get_post_meta( get_the_ID(), '_souq_price', true ); + $days = get_post_meta( get_the_ID(), '_souq_delivery_days', true ); + $category = get_post_meta( get_the_ID(), '_souq_category', true ); + $seller = get_post_meta( get_the_ID(), '_souq_seller_name', true ); + $rating = (float) get_post_meta( get_the_ID(), '_souq_rating', true ); + $reviews = (int) get_post_meta( get_the_ID(), '_souq_reviews_count', true ); + $stars_full = floor( $rating ); + $has_half = ( $rating - $stars_full ) >= 0.5; + ?> + + +
+ + +
+ +

No services listed yet. Be the first to offer your skills to the Ummah.

+ + Add Service + +
+ +
+ + +