feat: Souq native integration + auth simplification + CE-wide enhancements
Souq Marketplace: - Native souq pages: browse+filters, create listing, detail+seller profile, orders+chat - New API: reviews (POST), sellers/[id], upload (multipart), polar-checkout webhook - Listings API enhanced: minPrice, maxPrice, sortBy, deliveryDays filters - Seller workflow: start order, mark delivered, upload files, confirm delivery - Wallet: 5 Polar.sh FLH tiers, production checkout, mock fallback, success banner - Fix: order redirect /souq/history → /souq/orders Auth System: - Unified auth page, removed OAuth provider routing (2 files deleted) - Deleted oauth.ts (319 lines) — simplified auth chain - Streamlined login/register API routes Prisma Schema (+179 lines): - New models: Listing, Purchase, Review, Group/GroupMember - Gamification: DailyStreak, XpTransaction, Achievement, UserLevel - Learning: LearnCourse/Module/Enrollment/Certificate - Notifications, Referrals, Dhikr, PremiumBenefits Deleted legacy code: - src/app/api/marketplace/* (3 files) — replaced by /api/souq/* - src/app/api/files/[listingId]/route.ts — replaced by /api/souq/upload - src/app/api/seller/[sellerId]/route.ts — replaced by /api/souq/sellers/[id] Infrastructure: - Dockerfile: standalone output, Prisma runtime migration - docker-compose.yml: Polar env vars, healthcheck fix - docker-compose.staging.yml: staging on port 4014 - .gitea/workflows/ci.yml: CI pipeline
This commit is contained in:
+36
-5
@@ -28,9 +28,11 @@ interface CashoutEntry {
|
||||
}
|
||||
|
||||
const TOP_UP_OPTIONS = [
|
||||
{ flh: 500, usd: 4.99, bonus: "" },
|
||||
{ flh: 1100, usd: 9.99, bonus: "+10% bonus" },
|
||||
{ flh: 3000, usd: 24.99, bonus: "+20% bonus" },
|
||||
{ flh: 500, usd: 0.99, bonus: "", bestValue: false },
|
||||
{ flh: 1000, usd: 0.99, bonus: "", bestValue: true },
|
||||
{ flh: 5000, usd: 4.99, bonus: "+500 bonus", bestValue: false },
|
||||
{ flh: 10000, usd: 9.99, bonus: "+2,000 bonus", bestValue: false },
|
||||
{ flh: 50000, usd: 49.99, bonus: "+15,000 bonus", bestValue: false },
|
||||
];
|
||||
|
||||
const EARNING_TIPS = [
|
||||
@@ -54,6 +56,7 @@ export default function WalletPage() {
|
||||
text: string;
|
||||
} | null>(null);
|
||||
const [topupLoading, setTopupLoading] = useState<number | null>(null);
|
||||
const [topupSuccess, setTopupSuccess] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && !token) router.push("/auth");
|
||||
@@ -65,6 +68,21 @@ export default function WalletPage() {
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
// Check for ?topup=success from Polar.sh or mock redirect
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get("topup") === "success") {
|
||||
setTopupSuccess(true);
|
||||
fetchWallet();
|
||||
// Clean URL param without reload
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete("topup");
|
||||
window.history.replaceState({}, "", url.toString());
|
||||
const timer = setTimeout(() => setTopupSuccess(false), 6000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchWallet = async () => {
|
||||
try {
|
||||
const res = await fetch("/mobile/api/wallet", {
|
||||
@@ -235,6 +253,14 @@ export default function WalletPage() {
|
||||
<ErrorFeedback error={cashoutMessage.text} kind="default" onRetry={() => setCashoutMessage(null)} context="wallet" />
|
||||
)}
|
||||
|
||||
{/* Top-up Success Banner */}
|
||||
{topupSuccess && (
|
||||
<div className="flex items-center gap-2 p-3 rounded-xl text-sm bg-emerald-900/20 border border-emerald-800/40 text-emerald-400">
|
||||
<Check size={16} />
|
||||
Top-up successful! FLH has been added to your balance.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Top-Up Section */}
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold text-white mb-3 flex items-center gap-2">
|
||||
@@ -247,8 +273,13 @@ export default function WalletPage() {
|
||||
key={opt.flh}
|
||||
onClick={() => handleTopUp(opt.flh)}
|
||||
disabled={topupLoading === opt.flh}
|
||||
className="bg-[#111118] border border-gray-800/60 rounded-2xl p-5 text-center active:scale-95 transition hover:border-[#D4AF37]/30 disabled:opacity-60 min-h-[100px]"
|
||||
className="bg-[#111118] border border-gray-800/60 rounded-2xl p-4 text-center active:scale-95 transition hover:border-[#D4AF37]/30 disabled:opacity-60 relative"
|
||||
>
|
||||
{opt.bestValue && (
|
||||
<span className="absolute -top-2.5 inset-x-0 mx-auto w-fit px-2 py-0.5 bg-[#D4AF37] text-[#0a0a0f] text-[10px] font-bold rounded-full uppercase tracking-wider">
|
||||
Best Value
|
||||
</span>
|
||||
)}
|
||||
{topupLoading === opt.flh ? (
|
||||
<Loader2 size={20} className="animate-spin mx-auto text-[#D4AF37]" />
|
||||
) : (
|
||||
@@ -260,7 +291,7 @@ export default function WalletPage() {
|
||||
${opt.usd.toFixed(2)}
|
||||
</p>
|
||||
{opt.bonus && (
|
||||
<p className="text-xs text-emerald-400 mt-1 font-medium">
|
||||
<p className="text-[11px] text-emerald-400 mt-1.5 font-semibold">
|
||||
{opt.bonus}
|
||||
</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user