Files
edr-platform/apps/edr-freight-web/backoffice/src/pages/settings/ManualPaymentSettingsCard.tsx

133 lines
4.7 KiB
TypeScript

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/shared/common/ui/card";
import { Badge } from "@/shared/common/ui/badge";
import { Switch } from "@/shared/common/ui/switch";
import { Skeleton } from "@/shared/common/ui/skeleton";
import { AlertTriangle, Banknote, Landmark } from "lucide-react";
import { useAuth } from "@/auth/useAuth";
import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions";
import {
useManualPaymentSettingsQuery,
useUpdateManualPaymentSettings,
} from "@/hooks/useManualPaymentSettings";
type Currency = "ETB" | "USD";
const CURRENCIES: {
code: Currency;
field: "etbEnabled" | "usdEnabled";
icon: typeof Banknote;
title: string;
description: string;
}[] = [
{
code: "ETB",
field: "etbEnabled",
icon: Banknote,
title: "Birr (ETB) invoices",
description:
"ETB invoices are normally paid online by the customer. Switch this on when Finance also needs to settle them by hand — a bank transfer or a payment at the counter.",
},
{
code: "USD",
field: "usdEnabled",
icon: Landmark,
title: "Dollar (USD) invoices",
description:
"USD invoices are paid by bank transfer and have no online channel. Switching this off leaves USD customers with no way to be marked as paid.",
},
];
/**
* Switches the manual (offline) payment channel on or off per currency.
*
* Off means gone, not greyed out: the Manual Payments worklist lists only
* enabled currencies, and the API refuses a confirmation in a disabled one —
* so a stale tab or a direct call cannot slip a payment through.
*/
export default function ManualPaymentSettingsCard() {
const { user } = useAuth();
const canManage =
hasPermission(user, FREIGHT_PERMS.settings.manualPayment.manage) ||
hasPermission(user, FREIGHT_PERMS.admin);
const { data, isLoading } = useManualPaymentSettingsQuery();
const update = useUpdateManualPaymentSettings();
const noneEnabled = Boolean(data && !data.etbEnabled && !data.usdEnabled);
return (
<Card className="shadow-lg border-gray-200 dark:border-gray-700">
<CardHeader>
<CardTitle>Manual payments</CardTitle>
<CardDescription>
Whether Finance staff may mark invoices as paid by hand, from
Invoices Manual Payments. Each currency is switched separately.
Confirming still requires the payment slip and the booking&apos;s pay
window to be open.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{noneEnabled && (
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100">
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
<p>
Both currencies are off the Manual Payments list is empty and
Finance cannot settle any invoice by hand.
</p>
</div>
)}
{isLoading || !data
? CURRENCIES.map((c) => (
<Skeleton key={c.code} className="h-[86px] w-full rounded-md" />
))
: CURRENCIES.map(({ code, field, icon: Icon, title, description }) => {
const enabled = data[field];
return (
<div
key={code}
className="flex items-start justify-between gap-4 rounded-md border p-4 dark:border-gray-700"
>
<div className="space-y-1">
<div className="flex items-center gap-2">
<Icon className="h-4 w-4 text-muted-foreground" />
<p className="font-medium">{title}</p>
<Badge variant={enabled ? "default" : "secondary"}>
{enabled ? "Enabled" : "Disabled"}
</Badge>
</div>
<p className="text-sm text-muted-foreground">
{description}
</p>
</div>
<Switch
checked={enabled}
disabled={!canManage || update.isPending}
aria-label={`Allow manual payment for ${code} invoices`}
onCheckedChange={(checked) =>
update.mutate({ [field]: checked })
}
/>
</div>
);
})}
{!canManage && (
<p className="text-sm text-muted-foreground">
You can see these settings but not change them that needs the
manual-payment settings permission.
</p>
)}
</CardContent>
</Card>
);
}