import { useState } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/shared/common/ui/card"; import { Input } from "@/shared/common/ui/input"; import { Button } from "@/shared/common/ui/button"; import { AlertTriangle, CheckCircle2, RefreshCw, Save } from "lucide-react"; import { useExchangeSettingsQuery, useSetExchangeFallbackRate, } from "@/hooks/useExchangeSettings"; import type { ExchangeRateSource } from "@/services/exchangeSettings.service"; /** Feed health, phrased for an operator rather than a developer. */ function feedLabel(source: ExchangeRateSource | null): { live: boolean; text: string; } { switch (source) { case "live": return { live: true, text: "CBE reachable — using the live rate" }; case "stored": return { live: false, text: "CBE unreachable — using the fallback rate below", }; default: return { live: true, text: "No rate requested yet since the last restart" }; } } const formatTime = (value: string | null) => value ? new Date(value).toLocaleString() : "never"; /** * USD→ETB fallback used when the CBE exchange-rate endpoint is unreachable. * The live CBE rate always wins; every successful fetch overwrites the stored * value, so it tracks the last known good rate on its own. Editing here is for * a prolonged outage — the next successful CBE fetch replaces it. */ export default function ExchangeRateSettingsCard() { const { data, isLoading, refetch, isFetching } = useExchangeSettingsQuery(); const setRate = useSetExchangeFallbackRate(); const [draft, setDraft] = useState(""); const value = draft !== "" ? draft : (data?.fallbackRate?.toString() ?? ""); const parsed = Number(value); const invalid = !Number.isFinite(parsed) || parsed < 1 || parsed > 10_000; const dirty = draft !== "" && parsed !== data?.fallbackRate; const feed = feedLabel(data?.feed?.source ?? null); const handleSave = async () => { if (invalid) return; await setRate.mutateAsync(parsed); setDraft(""); }; return (
Exchange rate (USD → ETB) Rates come from the Commercial Bank of Ethiopia. The fallback below is used only when CBE cannot be reached, and is refreshed automatically after every successful update.
{feed.live ? ( ) : ( )}

{feed.text}

{data?.feed?.rate != null && (

Rate in use: {data.feed.rate} ETB per USD

)}

Last successful update: {formatTime(data?.feed?.lastSuccessAt ?? null)}

{data?.feed?.lastError && (

Last error: {data.feed.lastError}

)}
setDraft(e.target.value)} />
{invalid && draft !== "" && (

Enter a rate between 1 and 10,000.

)}

{data?.fallbackSource === "MANUAL" ? "Set manually. The next successful CBE update will replace it." : `Synced automatically from CBE (${formatTime( data?.lastSyncedAt ?? null, )}).`}

); }