rowboat/apps/x/apps/renderer/src/components/billing-error-dialog.tsx
Ramnique Singh b0000a10d9 feat(x): rowboat config IPC + model recommendations plumbing
- RowboatApiConfig gains optional modelRecommendations (one model per
  provider FLAVOR, native id format) matching the merged backend payload;
  optional so older API deployments and failed fetches never break parsing
- new rowboat:getConfig IPC serves the unauthenticated /v1/config bootstrap
  independent of sign-in (signed-out BYOK users need recommendations when
  connecting a provider); account:getRowboat is back to pure account state
  (signedIn/accessToken) — config is no longer a property of the account
- renderer useRowboatConfig() hook: one shared store for all config
  consumers (appUrl dialogs/sidebar/settings), plus imperative
  fetchRowboatConfig() for event-time consumers (voice, meeting) that must
  await the value when starting a session
- selectInitialModel(flavor, models, recommendations): pure spec-ordered
  initial pick (recommendation if listed, else first, else null) — runs
  only when a provider is first connected, never over a saved choice

Inert plumbing: nothing calls selectInitialModel yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 13:36:20 +05:30

54 lines
1.5 KiB
TypeScript

import { useEffect } from "react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import type { BillingErrorMatch } from "@/lib/billing-error"
import * as analytics from "@/lib/analytics"
import { useRowboatConfig } from "@/hooks/use-rowboat-config"
interface BillingErrorDialogProps {
open: boolean
match: BillingErrorMatch | null
onOpenChange: (open: boolean) => void
}
export function BillingErrorDialog({ open, match, onOpenChange }: BillingErrorDialogProps) {
const appUrl = useRowboatConfig()?.appUrl ?? null
useEffect(() => {
if (open && match) analytics.billingErrorShown(match.kind)
}, [open, match])
if (!match) return null
const handleUpgrade = () => {
analytics.billingUpgradeClicked(match.kind)
if (appUrl) window.open(`${appUrl}?intent=upgrade`)
onOpenChange(false)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{match.title}</DialogTitle>
<DialogDescription>{match.subtitle}</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2 sm:gap-2">
<Button variant="outline" onClick={() => onOpenChange(false)}>
Dismiss
</Button>
<Button onClick={handleUpgrade} disabled={!appUrl}>
{match.cta}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}