mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-24 21:41:08 +02:00
- 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>
54 lines
1.5 KiB
TypeScript
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>
|
|
)
|
|
}
|