8519202949
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).
155 lines
6.3 KiB
TypeScript
155 lines
6.3 KiB
TypeScript
import { useLanguage } from "../contexts/LanguageContext";
|
|
import {
|
|
useListRequests,
|
|
getListRequestsQueryKey,
|
|
useVerifyRequest,
|
|
usePublishRequest,
|
|
useDeliverSupport,
|
|
useConfirmReceipt,
|
|
useCloseRequest,
|
|
useRejectRequest,
|
|
} from "@workspace/api-client-react";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
Table, TableBody, TableCell, TableHead,
|
|
TableHeader, TableRow,
|
|
} from "@/components/ui/table";
|
|
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();
|
|
const queryClient = useQueryClient();
|
|
const { data: requests, isLoading } = useListRequests();
|
|
|
|
const verifyRequest = useVerifyRequest();
|
|
const publishRequest = usePublishRequest();
|
|
const deliverSupport = useDeliverSupport();
|
|
const confirmReceipt = useConfirmReceipt();
|
|
const closeRequest = useCloseRequest();
|
|
const rejectRequest = useRejectRequest();
|
|
|
|
const handleAction = async (action: any, id: string) => {
|
|
try {
|
|
await action.mutateAsync({ id });
|
|
queryClient.invalidateQueries({ queryKey: getListRequestsQueryKey() });
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="container mx-auto px-4 py-12">
|
|
<h1 className="text-3xl font-bold text-foreground mb-8">{t.admin.title}</h1>
|
|
|
|
<div className="bg-card rounded-xl border overflow-hidden shadow-sm">
|
|
<Table>
|
|
<TableHeader className="bg-muted/50">
|
|
<TableRow>
|
|
<TableHead>{t.admin.caseId}</TableHead>
|
|
<TableHead>{t.admin.beneficiary}</TableHead>
|
|
<TableHead>{t.admin.needType}</TableHead>
|
|
<TableHead>{t.admin.amount}</TableHead>
|
|
<TableHead>{t.admin.status}</TableHead>
|
|
<TableHead>{t.admin.currentStep}</TableHead>
|
|
<TableHead className="text-right">{t.admin.actions}</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{isLoading ? (
|
|
<TableRow>
|
|
<TableCell colSpan={7} className="text-center py-8 text-muted-foreground">
|
|
{t.common.loading}
|
|
</TableCell>
|
|
</TableRow>
|
|
) : requests?.map((req) => (
|
|
<TableRow key={req.id}>
|
|
<TableCell className="font-mono text-xs">{req.caseId}</TableCell>
|
|
<TableCell>{req.beneficiaryName}</TableCell>
|
|
<TableCell>
|
|
{t.needTypes[req.needType as keyof typeof t.needTypes] || req.needType}
|
|
</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}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>{req.currentStep}/10</TableCell>
|
|
<TableCell className="text-right">
|
|
<div className="flex items-center justify-end gap-2 flex-wrap">
|
|
<Link href={`/track/${req.id}`}>
|
|
<Button variant="outline" size="sm">{t.admin.track}</Button>
|
|
</Link>
|
|
{req.status === "new" && (
|
|
<>
|
|
<Button size="sm" onClick={() => handleAction(verifyRequest, req.id)}>
|
|
{t.admin.verify}
|
|
</Button>
|
|
<Button size="sm" variant="destructive" onClick={() => handleAction(rejectRequest, req.id)}>
|
|
{t.admin.reject}
|
|
</Button>
|
|
</>
|
|
)}
|
|
{req.status === "pending_review" && (
|
|
<>
|
|
<Button size="sm" onClick={() => handleAction(verifyRequest, req.id)}>
|
|
{t.admin.verify}
|
|
</Button>
|
|
<Button size="sm" variant="destructive" onClick={() => handleAction(rejectRequest, req.id)}>
|
|
{t.admin.reject}
|
|
</Button>
|
|
</>
|
|
)}
|
|
{req.status === "verified" && (
|
|
<Button size="sm" onClick={() => handleAction(publishRequest, req.id)}>
|
|
{t.admin.publish}
|
|
</Button>
|
|
)}
|
|
{req.status === "donated" && (
|
|
<Button size="sm" onClick={() => handleAction(deliverSupport, req.id)}>
|
|
{t.admin.deliver}
|
|
</Button>
|
|
)}
|
|
{req.status === "delivered" && (
|
|
<Button size="sm" onClick={() => handleAction(confirmReceipt, req.id)}>
|
|
{t.admin.confirmReceipt}
|
|
</Button>
|
|
)}
|
|
{req.status === "receipt_confirmed" && (
|
|
<Link href={`/thank-you/${req.id}`}>
|
|
<Button size="sm" variant="outline">
|
|
{t.track.submitThankYou}
|
|
</Button>
|
|
</Link>
|
|
)}
|
|
{req.status === "thank_you_submitted" && (
|
|
<Link href="/whatsapp-log">
|
|
<Button size="sm" variant="outline">{t.admin.whatsapp}</Button>
|
|
</Link>
|
|
)}
|
|
{req.status === "whatsapp_sent" && (
|
|
<Button size="sm" onClick={() => handleAction(closeRequest, req.id)}>
|
|
{t.admin.close}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{(!requests || requests.length === 0) && !isLoading && (
|
|
<TableRow>
|
|
<TableCell colSpan={7} className="text-center py-8 text-muted-foreground">
|
|
{t.admin.noRequests}
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|