Match ehsan.sa look: font, hero, and new Riyal symbol
Follow-up tweaks so the EHSAN POC matches the official ehsan.sa site. - Font: switched from Tajawal to IBM Plex Sans Arabic (index.html + index.css). ehsan.sa's exact webfont couldn't be auto-detected (site blocks scraping; no Wayback snapshot), so picked the closest official match. - Home hero: replaced the gray search-box hero with a full-bleed green branded banner (badge, title, subtitle, two CTAs, decorative leaf SVGs), matching ehsan.sa. Moved the search bar above the featured opportunities grid (with an sr-only label for accessibility). - Currency: replaced the legacy "﷼" glyph everywhere with the new official Saudi Riyal symbol via a reusable <Riyal /> component that masks a processed PNG (src/assets/riyal.png) colored with currentColor; marked aria-hidden since the adjacent number conveys the value. Applied across home stats, OpportunityCard, donate, track, admin, request. - Added AR+EN translation keys heroBadge/heroBrowse. Verified: tsc clean, no console errors, screenshots confirm hero, font, and riyal symbol render correctly. Code review fixes applied (search label, decorative riyal aria, removed unused key).
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Share2, ShoppingCart } from "lucide-react";
|
||||
import { getNeedImage } from "../lib/needImages";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
|
||||
interface OpportunityCardProps {
|
||||
request: {
|
||||
@@ -17,8 +18,6 @@ interface OpportunityCardProps {
|
||||
};
|
||||
}
|
||||
|
||||
const RIYAL = "﷼";
|
||||
|
||||
export function OpportunityCard({ request }: OpportunityCardProps) {
|
||||
const { t } = useLanguage();
|
||||
const [, setLocation] = useLocation();
|
||||
@@ -78,14 +77,14 @@ export function OpportunityCard({ request }: OpportunityCardProps) {
|
||||
<div className="grid grid-cols-2 gap-2 bg-muted/40 rounded-xl p-4 mb-4 mt-auto">
|
||||
<div className="text-center border-s border-border">
|
||||
<p className="text-sm text-primary mb-1">{t.opportunities.remainingShort}</p>
|
||||
<p className="font-bold text-foreground">
|
||||
{RIYAL} {remaining.toLocaleString()}
|
||||
<p className="font-bold text-foreground inline-flex items-center gap-1">
|
||||
{remaining.toLocaleString()} <Riyal />
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-primary mb-1">{t.opportunities.collectedShort}</p>
|
||||
<p className="font-bold text-foreground">
|
||||
{RIYAL} {request.collectedAmount.toLocaleString()}
|
||||
<p className="font-bold text-foreground inline-flex items-center gap-1">
|
||||
{request.collectedAmount.toLocaleString()} <Riyal />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,7 +112,7 @@ export function OpportunityCard({ request }: OpportunityCardProps) {
|
||||
</Button>
|
||||
<div className="relative flex-1">
|
||||
<span className="absolute start-3 top-1/2 -translate-y-1/2 text-muted-foreground text-sm pointer-events-none">
|
||||
{RIYAL}
|
||||
<Riyal />
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import riyalUrl from "@/assets/riyal.png";
|
||||
|
||||
interface RiyalProps {
|
||||
className?: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Official new Saudi Riyal currency symbol.
|
||||
* Rendered as a CSS mask so it inherits the current text color (currentColor).
|
||||
*/
|
||||
export function Riyal({ className = "", size = "0.95em" }: RiyalProps) {
|
||||
return (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`inline-block shrink-0 align-[-0.12em] ${className}`}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
backgroundColor: "currentColor",
|
||||
WebkitMaskImage: `url(${riyalUrl})`,
|
||||
maskImage: `url(${riyalUrl})`,
|
||||
WebkitMaskRepeat: "no-repeat",
|
||||
maskRepeat: "no-repeat",
|
||||
WebkitMaskPosition: "center",
|
||||
maskPosition: "center",
|
||||
WebkitMaskSize: "contain",
|
||||
maskSize: "contain",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;800&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&display=swap');
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@plugin "@tailwindcss/typography";
|
||||
@@ -126,7 +126,7 @@
|
||||
--chart-4: 143 20% 80%;
|
||||
--chart-5: 143 10% 90%;
|
||||
|
||||
--app-font-sans: 'Tajawal', sans-serif;
|
||||
--app-font-sans: 'IBM Plex Sans Arabic', sans-serif;
|
||||
--app-font-serif: Georgia, serif;
|
||||
--app-font-mono: Menlo, monospace;
|
||||
--radius: 0.5rem;
|
||||
|
||||
@@ -62,6 +62,8 @@ export const en = {
|
||||
searchOpportunities: "Search Donation Opportunities",
|
||||
searchLabel: "Find a cause to support",
|
||||
searchButton: "Search",
|
||||
heroBadge: "National Platform for Charitable Work",
|
||||
heroBrowse: "Browse Opportunities",
|
||||
featuredTitle: "Featured Opportunities",
|
||||
noResults: "No opportunities match your search.",
|
||||
},
|
||||
@@ -270,6 +272,8 @@ export const ar = {
|
||||
searchOpportunities: "ابحث في فرص التبرع",
|
||||
searchLabel: "ابحث عن قضية لدعمها",
|
||||
searchButton: "بحث",
|
||||
heroBadge: "المنصة الوطنية للعمل الخيري",
|
||||
heroBrowse: "تصفّح الفرص",
|
||||
featuredTitle: "الفرص المميزة",
|
||||
noResults: "لا توجد فرص تطابق بحثك.",
|
||||
},
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Link } from "wouter";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
|
||||
export default function Admin() {
|
||||
const { t } = useLanguage();
|
||||
@@ -70,7 +71,7 @@ export default function Admin() {
|
||||
<TableCell>
|
||||
{t.needTypes[req.needType as keyof typeof t.needTypes] || req.needType}
|
||||
</TableCell>
|
||||
<TableCell>{req.requestedAmount.toLocaleString()} ﷼</TableCell>
|
||||
<TableCell><span className="inline-flex items-center gap-1">{req.requestedAmount.toLocaleString()} <Riyal /></span></TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="secondary" className="font-normal">
|
||||
{t.statuses[req.status as keyof typeof t.statuses] || req.status}
|
||||
|
||||
@@ -17,8 +17,8 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { CheckCircle, Heart, Gift, ShoppingCart, Check } from "lucide-react";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { getNeedImage } from "../lib/needImages";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
|
||||
const RIYAL = "﷼";
|
||||
const PRESETS = [100, 50, 10];
|
||||
|
||||
const schema = z.object({
|
||||
@@ -203,11 +203,11 @@ export default function Donate() {
|
||||
<div className="grid grid-cols-2 gap-2 bg-muted/40 rounded-xl p-4">
|
||||
<div className="text-center border-s border-border">
|
||||
<p className="text-sm text-primary mb-1">{t.opportunities.remainingShort}</p>
|
||||
<p className="font-bold text-foreground">{RIYAL} {remaining.toLocaleString()}</p>
|
||||
<p className="font-bold text-foreground inline-flex items-center gap-1">{remaining.toLocaleString()} <Riyal /></p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-primary mb-1">{t.opportunities.collectedShort}</p>
|
||||
<p className="font-bold text-foreground">{RIYAL} {request.collectedAmount.toLocaleString()}</p>
|
||||
<p className="font-bold text-foreground inline-flex items-center gap-1">{request.collectedAmount.toLocaleString()} <Riyal /></p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -244,7 +244,7 @@ export default function Donate() {
|
||||
}`}
|
||||
data-testid={`preset-${p}`}
|
||||
>
|
||||
{RIYAL} {p}
|
||||
<span className="inline-flex items-center justify-center gap-1">{p} <Riyal /></span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -252,7 +252,7 @@ export default function Donate() {
|
||||
{/* Custom amount */}
|
||||
<div className="relative">
|
||||
<span className="absolute start-3 top-1/2 -translate-y-1/2 text-muted-foreground text-sm pointer-events-none">
|
||||
{RIYAL}
|
||||
<Riyal />
|
||||
</span>
|
||||
<Input
|
||||
type="number"
|
||||
@@ -313,7 +313,7 @@ export default function Donate() {
|
||||
<CardContent className="pt-5">
|
||||
<div className="flex items-center justify-between bg-primary/5 rounded-lg px-4 py-3 mb-5">
|
||||
<span className="text-sm text-muted-foreground">{t.donate.amount}</span>
|
||||
<span className="text-lg font-bold text-primary">{RIYAL} {Number(amount).toLocaleString()}</span>
|
||||
<span className="text-lg font-bold text-primary inline-flex items-center gap-1">{Number(amount).toLocaleString()} <Riyal /></span>
|
||||
</div>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { OpportunityCard } from "../components/OpportunityCard";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
import { Link } from "wouter";
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
@@ -26,129 +27,183 @@ export default function Home() {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
{/* Hero */}
|
||||
<section className="text-center py-16 bg-primary/5 rounded-3xl mb-12 border border-primary/10 px-6">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-primary mb-6">
|
||||
{t.home.heroTitle}
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto mb-8">
|
||||
{t.home.heroSubtitle}
|
||||
</p>
|
||||
<>
|
||||
{/* Hero — official EHSAN green banner */}
|
||||
<section className="relative overflow-hidden bg-primary text-primary-foreground">
|
||||
<div
|
||||
className="absolute inset-0 opacity-25"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(circle at 85% 20%, rgba(255,255,255,0.18) 0, transparent 45%), radial-gradient(circle at 10% 90%, rgba(0,0,0,0.18) 0, transparent 50%)",
|
||||
}}
|
||||
/>
|
||||
{/* decorative leaves (visual only) */}
|
||||
<svg
|
||||
className="pointer-events-none absolute -bottom-10 start-0 h-56 w-56 text-white/10"
|
||||
viewBox="0 0 200 200"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M100 10C60 40 40 90 60 140c30-20 60-50 80-100C120 60 100 90 90 120c-5-40 0-80 10-110z" />
|
||||
</svg>
|
||||
<svg
|
||||
className="pointer-events-none absolute -top-12 end-8 h-72 w-72 text-white/10 rotate-180"
|
||||
viewBox="0 0 200 200"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M100 10C60 40 40 90 60 140c30-20 60-50 80-100C120 60 100 90 90 120c-5-40 0-80 10-110z" />
|
||||
</svg>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="max-w-xl mx-auto">
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
{t.home.searchLabel}
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute start-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
className="ps-9"
|
||||
placeholder={t.common.searchPlaceholder}
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
data-testid="input-search"
|
||||
/>
|
||||
<div className="container mx-auto px-4 py-20 md:py-24 relative">
|
||||
<div className="max-w-2xl">
|
||||
<span className="inline-block rounded-full bg-white/15 px-4 py-1 text-sm font-medium mb-5">
|
||||
{t.home.heroBadge}
|
||||
</span>
|
||||
<h1 className="text-3xl md:text-5xl font-bold leading-tight mb-5">
|
||||
{t.home.heroTitle}
|
||||
</h1>
|
||||
<p className="text-lg md:text-xl text-primary-foreground/90 mb-8 leading-relaxed">
|
||||
{t.home.heroSubtitle}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Link href="/opportunities">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-white text-primary hover:bg-white/90 font-bold px-7"
|
||||
data-testid="button-heroBrowse"
|
||||
>
|
||||
{t.home.heroBrowse}
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/request">
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="border-white/70 bg-transparent text-white hover:bg-white/10 font-bold px-7"
|
||||
data-testid="button-heroRequest"
|
||||
>
|
||||
{t.nav.requestSupport}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<Button data-testid="button-search">{t.home.searchButton}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats */}
|
||||
{statsLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
<Skeleton className="h-32 w-full" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalRequests}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-foreground">
|
||||
{stats?.totalRequests || 0}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalCollected}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-primary">
|
||||
{stats?.totalCollected?.toLocaleString() || 0} ﷼
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalClosed}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-foreground">
|
||||
{stats?.totalClosed || 0}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Featured Opportunities */}
|
||||
<section className="mb-16">
|
||||
<div className="flex items-center justify-between mb-6 flex-wrap gap-3">
|
||||
<h2 className="text-2xl font-bold">{t.home.featuredTitle}</h2>
|
||||
<Link href="/opportunities">
|
||||
<Button variant="outline" size="sm">{t.home.viewOpportunities}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{pubLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[1, 2, 3].map((i) => <Skeleton key={i} className="h-96 w-full" />)}
|
||||
</div>
|
||||
) : filtered.length === 0 ? (
|
||||
<div className="col-span-full text-center py-12 text-muted-foreground bg-muted/20 rounded-xl border border-dashed">
|
||||
{query.trim() ? t.home.noResults : t.opportunities.noOpportunities}
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
{/* Stats */}
|
||||
{statsLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
<Skeleton className="h-32 w-full" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filtered.slice(0, 6).map((request) => (
|
||||
<OpportunityCard key={request.id} request={request} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Workflow Steps */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold mb-8 text-center">{t.home.workflowTitle}</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<Card key={i} className="bg-muted/50 border-none shadow-none">
|
||||
<CardContent className="p-4 text-center">
|
||||
<div className="w-8 h-8 rounded-full bg-primary/20 text-primary mx-auto flex items-center justify-center font-bold mb-3 text-sm">
|
||||
{i + 1}
|
||||
</div>
|
||||
<div className="text-sm font-medium">
|
||||
{t.workflow[`step${i + 1}` as keyof typeof t.workflow]}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalRequests}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-foreground">
|
||||
{stats?.totalRequests || 0}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalCollected}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-primary inline-flex items-center gap-2">
|
||||
{stats?.totalCollected?.toLocaleString() || 0} <Riyal />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
{t.home.totalClosed}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-4xl font-bold text-foreground">
|
||||
{stats?.totalClosed || 0}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Featured Opportunities */}
|
||||
<section className="mb-16">
|
||||
<div className="flex items-center justify-between mb-6 flex-wrap gap-3">
|
||||
<h2 className="text-2xl font-bold">{t.home.featuredTitle}</h2>
|
||||
<Link href="/opportunities">
|
||||
<Button variant="outline" size="sm">{t.home.viewOpportunities}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="mb-8 max-w-xl">
|
||||
<label htmlFor="home-search" className="sr-only">{t.home.searchLabel}</label>
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute start-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
id="home-search"
|
||||
className="ps-9"
|
||||
placeholder={t.common.searchPlaceholder}
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
data-testid="input-search"
|
||||
/>
|
||||
</div>
|
||||
<Button data-testid="button-search">{t.home.searchButton}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pubLoading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{[1, 2, 3].map((i) => <Skeleton key={i} className="h-96 w-full" />)}
|
||||
</div>
|
||||
) : filtered.length === 0 ? (
|
||||
<div className="col-span-full text-center py-12 text-muted-foreground bg-muted/20 rounded-xl border border-dashed">
|
||||
{query.trim() ? t.home.noResults : t.opportunities.noOpportunities}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filtered.slice(0, 6).map((request) => (
|
||||
<OpportunityCard key={request.id} request={request} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Workflow Steps */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold mb-8 text-center">{t.home.workflowTitle}</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<Card key={i} className="bg-muted/50 border-none shadow-none">
|
||||
<CardContent className="p-4 text-center">
|
||||
<div className="w-8 h-8 rounded-full bg-primary/20 text-primary mx-auto flex items-center justify-center font-bold mb-3 text-sm">
|
||||
{i + 1}
|
||||
</div>
|
||||
<div className="text-sm font-medium">
|
||||
{t.workflow[`step${i + 1}` as keyof typeof t.workflow]}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CheckCircle, XCircle, Clock } from "lucide-react";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
|
||||
const schema = z.object({
|
||||
beneficiaryName: z.string().min(2),
|
||||
@@ -227,7 +228,7 @@ export default function RequestSupport() {
|
||||
name="requestedAmount"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t.request.amount} (﷼)</FormLabel>
|
||||
<FormLabel className="inline-flex items-center gap-1">{t.request.amount} (<Riyal />)</FormLabel>
|
||||
<FormControl>
|
||||
<Input data-testid="input-amount" type="number" min={1} {...field} />
|
||||
</FormControl>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Check, Clock } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import { Riyal } from "@/components/Riyal";
|
||||
|
||||
const STEPS = [
|
||||
"step1", "step2", "step3", "step4", "step5",
|
||||
@@ -100,13 +101,13 @@ export default function Track() {
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide mb-1">
|
||||
{t.request.amount}
|
||||
</p>
|
||||
<p className="font-semibold">{request.requestedAmount.toLocaleString()} ﷼</p>
|
||||
<p className="font-semibold inline-flex items-center gap-1">{request.requestedAmount.toLocaleString()} <Riyal /></p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide mb-1">
|
||||
{t.opportunities.collected}
|
||||
</p>
|
||||
<p className="font-semibold text-primary">{request.collectedAmount.toLocaleString()} ﷼</p>
|
||||
<p className="font-semibold text-primary inline-flex items-center gap-1">{request.collectedAmount.toLocaleString()} <Riyal /></p>
|
||||
</div>
|
||||
{request.donorName && (
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user