feat: Phase 1 WP integration — API proxy, Store, Freebies, Blog, Tools pages
Deploy Staging / build (push) Failing after 9m32s
Deploy Staging / build (push) Failing after 9m32s
- 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
This commit is contained in:
@@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default function Loading() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] flex items-center justify-center">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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<Post[]>([]);
|
||||||
|
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 (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] pb-24">
|
||||||
|
<div className="px-4 pt-6 pb-4 flex items-center gap-3">
|
||||||
|
<Link href="/" className="w-9 h-9 rounded-full bg-gray-800/50 flex items-center justify-center active:opacity-60 transition">
|
||||||
|
<ArrowLeft size={18} className="text-gray-400" />
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-bold text-white flex items-center gap-2">
|
||||||
|
<Newspaper size={18} className="text-sky-400" />
|
||||||
|
Falah Fintech Blog
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs text-gray-500">Islamic fintech insights & updates</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center justify-center py-20">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : posts.length === 0 ? (
|
||||||
|
<div className="text-center py-10 px-4">
|
||||||
|
<Newspaper size={40} className="mx-auto text-gray-700 mb-3" />
|
||||||
|
<p className="text-sm text-gray-500">No posts yet</p>
|
||||||
|
<p className="text-xs text-gray-700 mt-1">Check back soon for updates</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-4 space-y-3">
|
||||||
|
{posts.map((post) => (
|
||||||
|
<a
|
||||||
|
key={post.id}
|
||||||
|
href={post.link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block bg-[#111118] border border-gray-800/60 rounded-2xl p-4 active:scale-[0.98] transition"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-2">
|
||||||
|
<h2 className="text-sm font-semibold text-white flex-1 pr-4">
|
||||||
|
{post.title}
|
||||||
|
</h2>
|
||||||
|
<ExternalLink size={14} className="text-gray-600 shrink-0 mt-1" />
|
||||||
|
</div>
|
||||||
|
{post.excerpt && (
|
||||||
|
<p className="text-xs text-gray-500 mb-3 line-clamp-2">{post.excerpt}</p>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-2 text-[10px] text-gray-600">
|
||||||
|
<Calendar size={10} />
|
||||||
|
{formatDate(post.date)}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default function Loading() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] flex items-center justify-center">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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<string>("");
|
||||||
|
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 (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] pb-24">
|
||||||
|
<div className="px-4 pt-6 pb-4 flex items-center gap-3">
|
||||||
|
<Link href="/" className="w-9 h-9 rounded-full bg-gray-800/50 flex items-center justify-center active:opacity-60 transition">
|
||||||
|
<ArrowLeft size={18} className="text-gray-400" />
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-bold text-white flex items-center gap-2">
|
||||||
|
<Gift size={18} className="text-emerald-400" />
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs text-gray-500">Free downloads & resources</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center justify-center py-20">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-4">
|
||||||
|
{content ? (
|
||||||
|
<div
|
||||||
|
className="prose prose-invert max-w-none text-sm text-gray-300 [&_a]:text-[#D4AF37] [&_h2]:text-white [&_h3]:text-white [&_ul]:space-y-2 [&_li]:text-gray-400 [&_img]:rounded-xl"
|
||||||
|
dangerouslySetInnerHTML={{ __html: content }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-10">
|
||||||
|
<Download size={40} className="mx-auto text-gray-700 mb-3" />
|
||||||
|
<p className="text-sm text-gray-500">Loading resources...</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default function Loading() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] flex items-center justify-center">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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<Product[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(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 (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] pb-24">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="px-4 pt-6 pb-4 flex items-center gap-3">
|
||||||
|
<Link href="/" className="w-9 h-9 rounded-full bg-gray-800/50 flex items-center justify-center active:opacity-60 transition">
|
||||||
|
<ArrowLeft size={18} className="text-gray-400" />
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-bold text-white flex items-center gap-2">
|
||||||
|
<ShoppingBag size={18} className="text-[#D4AF37]" />
|
||||||
|
Digital Store
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs text-gray-500">Islamic digital products</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center justify-center py-20">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-[#D4AF37]/30 border-t-[#D4AF37] animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="mx-4 p-5 bg-red-900/20 border border-red-800/30 rounded-2xl text-center">
|
||||||
|
<p className="text-sm text-red-400">{error}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-4 space-y-4">
|
||||||
|
{products.length === 0 && (
|
||||||
|
<div className="text-center py-10">
|
||||||
|
<ShoppingBag size={40} className="mx-auto text-gray-700 mb-3" />
|
||||||
|
<p className="text-sm text-gray-500">No products available yet</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{products.map((product) => (
|
||||||
|
<div
|
||||||
|
key={product.id}
|
||||||
|
className="bg-[#111118] border border-gray-800/60 rounded-2xl overflow-hidden active:scale-[0.98] transition"
|
||||||
|
>
|
||||||
|
{product.image && (
|
||||||
|
<img
|
||||||
|
src={product.image}
|
||||||
|
alt={product.name}
|
||||||
|
className="w-full h-40 object-cover"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="flex items-start justify-between mb-2">
|
||||||
|
<h3 className="text-sm font-semibold text-white flex-1">{product.name}</h3>
|
||||||
|
<span className="text-sm font-bold text-[#D4AF37] ml-2">{product.price}</span>
|
||||||
|
</div>
|
||||||
|
{product.description && (
|
||||||
|
<p className="text-xs text-gray-500 mb-3 line-clamp-2">{product.description}</p>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-[10px] px-2 py-1 rounded-full bg-emerald-900/30 text-emerald-300 border border-emerald-700/30 flex items-center gap-1">
|
||||||
|
<Download size={10} /> Digital
|
||||||
|
</span>
|
||||||
|
<span className="text-[10px] px-2 py-1 rounded-full bg-[#D4AF37]/15 text-[#D4AF37] border border-[#D4AF37]/30">
|
||||||
|
{product.currency}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={product.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="mt-3 w-full py-3 bg-gradient-to-r from-[#D4AF37] to-amber-500 text-[#0a0a0f] text-sm font-semibold rounded-xl flex items-center justify-center gap-2 active:scale-[0.98] transition min-h-[44px]"
|
||||||
|
>
|
||||||
|
<ExternalLink size={14} /> Buy Now
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<div className="min-h-dvh bg-[#0a0a0f] pb-24">
|
||||||
|
<div className="px-4 pt-6 pb-4 flex items-center gap-3">
|
||||||
|
<Link href="/" className="w-9 h-9 rounded-full bg-gray-800/50 flex items-center justify-center active:opacity-60 transition">
|
||||||
|
<ArrowLeft size={18} className="text-gray-400" />
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-bold text-white flex items-center gap-2">
|
||||||
|
<Wrench size={18} className="text-gray-400" />
|
||||||
|
Islamic Tools
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs text-gray-500">Essential daily tools</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-4 space-y-3">
|
||||||
|
{tools.map((tool) => (
|
||||||
|
<Link
|
||||||
|
key={tool.href}
|
||||||
|
href={tool.href}
|
||||||
|
className="block bg-[#111118] border border-gray-800/60 rounded-2xl p-4 active:scale-[0.98] transition"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`w-12 h-12 rounded-xl ${tool.color} flex items-center justify-center`}>
|
||||||
|
<tool.icon size={22} />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-sm font-semibold text-white flex items-center gap-2">
|
||||||
|
{tool.label}
|
||||||
|
{!tool.public && (
|
||||||
|
<span className="text-[9px] px-1.5 py-0.5 rounded-full bg-gray-800 text-gray-500">
|
||||||
|
login required
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-gray-500 mt-0.5">{tool.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -18,10 +18,17 @@ import {
|
|||||||
X,
|
X,
|
||||||
Home,
|
Home,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
|
Wrench,
|
||||||
|
Gift,
|
||||||
|
Newspaper,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
// All destinations except Home (Home stays in center nav)
|
// All destinations except Home (Home stays in center nav)
|
||||||
const overflowItems = [
|
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: "/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: "/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" },
|
{ href: "/forum", label: "Forum", icon: MessageCircle, color: "text-sky-400", bg: "bg-sky-900/20" },
|
||||||
|
|||||||
Reference in New Issue
Block a user