Task #19: Donation cart (سلة تبرعاتك) for EHSAN POC

Built a real donation cart matching ehsan.sa:
- CartContext provider (localStorage-persisted, defensive parsing) wired into App.tsx
- OpportunityCard: cart icon now adds the case with its typed amount and swaps the
  action row to an added state («مضاف لسلة تبرعاتك» + «إزالة»)
- Header cart button: live count badge + navigates to /cart
- New /cart page + route: breadcrumb, item list (delete icon, category, name,
  editable «قيمة المبلغ» amount, image+progress%), summary panel «الإجمالي» +
  green «للمتابعة للدفع», decorative leaf SVG background, empty state
- translations.ts: parallel AR+EN `cart` section
- donate.tsx: removes the donated case from the cart on successful donation
  (cart reconciliation), preventing stale added-state/badge

Notes/deviations:
- Checkout handoff routes the first cart item into the existing single-case donate
  flow (the POC backend has no multi-item payment). Reconciliation keeps remaining
  items coherent. A true multi-item checkout backend was out of scope.
- Verified with passing e2e test (add → badge → cart page → remove → empty) and
  clean tsc; architect review addressed (reconciliation + defensive parsing).
This commit is contained in:
Replit Agent
2026-06-05 19:54:45 +00:00
parent b37e5fdfdb
commit d0d504bc74
8 changed files with 388 additions and 42 deletions
+11 -6
View File
@@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { LanguageProvider } from "./contexts/LanguageContext";
import { CartProvider } from "./contexts/CartContext";
import { AuthProvider, useAuth } from "./contexts/AuthContext";
import { AppLayout } from "./components/layout/AppLayout";
import NotFound from "@/pages/not-found";
@@ -19,6 +20,7 @@ import Track from "./pages/track";
import ThankYou from "./pages/thank-you";
import WhatsappLog from "./pages/whatsapp-log";
import Login from "./pages/login";
import Cart from "./pages/cart";
const queryClient = new QueryClient();
@@ -39,6 +41,7 @@ function Router() {
<Route path="/request" component={RequestSupport} />
<Route path="/opportunities" component={Opportunities} />
<Route path="/donate/:id" component={Donate} />
<Route path="/cart" component={Cart} />
<Route path="/login" component={Login} />
<Route path="/admin">
<Protected component={Admin} />
@@ -59,12 +62,14 @@ function App() {
<QueryClientProvider client={queryClient}>
<LanguageProvider>
<AuthProvider>
<TooltipProvider>
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
<Router />
</WouterRouter>
<Toaster />
</TooltipProvider>
<CartProvider>
<TooltipProvider>
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
<Router />
</WouterRouter>
<Toaster />
</TooltipProvider>
</CartProvider>
</AuthProvider>
</LanguageProvider>
</QueryClientProvider>