- routeUi.* (123 keys): RouteDialog (incl. follow-mode banner, transit time picker + mode filters, itinerary legs), StopDepartures, TripItineraryCard. - sasquatchUi.* (81 keys): SasquatchGuide, SasquatchProDialog, SasquatchActionCard, SasquatchAiStatusCard. LLM prompts + chat bodies deliberately left English. - collectionUi.* / bootUi.*: AddToCollectionDialog example placeholders, SplashScreen rotating status lines + aria labels. - New src/i18n/batch3/* fragment files, spread into en/es so parallel passes never edit the big literal.
65 lines
2.6 KiB
TypeScript
65 lines
2.6 KiB
TypeScript
import { AlertTriangle, Coins, Loader2 } from 'lucide-react';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|
import { PLATFORM_OWNER_PUBKEYS } from '@/hooks/useListingRestrictions';
|
|
import { useSasquatchAiStatus } from '@/hooks/useSasquatchAiStatus';
|
|
import { useTranslation } from '@/contexts/language';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
/**
|
|
* Owner-only card: the Sasquatch AI (Routstr node) balance, with a warning when
|
|
* it's running low. This is the "top up before AI degrades" nudge — no webhook
|
|
* needed, since the operator can see it live in Settings.
|
|
*/
|
|
export function SasquatchAiStatusCard() {
|
|
const { t } = useTranslation();
|
|
const { user } = useCurrentUser();
|
|
const isOwner = !!user?.pubkey && PLATFORM_OWNER_PUBKEYS.includes(user.pubkey);
|
|
const { data, isLoading, isError } = useSasquatchAiStatus();
|
|
|
|
if (!isOwner) return null;
|
|
|
|
const low = data?.low === true;
|
|
const balance = data?.balanceSats;
|
|
|
|
return (
|
|
<Card className={cn('overflow-hidden bg-white/2', low ? 'border-amber-500/40' : 'border-white/10')}>
|
|
<CardContent className="py-3.5">
|
|
<div className="flex items-center justify-between gap-3">
|
|
<p className="flex items-center gap-2 text-sm font-bold text-white">
|
|
{low ? (
|
|
<AlertTriangle className="h-4 w-4 text-amber-400" />
|
|
) : (
|
|
<Coins className="h-4 w-4 text-red-400" />
|
|
)}
|
|
{t('sasquatchUi.aiTitle')}
|
|
<span className="rounded bg-red-500/10 px-1.5 py-0.5 text-[9px] font-black uppercase tracking-widest text-red-400/70">
|
|
{t('sasquatchUi.owner')}
|
|
</span>
|
|
</p>
|
|
<span
|
|
className={cn(
|
|
'shrink-0 text-xs font-bold',
|
|
isError ? 'text-gray-500' : low ? 'text-amber-400' : 'text-green-400',
|
|
)}
|
|
>
|
|
{isLoading ? (
|
|
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
) : isError || data?.configured === false ? (
|
|
t('sasquatchUi.notConfigured')
|
|
) : balance === null || balance === undefined ? (
|
|
t('sasquatchUi.unavailable')
|
|
) : (
|
|
t('sasquatchUi.satsValue', { balance: balance.toLocaleString() })
|
|
)}
|
|
</span>
|
|
</div>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
{low
|
|
? t('sasquatchUi.lowBalance', { provider: data?.provider ?? t('sasquatchUi.theNode') })
|
|
: t('sasquatchUi.aiDesc')}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|