diff --git a/artifacts/ehsan-poc/public/opengraph.jpg b/artifacts/ehsan-poc/public/opengraph.jpg index 07511bb..875fb79 100644 Binary files a/artifacts/ehsan-poc/public/opengraph.jpg and b/artifacts/ehsan-poc/public/opengraph.jpg differ diff --git a/artifacts/ehsan-poc/src/App.tsx b/artifacts/ehsan-poc/src/App.tsx index ca028f6..3e78fb4 100644 --- a/artifacts/ehsan-poc/src/App.tsx +++ b/artifacts/ehsan-poc/src/App.tsx @@ -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() { + @@ -59,12 +62,14 @@ function App() { - - - - - - + + + + + + + + diff --git a/artifacts/ehsan-poc/src/components/OpportunityCard.tsx b/artifacts/ehsan-poc/src/components/OpportunityCard.tsx index 8a3c098..4a9a0dd 100644 --- a/artifacts/ehsan-poc/src/components/OpportunityCard.tsx +++ b/artifacts/ehsan-poc/src/components/OpportunityCard.tsx @@ -1,9 +1,10 @@ import { useState } from "react"; import { useLocation } from "wouter"; import { useLanguage } from "../contexts/LanguageContext"; +import { useCart } from "../contexts/CartContext"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Share2, ShoppingCart } from "lucide-react"; +import { Share2, ShoppingCart, Check, Trash2 } from "lucide-react"; import { getNeedImage } from "../lib/needImages"; import { Riyal } from "@/components/Riyal"; @@ -21,8 +22,11 @@ interface OpportunityCardProps { export function OpportunityCard({ request }: OpportunityCardProps) { const { t } = useLanguage(); const [, setLocation] = useLocation(); + const { addItem, removeItem, isInCart } = useCart(); const [amount, setAmount] = useState(""); + const inCart = isInCart(request.id); + const progress = Math.min( 100, request.requestedAmount > 0 @@ -38,6 +42,20 @@ export function OpportunityCard({ request }: OpportunityCardProps) { setLocation(`/donate/${request.id}${suffix}`); }; + const handleAddToCart = () => { + addItem( + { + id: request.id, + caseId: request.caseId, + description: request.description, + needType: request.needType, + requestedAmount: request.requestedAmount, + collectedAmount: request.collectedAmount, + }, + Number(amount) + ); + }; + return (
{/* Photo + progress bar */} @@ -89,43 +107,64 @@ export function OpportunityCard({ request }: OpportunityCardProps) {
- {/* Donate row */} -
- - -
- - + + + {t.cart.added} - setAmount(e.target.value)} - placeholder={t.opportunities.amountPlaceholder} - className="ps-8 h-full" - disabled={isComplete} - data-testid={`input-cardAmount-${request.id}`} - /> +
-
+ ) : ( +
+ + +
+ + + + setAmount(e.target.value)} + placeholder={t.opportunities.amountPlaceholder} + className="ps-8 h-full" + disabled={isComplete} + data-testid={`input-cardAmount-${request.id}`} + /> +
+
+ )} ); diff --git a/artifacts/ehsan-poc/src/components/layout/Header.tsx b/artifacts/ehsan-poc/src/components/layout/Header.tsx index 1f72deb..472010b 100644 --- a/artifacts/ehsan-poc/src/components/layout/Header.tsx +++ b/artifacts/ehsan-poc/src/components/layout/Header.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from "react"; import { Link, useLocation } from "wouter"; import { useLanguage } from "../../contexts/LanguageContext"; import { useAuth } from "../../contexts/AuthContext"; +import { useCart } from "../../contexts/CartContext"; import { Button } from "../ui/button"; import { Search, @@ -29,6 +30,7 @@ import ehsanLogo from "../../assets/ehsan-logo.png"; export function Header() { const { language, setLanguage, t } = useLanguage(); const { isAuthenticated, logout } = useAuth(); + const { count: cartCount } = useCart(); const [location, setLocation] = useLocation(); const [mobileOpen, setMobileOpen] = useState(false); const [servicesOpen, setServicesOpen] = useState(false); @@ -218,11 +220,20 @@ export function Header() { + + + ) : ( +
+ {/* Items list */} +
+ {items.map((item) => { + const progress = Math.min( + 100, + item.requestedAmount > 0 + ? Math.round((item.collectedAmount / item.requestedAmount) * 100) + : 0 + ); + return ( + +
+ {/* Image + progress */} +
+ {item.description} +
+
+ {progress}% +
+
+
+ + {/* Content */} +
+
+ + {t.needTypes[item.needType as keyof typeof t.needTypes] || + t.opportunities.generalProjects} + + +
+ +

+ {item.description} +

+ +
+ + + + 0 ? item.amount : ""} + onChange={(e) => updateAmount(item.id, Number(e.target.value))} + placeholder={t.cart.amountLabel} + className="ps-8" + data-testid={`input-amount-${item.id}`} + /> +
+
+
+
+ ); + })} +
+ + {/* Summary panel */} +
+
+
+ {t.cart.total} + + {total.toLocaleString()} + +
+ +
+
+
+ )} + + + ); +} diff --git a/artifacts/ehsan-poc/src/pages/donate.tsx b/artifacts/ehsan-poc/src/pages/donate.tsx index 1e1e947..a89c09b 100644 --- a/artifacts/ehsan-poc/src/pages/donate.tsx +++ b/artifacts/ehsan-poc/src/pages/donate.tsx @@ -4,6 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { useParams, useLocation, useSearch, Link } from "wouter"; import { useLanguage } from "../contexts/LanguageContext"; +import { useCart } from "../contexts/CartContext"; import { useGetRequest, useDonateToRequest, getListRequestsQueryKey, getListPublishedRequestsQueryKey, getGetRequestQueryKey, @@ -35,6 +36,7 @@ export default function Donate() { const search = useSearch(); const [, setLocation] = useLocation(); const queryClient = useQueryClient(); + const { removeItem: removeFromCart } = useCart(); const initialAmount = (() => { const a = Number(new URLSearchParams(search).get("amount")); @@ -140,6 +142,7 @@ export default function Donate() { queryClient.invalidateQueries({ queryKey: getListRequestsQueryKey() }); queryClient.invalidateQueries({ queryKey: getListPublishedRequestsQueryKey() }); queryClient.invalidateQueries({ queryKey: getGetRequestQueryKey(params.id || "") }); + removeFromCart(params.id || ""); setDonated(true); }, }