The Live Trading control stored its mode in localStorage, so the shell could claim live money while the bot was in dry-run. Show the Tradingbot status when TRADING_BOT_URL is set, and an unknown state when it is not. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import { Trans } from "@lingui/react/macro";
|
|
import { cn } from "@rakazo/ui-web";
|
|
import { Activity } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
import {
|
|
presentTradingStatus,
|
|
type TradingBotUiState,
|
|
type TradingModeLabelId,
|
|
type TradingModeSubtitle,
|
|
} from "../lib/trading-bot-status";
|
|
|
|
export function TradingModeIndicator({
|
|
snapshot,
|
|
className,
|
|
}: {
|
|
snapshot: TradingBotUiState;
|
|
className?: string;
|
|
}) {
|
|
const presentation = presentTradingStatus(snapshot);
|
|
return (
|
|
<div
|
|
role="status"
|
|
aria-live="polite"
|
|
aria-busy={presentation.label === "loading"}
|
|
data-testid="trading-mode-status"
|
|
data-trading-state={presentation.label}
|
|
className={cn("flex min-w-0 items-center gap-3 rounded-xl px-2.5 py-2", className)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"grid h-[30px] w-[30px] shrink-0 place-items-center rounded-lg",
|
|
presentation.tone === "live"
|
|
? "bg-warning/10 text-warning"
|
|
: "bg-muted text-muted-foreground",
|
|
)}
|
|
>
|
|
<Activity
|
|
strokeWidth={1.8}
|
|
aria-hidden="true"
|
|
className={cn("size-icon-md", presentation.pulse && "animate-pulse")}
|
|
/>
|
|
</span>
|
|
<span className="min-w-0">
|
|
<span className="block truncate text-ui-body font-medium text-foreground/90">
|
|
<TradingModeLabel label={presentation.label} />
|
|
</span>
|
|
{presentation.subtitle ? (
|
|
<span className="block truncate text-ui-body-sm text-muted-foreground/70">
|
|
<TradingModeSubtitleText subtitle={presentation.subtitle} />
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TradingModeLabel({ label }: { label: TradingModeLabelId }): ReactNode {
|
|
switch (label) {
|
|
case "loading":
|
|
return <Trans>Trading</Trans>;
|
|
case "unconfigured":
|
|
return <Trans>Not configured</Trans>;
|
|
case "offline":
|
|
return <Trans>Offline</Trans>;
|
|
case "unknown":
|
|
return <Trans>Unknown</Trans>;
|
|
case "live":
|
|
return <Trans>Live Trading</Trans>;
|
|
case "paper":
|
|
return <Trans>Paper</Trans>;
|
|
case "dry_run":
|
|
return <Trans>Dry run</Trans>;
|
|
default: {
|
|
const exhaustive: never = label;
|
|
return exhaustive;
|
|
}
|
|
}
|
|
}
|
|
|
|
function TradingModeSubtitleText({ subtitle }: { subtitle: TradingModeSubtitle }): ReactNode {
|
|
switch (subtitle.type) {
|
|
case "error":
|
|
return subtitle.text;
|
|
case "adapters":
|
|
return subtitle.count === 1 ? (
|
|
<Trans>1 adapter</Trans>
|
|
) : (
|
|
<Trans>{subtitle.count} adapters</Trans>
|
|
);
|
|
default: {
|
|
const exhaustive: never = subtitle;
|
|
return exhaustive;
|
|
}
|
|
}
|
|
}
|