From 6c07dcd098e5c094593cbec2bd88287c46055b75 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 9 Jul 2026 18:41:47 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Phase=201=20WP=20integration=20?= =?UTF-8?q?=E2=80=94=20API=20proxy,=20Store,=20Freebies,=20Blog,=20Tools?= =?UTF-8?q?=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add WP REST API proxy (/api/wp/[...path]) - Add Store page ( from Gumroad/Stripe) - Add Freebies page (WP content via REST API) - Add Blog page (WP posts via REST API) - Add Tools page (index of Islamic tools) - Add public nav entries in BottomNav - No WP modifications — all server-side Docs: https://git.falahos.my/falah-os/war-room/wiki/UI-UX-Audit-mobile.falahos.my --- src/app/api/wp/[...path]/route.ts | 44 +++++++ src/app/blog/loading.tsx | 7 ++ src/app/blog/page.tsx | 102 ++++++++++++++++ src/app/freebies/loading.tsx | 7 ++ src/app/freebies/page.tsx | 62 ++++++++++ src/app/store/loading.tsx | 7 ++ src/app/store/page.tsx | 187 ++++++++++++++++++++++++++++++ src/app/tools/page.tsx | 86 ++++++++++++++ src/components/BottomNav.tsx | 7 ++ 9 files changed, 509 insertions(+) create mode 100644 src/app/api/wp/[...path]/route.ts create mode 100644 src/app/blog/loading.tsx create mode 100644 src/app/blog/page.tsx create mode 100644 src/app/freebies/loading.tsx create mode 100644 src/app/freebies/page.tsx create mode 100644 src/app/store/loading.tsx create mode 100644 src/app/store/page.tsx create mode 100644 src/app/tools/page.tsx diff --git a/src/app/api/wp/[...path]/route.ts b/src/app/api/wp/[...path]/route.ts new file mode 100644 index 0000000..a17af6f --- /dev/null +++ b/src/app/api/wp/[...path]/route.ts @@ -0,0 +1,44 @@ +import { NextRequest, NextResponse } from "next/server"; + +const WP_API_BASE = "https://ummah.falahos.my/wp-json"; + +export async function GET( + _request: NextRequest, + { params }: { params: Promise<{ path: string[] }> } +) { + try { + const { path } = await params; + const pathStr = path.join("/"); + const searchParams = _request.nextUrl.searchParams.toString(); + const url = `${WP_API_BASE}/${pathStr}${searchParams ? `?${searchParams}` : ""}`; + + const response = await fetch(url, { + headers: { + "Accept": "application/json", + "User-Agent": "FalahOS-Mobile/1.0", + }, + next: { revalidate: 300 }, // 5 min cache + }); + + if (!response.ok) { + return NextResponse.json( + { error: `WP API error: ${response.statusText}` }, + { status: response.status } + ); + } + + const data = await response.json(); + + return NextResponse.json(data, { + headers: { + "Cache-Control": "public, s-maxage=300, stale-while-revalidate=600", + }, + }); + } catch (error) { + console.error("WP API proxy error:", error); + return NextResponse.json( + { error: "Failed to fetch from WordPress API" }, + { status: 502 } + ); + } +} diff --git a/src/app/blog/loading.tsx b/src/app/blog/loading.tsx new file mode 100644 index 0000000..f911631 --- /dev/null +++ b/src/app/blog/loading.tsx @@ -0,0 +1,7 @@ +export default function Loading() { + return ( +
+
+
+ ); +} diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx new file mode 100644 index 0000000..eaf211a --- /dev/null +++ b/src/app/blog/page.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Newspaper, Calendar, ArrowLeft, ExternalLink } from "lucide-react"; +import Link from "next/link"; + +interface Post { + id: number; + title: string; + slug: string; + excerpt: string; + date: string; + link: string; +} + +export default function BlogPage() { + const [posts, setPosts] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch("/mobile/api/wp/wp/v2/posts?per_page=10&_fields=id,title,slug,excerpt,date,link") + .then((r) => r.json()) + .then((data) => { + if (Array.isArray(data)) { + setPosts( + data.map((p: any) => ({ + id: p.id, + title: p.title?.rendered || "", + slug: p.slug, + excerpt: p.excerpt?.rendered?.replace(/<[^>]+>/g, "") || "", + date: p.date, + link: p.link, + })) + ); + } + setLoading(false); + }) + .catch(() => setLoading(false)); + }, []); + + const formatDate = (d: string) => + new Date(d).toLocaleDateString("en-MY", { + day: "numeric", + month: "long", + year: "numeric", + }); + + return ( +
+
+ + + +
+

+ + Falah Fintech Blog +

+

Islamic fintech insights & updates

+
+
+ + {loading ? ( +
+
+
+ ) : posts.length === 0 ? ( +
+ +

No posts yet

+

Check back soon for updates

+
+ ) : ( + + )} +
+ ); +} diff --git a/src/app/freebies/loading.tsx b/src/app/freebies/loading.tsx new file mode 100644 index 0000000..f911631 --- /dev/null +++ b/src/app/freebies/loading.tsx @@ -0,0 +1,7 @@ +export default function Loading() { + return ( +
+
+
+ ); +} diff --git a/src/app/freebies/page.tsx b/src/app/freebies/page.tsx new file mode 100644 index 0000000..d7bdd70 --- /dev/null +++ b/src/app/freebies/page.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Download, Gift, ExternalLink, ArrowLeft } from "lucide-react"; +import Link from "next/link"; + +export default function FreebiesPage() { + const [content, setContent] = useState(""); + const [title, setTitle] = useState("Free Islamic Resources"); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch("/mobile/api/wp/wp/v2/pages?slug=freebies") + .then((r) => r.json()) + .then((data) => { + if (Array.isArray(data) && data.length > 0) { + const page = data[0]; + setTitle(page.title?.rendered || "Free Islamic Resources"); + setContent(page.content?.rendered || ""); + } + setLoading(false); + }) + .catch(() => setLoading(false)); + }, []); + + return ( +
+
+ + + +
+

+ + {title} +

+

Free downloads & resources

+
+
+ + {loading ? ( +
+
+
+ ) : ( +
+ {content ? ( +
+ ) : ( +
+ +

Loading resources...

+
+ )} +
+ )} +
+ ); +} diff --git a/src/app/store/loading.tsx b/src/app/store/loading.tsx new file mode 100644 index 0000000..f911631 --- /dev/null +++ b/src/app/store/loading.tsx @@ -0,0 +1,7 @@ +export default function Loading() { + return ( +
+
+
+ ); +} diff --git a/src/app/store/page.tsx b/src/app/store/page.tsx new file mode 100644 index 0000000..f63fbd2 --- /dev/null +++ b/src/app/store/page.tsx @@ -0,0 +1,187 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { ShoppingBag, ExternalLink, Star, Download, ArrowLeft } from "lucide-react"; +import Link from "next/link"; + +interface Product { + id: string; + name: string; + description: string; + price: string; + currency: string; + image: string; + url: string; +} + +export default function StorePage() { + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const wpContent = sessionStorage.getItem("wp_store_content"); + if (wpContent) { + try { + setProducts(JSON.parse(wpContent)); + setLoading(false); + return; + } catch {} + } + + fetch("/mobile/api/wp/wp/v2/pages?slug=store") + .then((r) => r.json()) + .then((data) => { + if (Array.isArray(data) && data.length > 0) { + const page = data[0]; + const parser = new DOMParser(); + const doc = parser.parseFromString(page.content?.rendered || "", "text/html"); + const productElements = doc.querySelectorAll(".edd_download, .product, .wp-block-column"); + const parsed: Product[] = Array.from(productElements).map((el, i) => ({ + id: `wp-${i}`, + name: el.querySelector("h3, .edd_download_title, strong")?.textContent || `Product ${i + 1}`, + description: el.querySelector("p, .edd_download_excerpt")?.textContent || "", + price: el.querySelector(".edd_price, .amount, .price")?.textContent || "$—", + currency: "USD", + image: el.querySelector("img")?.getAttribute("src") || "", + url: el.querySelector("a")?.getAttribute("href") || "#", + })); + setProducts(parsed.length > 0 ? parsed : getDefaultProducts()); + } else { + setProducts(getDefaultProducts()); + } + setLoading(false); + }) + .catch(() => { + setProducts(getDefaultProducts()); + setLoading(false); + }); + }, []); + + return ( +
+ {/* Header */} +
+ + + +
+

+ + Digital Store +

+

Islamic digital products

+
+
+ + {loading ? ( +
+
+
+ ) : error ? ( +
+

{error}

+
+ ) : ( +
+ {products.length === 0 && ( +
+ +

No products available yet

+
+ )} + {products.map((product) => ( +
+ {product.image && ( + {product.name} + )} +
+
+

{product.name}

+ {product.price} +
+ {product.description && ( +

{product.description}

+ )} +
+ + Digital + + + {product.currency} + +
+ + Buy Now + +
+
+ ))} +
+ )} +
+ ); +} + +function getDefaultProducts(): Product[] { + return [ + { + id: "gumroad-1", + name: "Halal Business Plan Pro", + description: "Comprehensive halal business plan template with financial projections, market analysis, and Islamic finance compliance checklist.", + price: "$12.00", + currency: "USD", + image: "", + url: "https://alfalahtech.gumroad.com/l/halal-business-plan-pro", + }, + { + id: "gumroad-2", + name: "Faraid Inheritance Workbook", + description: "Step-by-step workbook for Islamic inheritance planning (Faraid). Includes calculation sheets, distribution charts, and family worksheets.", + price: "$10.00", + currency: "USD", + image: "", + url: "https://alfalahtech.gumroad.com/l/faraid-inheritance-workbook", + }, + { + id: "gumroad-3", + name: "30-Day Quran Journal", + description: "Structured daily Quran journal with reflection prompts, Arabic verses, translation, and space for personal notes — spanning 30 days.", + price: "$7.00", + currency: "USD", + image: "", + url: "https://alfalahtech.gumroad.com/l/30-day-quran-journal", + }, + { + id: "gumroad-4", + name: "Islamic Budgeting Bundle", + description: "Bundle of budgeting templates aligned with Islamic finance principles: monthly budget, debt tracker, savings goal planner, and zakat calculator.", + price: "$9.00", + currency: "USD", + image: "", + url: "https://alfalahtech.gumroad.com/l/islamic-budgeting-bundle", + }, + { + id: "gumroad-5", + name: "Islamic Social Templates 50pk", + description: "50 premium social media templates for Islamic content creators. Includes Instagram posts, stories, and Facebook covers in PSD and Canva formats.", + price: "$10.00", + currency: "USD", + image: "", + url: "https://alfalahtech.gumroad.com/l/islamic-social-templates-50pk", + }, + ]; +} diff --git a/src/app/tools/page.tsx b/src/app/tools/page.tsx new file mode 100644 index 0000000..9c26c4a --- /dev/null +++ b/src/app/tools/page.tsx @@ -0,0 +1,86 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Compass, CircleDot, BookOpen, MapPin, ArrowLeft, Wrench } from "lucide-react"; +import Link from "next/link"; + +const tools = [ + { + href: "/prayer", + icon: Compass, + label: "Prayer Times", + desc: "Accurate prayer times based on your location", + color: "bg-emerald-900/30 text-emerald-300", + public: true, + }, + { + href: "/qibla", + icon: MapPin, + label: "Qibla Finder", + desc: "Find direction to the Kaaba", + color: "bg-amber-900/30 text-amber-300", + public: false, + }, + { + href: "/dhikr", + icon: CircleDot, + label: "Dhikr Counter", + desc: "Digital tasbih with daily tracking", + color: "bg-sky-900/30 text-sky-300", + public: false, + }, + { + href: "/learn", + icon: BookOpen, + label: "Learning Hub", + desc: "Islamic courses and modules", + color: "bg-purple-900/30 text-purple-300", + public: false, + }, +]; + +export default function ToolsPage() { + return ( +
+
+ + + +
+

+ + Islamic Tools +

+

Essential daily tools

+
+
+ +
+ {tools.map((tool) => ( + +
+
+ +
+
+

+ {tool.label} + {!tool.public && ( + + login required + + )} +

+

{tool.desc}

+
+
+ + ))} +
+
+ ); +} diff --git a/src/components/BottomNav.tsx b/src/components/BottomNav.tsx index 5e87c27..1908ac0 100644 --- a/src/components/BottomNav.tsx +++ b/src/components/BottomNav.tsx @@ -18,10 +18,17 @@ import { X, Home, GraduationCap, + Wrench, + Gift, + Newspaper, } from "lucide-react"; // All destinations except Home (Home stays in center nav) const overflowItems = [ + { href: "/store", label: "Store", icon: ShoppingBag, color: "text-[#D4AF37]", bg: "bg-[#D4AF37]/15" }, + { href: "/freebies", label: "Freebies", icon: Star, color: "text-emerald-400", bg: "bg-emerald-900/20" }, + { href: "/blog", label: "Blog", icon: MessageCircle, color: "text-sky-400", bg: "bg-sky-900/20" }, + { href: "/tools", label: "Tools", icon: Wrench, color: "text-amber-400", bg: "bg-amber-900/20" }, { href: "/nur", label: "Nur AI", icon: Bot, color: "text-amber-400", bg: "bg-amber-900/20" }, { href: "/prayer", label: "Prayer", icon: Clock, color: "text-emerald-400", bg: "bg-emerald-900/20" }, { href: "/forum", label: "Forum", icon: MessageCircle, color: "text-sky-400", bg: "bg-sky-900/20" },