Files
Antigravity AI dceb30ed8c feat: add falah-souq marketplace plugin and native falah_wallet widget
- [falah-souq] new standalone plugin with [souq_marketplace] shortcode
  Fiverr-style service grid with Islamic luxury design (Emerald/Gold/Cream/Navy)
  Custom post type 'souq_service', category filter bar, card hover states
- [falah_wallet] replace iframe with native balance+transactions widget
  Balance card with loading state, send/receive action buttons
  Recent transactions list, login-gated with smooth reveal
  CSS tokens matching Islamic design system
- Rebuild falah-shortcodes.zip with updated wallet widget
2026-07-06 22:10:47 +08:00

493 lines
22 KiB
PHP

<?php
/**
* Plugin Name: Falah Shortcodes
* Description: Implements [prayer_times], [dhikr_counter], [qibla_compass], [daily_verse], [nur_ai_chat], [falah_wallet], [souq_marketplace] for ummah.falahos.my
* Version: 1.0.0
* Author: Falah OS Team
* License: MIT
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'FALAH_SC_VERSION', '1.0.0' );
define( 'FALAH_SC_PATH', plugin_dir_path( __FILE__ ) );
define( 'FALAH_SC_URL', plugin_dir_url( __FILE__ ) );
// ─── Admin settings ───────────────────────────────────────────────────────────
add_action( 'admin_menu', function () {
add_options_page(
'Falah Shortcodes',
'Falah Shortcodes',
'manage_options',
'falah-shortcodes',
'falah_sc_settings_page'
);
} );
add_action( 'admin_init', function () {
register_setting( 'falah_sc_options', 'falah_mobile_url', [
'type' => 'string',
'sanitize_callback' => 'esc_url_raw',
'default' => 'https://falahos.my/mobile',
] );
register_setting( 'falah_sc_options', 'falah_prayer_city', [
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'Kuala Lumpur',
] );
register_setting( 'falah_sc_options', 'falah_prayer_country', [
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'Malaysia',
] );
} );
function falah_sc_settings_page() {
?>
<div class="wrap">
<h1>Falah Shortcodes Settings</h1>
<form method="post" action="options.php">
<?php settings_fields( 'falah_sc_options' ); ?>
<table class="form-table">
<tr>
<th>Falah Mobile URL</th>
<td>
<input type="url" name="falah_mobile_url" value="<?php echo esc_attr( get_option( 'falah_mobile_url', 'https://falahos.my/mobile' ) ); ?>" class="regular-text" />
<p class="description">Base URL for Falah Mobile app (used by [nur_ai_chat], [falah_wallet], [souq_marketplace])</p>
</td>
</tr>
<tr>
<th>Default Prayer City</th>
<td><input type="text" name="falah_prayer_city" value="<?php echo esc_attr( get_option( 'falah_prayer_city', 'Kuala Lumpur' ) ); ?>" class="regular-text" /></td>
</tr>
<tr>
<th>Default Prayer Country</th>
<td><input type="text" name="falah_prayer_country" value="<?php echo esc_attr( get_option( 'falah_prayer_country', 'Malaysia' ) ); ?>" class="regular-text" /></td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
// ─── Enqueue assets ───────────────────────────────────────────────────────────
add_action( 'wp_enqueue_scripts', function () {
wp_register_style(
'falah-sc-styles',
FALAH_SC_URL . 'assets/falah-shortcodes.css',
[],
FALAH_SC_VERSION
);
wp_register_script(
'falah-sc-scripts',
FALAH_SC_URL . 'assets/falah-shortcodes.js',
[],
FALAH_SC_VERSION,
true
);
wp_localize_script( 'falah-sc-scripts', 'FalahSC', [
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'mobileUrl' => esc_url( get_option( 'falah_mobile_url', 'https://falahos.my/mobile' ) ),
'prayerCity' => esc_js( get_option( 'falah_prayer_city', 'Kuala Lumpur' ) ),
'prayerCountry' => esc_js( get_option( 'falah_prayer_country', 'Malaysia' ) ),
'nonce' => wp_create_nonce( 'falah_sc_nonce' ),
] );
} );
function falah_sc_enqueue() {
wp_enqueue_style( 'falah-sc-styles' );
wp_enqueue_script( 'falah-sc-scripts' );
}
// ─── [prayer_times] ───────────────────────────────────────────────────────────
add_shortcode( 'prayer_times', function ( $atts ) {
falah_sc_enqueue();
$atts = shortcode_atts( [
'city' => get_option( 'falah_prayer_city', 'Kuala Lumpur' ),
'country' => get_option( 'falah_prayer_country', 'Malaysia' ),
], $atts );
$city = esc_attr( $atts['city'] );
$country = esc_attr( $atts['country'] );
ob_start();
?>
<div class="falah-widget falah-prayer-times" data-city="<?php echo $city; ?>" data-country="<?php echo $country; ?>">
<div class="falah-widget-header">
<span class="falah-icon">🕌</span>
<h3>Prayer Times</h3>
<span class="falah-location"><?php echo $city; ?>, <?php echo $country; ?></span>
</div>
<div class="falah-prayer-grid" id="falah-prayer-grid">
<div class="falah-loading">
<div class="falah-spinner"></div>
<span>Fetching prayer times…</span>
</div>
</div>
<div class="falah-next-prayer" id="falah-next-prayer"></div>
<div class="falah-hijri" id="falah-hijri"></div>
</div>
<?php
return ob_get_clean();
} );
// ─── [dhikr_counter] ──────────────────────────────────────────────────────────
add_shortcode( 'dhikr_counter', function ( $atts ) {
falah_sc_enqueue();
$atts = shortcode_atts( [
'target' => 33,
'dhikr' => 'SubhanAllah',
], $atts );
$target = intval( $atts['target'] );
$dhikr = esc_html( $atts['dhikr'] );
$dhikr_list = [
[ 'text' => 'SubhanAllah', 'meaning' => 'Glory be to Allah', 'target' => 33 ],
[ 'text' => 'Alhamdulillah', 'meaning' => 'Praise be to Allah', 'target' => 33 ],
[ 'text' => 'Allahu Akbar', 'meaning' => 'Allah is the Greatest', 'target' => 34 ],
[ 'text' => 'La ilaha illallah', 'meaning' => 'There is no god but Allah', 'target' => 100 ],
[ 'text' => 'Astaghfirullah', 'meaning' => 'I seek forgiveness from Allah', 'target' => 100 ],
];
$list_json = json_encode( $dhikr_list );
ob_start();
?>
<div class="falah-widget falah-dhikr-counter" id="falah-dhikr-widget" data-list='<?php echo esc_attr( $list_json ); ?>'>
<div class="falah-widget-header">
<span class="falah-icon">📿</span>
<h3>Dhikr Counter</h3>
</div>
<div class="falah-dhikr-selector" id="falah-dhikr-selector">
<?php foreach ( $dhikr_list as $i => $d ) : ?>
<button class="falah-dhikr-btn <?php echo $i === 0 ? 'active' : ''; ?>" data-index="<?php echo $i; ?>">
<?php echo esc_html( $d['text'] ); ?>
</button>
<?php endforeach; ?>
</div>
<div class="falah-dhikr-display">
<div class="falah-dhikr-arabic" id="falah-dhikr-arabic">سُبْحَانَ اللَّهِ</div>
<div class="falah-dhikr-meaning" id="falah-dhikr-meaning">Glory be to Allah</div>
</div>
<div class="falah-counter-circle" id="falah-counter-circle">
<svg viewBox="0 0 120 120">
<circle class="falah-ring-bg" cx="60" cy="60" r="50"/>
<circle class="falah-ring-progress" id="falah-ring" cx="60" cy="60" r="50"/>
</svg>
<div class="falah-counter-number" id="falah-counter-number">0</div>
</div>
<div class="falah-counter-target" id="falah-counter-target">/ 33</div>
<div class="falah-counter-actions">
<button class="falah-btn-tap" id="falah-btn-tap">Count</button>
<button class="falah-btn-reset" id="falah-btn-reset">Reset</button>
</div>
<div class="falah-session-total" id="falah-session-total"></div>
</div>
<?php
return ob_get_clean();
} );
// ─── [qibla_compass] ──────────────────────────────────────────────────────────
add_shortcode( 'qibla_compass', function ( $atts ) {
falah_sc_enqueue();
ob_start();
?>
<div class="falah-widget falah-qibla" id="falah-qibla-widget">
<div class="falah-widget-header">
<span class="falah-icon">🧭</span>
<h3>Qibla Compass</h3>
</div>
<div id="falah-qibla-status" class="falah-qibla-status">
<button class="falah-btn-locate" id="falah-qibla-locate">
📍 Find My Qibla
</button>
</div>
<div id="falah-qibla-display" class="falah-qibla-display" style="display:none">
<div class="falah-compass-wrap">
<div class="falah-compass" id="falah-compass">
<div class="falah-compass-rose">
<span class="falah-dir n">N</span>
<span class="falah-dir e">E</span>
<span class="falah-dir s">S</span>
<span class="falah-dir w">W</span>
</div>
<div class="falah-needle-kaaba" id="falah-qibla-needle">🕋</div>
</div>
</div>
<div class="falah-qibla-info">
<div id="falah-qibla-angle"></div>
<div id="falah-qibla-location"></div>
</div>
</div>
</div>
<?php
return ob_get_clean();
} );
// ─── [daily_verse] ────────────────────────────────────────────────────────────
add_shortcode( 'daily_verse', function ( $atts ) {
falah_sc_enqueue();
$atts = shortcode_atts( [ 'surah' => 0, 'ayah' => 0 ], $atts );
// Deterministic daily verse: use day-of-year as seed (6236 ayahs total)
$day_seed = intval( date( 'z' ) ) + intval( date( 'Y' ) ) * 365;
$verse_num = ( $day_seed % 6236 ) + 1;
$cache_key = 'falah_verse_' . $verse_num;
$cached = get_transient( $cache_key );
if ( $cached !== false ) {
return $cached;
}
ob_start();
?>
<div class="falah-widget falah-daily-verse" id="falah-daily-verse" data-verse="<?php echo intval( $verse_num ); ?>">
<div class="falah-widget-header">
<span class="falah-icon">📖</span>
<h3>Verse of the Day</h3>
</div>
<div class="falah-verse-loading" id="falah-verse-loading">
<div class="falah-spinner"></div>
</div>
<div class="falah-verse-content" id="falah-verse-content" style="display:none">
<p class="falah-verse-arabic" id="falah-verse-arabic" dir="rtl"></p>
<p class="falah-verse-translation" id="falah-verse-translation"></p>
<p class="falah-verse-ref" id="falah-verse-ref"></p>
</div>
</div>
<?php
$html = ob_get_clean();
set_transient( $cache_key, $html, DAY_IN_SECONDS );
return $html;
} );
// ─── Iframe helper ────────────────────────────────────────────────────────────
function falah_iframe_widget( $title, $icon, $path, $fallback_msg ) {
falah_sc_enqueue();
$mobile_url = rtrim( get_option( 'falah_mobile_url', 'https://falahos.my/mobile' ), '/' );
$src = esc_url( $mobile_url . $path );
ob_start();
?>
<div class="falah-widget falah-iframe-widget">
<div class="falah-widget-header">
<span class="falah-icon"><?php echo esc_html( $icon ); ?></span>
<h3><?php echo esc_html( $title ); ?></h3>
</div>
<div class="falah-iframe-container">
<iframe
src="<?php echo $src; ?>"
title="<?php echo esc_attr( $title ); ?>"
loading="lazy"
allow="geolocation; microphone"
class="falah-iframe"
onload="this.previousElementSibling.style.display='none'"
></iframe>
<div class="falah-iframe-fallback">
<div class="falah-spinner"></div>
<p><?php echo esc_html( $fallback_msg ); ?></p>
<a href="<?php echo $src; ?>" target="_blank" class="falah-open-link">Open in new tab →</a>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
// ─── [nur_ai_chat] ────────────────────────────────────────────────────────────
add_shortcode( 'nur_ai_chat', function ( $atts ) {
return falah_iframe_widget( 'Nur AI Coach', '✨', '/nur', 'Loading Nur AI Coach…' );
} );
// ─── [falah_wallet] ───────────────────────────────────────────────────────────
add_shortcode( 'falah_wallet', function ( $atts ) {
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();
?>
<div class="falah-widget falah-wallet-widget" id="falah-wallet-widget">
<div class="falah-widget-header">
<span class="falah-icon">💰</span>
<h3>Falah Wallet</h3>
<span class="falah-wallet-currency-badge"><?php echo $currency; ?></span>
</div>
<div class="falah-wallet-body">
<div class="falah-wallet-balance-card" id="falah-wallet-balance-card">
<div class="falah-wallet-balance-label">Total Balance</div>
<div class="falah-wallet-balance-amount" id="falah-wallet-balance">
<span class="falah-wallet-loading-dots"><span></span><span></span><span></span></span>
</div>
<div class="falah-wallet-balance-sub" id="falah-wallet-balance-fiat"></div>
<div class="falah-wallet-actions">
<button class="falah-wallet-btn falah-wallet-btn-send" id="falah-wallet-btn-send" disabled>
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 10h14M10 3l7 7-7 7"/></svg>
Send
</button>
<button class="falah-wallet-btn falah-wallet-btn-receive" id="falah-wallet-btn-receive" disabled>
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M17 10H3M10 17L3 10l7-7"/></svg>
Receive
</button>
</div>
</div>
<div class="falah-wallet-login-prompt" id="falah-wallet-login-prompt" style="display:none">
<p>Sign in to access your Falah Wallet</p>
<a href="<?php echo esc_url( wp_login_url( get_permalink() ) ); ?>" class="falah-wallet-login-link">Sign In</a>
</div>
<?php if ( $show_tx ) : ?>
<div class="falah-wallet-tx-section" id="falah-wallet-tx-section">
<div class="falah-wallet-tx-header">
<span class="falah-wallet-tx-title">Recent Transactions</span>
<a href="<?php echo esc_url( get_option( 'falah_mobile_url', 'https://falahos.my/mobile' ) . '/wallet' ); ?>" target="_blank" rel="noopener" class="falah-wallet-tx-viewall">View all →</a>
</div>
<div class="falah-wallet-tx-list" id="falah-wallet-tx-list">
<div class="falah-wallet-tx-skeleton">
<div class="falah-wallet-tx-skel-row"></div>
<div class="falah-wallet-tx-skel-row"></div>
<div class="falah-wallet-tx-skel-row"></div>
</div>
</div>
</div>
<?php endif; ?>
</div>
<div class="falah-wallet-modal" id="falah-wallet-send-modal" role="dialog" aria-modal="true" aria-label="Send <?php echo $currency; ?>" style="display:none">
<div class="falah-wallet-modal-backdrop" id="falah-wallet-send-backdrop"></div>
<div class="falah-wallet-modal-box">
<div class="falah-wallet-modal-header">
<h4>Send <?php echo $currency; ?></h4>
<button class="falah-wallet-modal-close" id="falah-wallet-send-close" aria-label="Close">✕</button>
</div>
<div class="falah-wallet-modal-body">
<label class="falah-wallet-field-label" for="falah-wallet-send-to">Recipient Address</label>
<input class="falah-wallet-field" type="text" id="falah-wallet-send-to" placeholder="0x…" autocomplete="off" />
<label class="falah-wallet-field-label" for="falah-wallet-send-amount">Amount (<?php echo $currency; ?>)</label>
<input class="falah-wallet-field" type="number" id="falah-wallet-send-amount" placeholder="0.00" min="0" step="any" />
<div class="falah-wallet-modal-err" id="falah-wallet-send-err" role="alert"></div>
<button class="falah-wallet-btn falah-wallet-btn-send falah-wallet-btn-full" id="falah-wallet-send-submit">Confirm Send</button>
</div>
</div>
</div>
<div class="falah-wallet-modal" id="falah-wallet-receive-modal" role="dialog" aria-modal="true" aria-label="Receive <?php echo $currency; ?>" style="display:none">
<div class="falah-wallet-modal-backdrop" id="falah-wallet-receive-backdrop"></div>
<div class="falah-wallet-modal-box">
<div class="falah-wallet-modal-header">
<h4>Receive <?php echo $currency; ?></h4>
<button class="falah-wallet-modal-close" id="falah-wallet-receive-close" aria-label="Close">✕</button>
</div>
<div class="falah-wallet-modal-body falah-wallet-receive-body">
<div class="falah-wallet-qr-placeholder" id="falah-wallet-qr">
<div class="falah-wallet-qr-inner" id="falah-wallet-qr-inner"><div class="falah-spinner"></div></div>
</div>
<div class="falah-wallet-address-box" id="falah-wallet-address-box">
<span id="falah-wallet-address-text" class="falah-wallet-address-text"></span>
<button class="falah-wallet-copy-btn" id="falah-wallet-copy-addr" title="Copy address">
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="7" y="7" width="11" height="11" rx="2"/><path d="M3 13V3h10"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<?php
return ob_get_clean();
} );
// ─── [souq_marketplace] ───────────────────────────────────────────────────────
add_shortcode( 'souq_marketplace', function ( $atts ) {
return falah_iframe_widget( 'Souq Marketplace', '🛒', '/souq', 'Loading Souq Marketplace…' );
} );
// ─── Dashboard page content (shortcode + direct page fill) ───────────────────
add_shortcode( 'falah_dashboard', function ( $atts ) {
falah_sc_enqueue();
ob_start();
?>
<div class="falah-dashboard" id="falah-dashboard">
<div class="falah-dashboard-header">
<h2>Welcome back 👋</h2>
<p class="falah-dashboard-date" id="falah-dash-date"></p>
</div>
<div class="falah-dashboard-grid">
<div class="falah-dash-card falah-dash-prayer">
<div class="falah-dash-card-icon">🕌</div>
<div class="falah-dash-card-body">
<div class="falah-dash-card-label">Next Prayer</div>
<div class="falah-dash-card-value" id="falah-dash-next-prayer">Loading…</div>
<div class="falah-dash-card-sub" id="falah-dash-next-time"></div>
</div>
</div>
<div class="falah-dash-card falah-dash-verse">
<div class="falah-dash-card-icon">📖</div>
<div class="falah-dash-card-body">
<div class="falah-dash-card-label">Today's Verse</div>
<div class="falah-dash-card-value" id="falah-dash-verse">Loading…</div>
</div>
</div>
<div class="falah-dash-card falah-dash-dhikr">
<div class="falah-dash-card-icon">📿</div>
<div class="falah-dash-card-body">
<div class="falah-dash-card-label">Daily Dhikr</div>
<div class="falah-dash-card-value" id="falah-dash-dhikr-count">0</div>
<div class="falah-dash-card-sub">counts today</div>
</div>
</div>
<div class="falah-dash-card falah-dash-hijri">
<div class="falah-dash-card-icon">🗓️</div>
<div class="falah-dash-card-body">
<div class="falah-dash-card-label">Hijri Date</div>
<div class="falah-dash-card-value" id="falah-dash-hijri">Loading…</div>
</div>
</div>
</div>
<div class="falah-dashboard-links">
<a href="/nur" class="falah-dash-link">✨ Nur AI Coach</a>
<a href="/tools" class="falah-dash-link">🛠️ Islamic Tools</a>
<a href="/wallet" class="falah-dash-link">💰 Wallet</a>
<a href="/courses" class="falah-dash-link">🎓 Courses</a>
<a href="/forum" class="falah-dash-link">💬 Forum</a>
<a href="/souq" class="falah-dash-link">🛒 Souq</a>
</div>
</div>
<?php
return ob_get_clean();
} );
// Auto-inject dashboard content into the Dashboard page if it's still empty
add_filter( 'the_content', function ( $content ) {
if ( is_page() ) {
global $post;
$slug = $post->post_name ?? '';
if ( $slug === 'dashboard' && trim( strip_tags( $content ) ) === '' ) {
return do_shortcode( '[falah_dashboard]' );
}
}
return $content;
} );