diff --git a/surfsense_web/app/dashboard/[workspace_id]/user-settings/components/PurchaseHistoryContent.tsx b/surfsense_web/app/dashboard/[workspace_id]/user-settings/components/PurchaseHistoryContent.tsx index 263a286c1..0056b9374 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/user-settings/components/PurchaseHistoryContent.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/user-settings/components/PurchaseHistoryContent.tsx @@ -108,9 +108,8 @@ function normalizeCreditPurchase(p: CreditPurchase): UnifiedPurchase { function formatGranted(p: UnifiedPurchase): string { if (p.kind === "credits") { const dollars = p.granted / 1_000_000; - // Credit packs are always whole dollars at the moment, but future - // fractional grants (refunds, partial top-ups, auto-reload) shouldn't - // silently round to "$0". + // Credit packs are always whole dollars today, but future fractional grants + // such as refunds or low-balance refills shouldn't silently round to "$0". if (dollars >= 1) return `$${dollars.toFixed(2)} of credit`; if (dollars > 0) return `$${dollars.toFixed(3)} of credit`; return "$0 of credit"; diff --git a/surfsense_web/components/layout/ui/sidebar/CreditBalanceDisplay.tsx b/surfsense_web/components/layout/ui/sidebar/CreditBalanceDisplay.tsx index 1d45137fb..81a939b21 100644 --- a/surfsense_web/components/layout/ui/sidebar/CreditBalanceDisplay.tsx +++ b/surfsense_web/components/layout/ui/sidebar/CreditBalanceDisplay.tsx @@ -27,7 +27,7 @@ function formatUsd(micros: number): string { * premium-credit meters. Values come from Zero (live-replicated from Postgres) * as integer micro-USD (1_000_000 == $1.00). A low-balance warning highlights * the amount when it falls below $0.50 so the user knows to top up or enable - * auto-reload. + * automatic top-ups. */ export function CreditBalanceDisplay() { const isAnonymous = useIsAnonymous(); @@ -46,7 +46,7 @@ export function CreditBalanceDisplay() { "font-medium tabular-nums", isLow ? "text-amber-600 dark:text-amber-500" : "text-foreground" )} - title={isLow ? "Low balance — buy credits or enable auto-reload" : undefined} + title={isLow ? "Low balance: add credits or enable top-ups" : undefined} > {formatUsd(balanceMicros)} diff --git a/surfsense_web/components/pricing/pricing-section.tsx b/surfsense_web/components/pricing/pricing-section.tsx index cc7d81307..74c76ad36 100644 --- a/surfsense_web/components/pricing/pricing-section.tsx +++ b/surfsense_web/components/pricing/pricing-section.tsx @@ -41,7 +41,7 @@ const demoPlans = [ "Premium models like GPT-5.5, Claude Sonnet 5, Gemini 3.1 Pro billed at provider cost", "Scheduled and event-triggered agents for briefs, alerts, and monitoring", "Write results back to Notion, Slack, Linear, and Jira", - "Top up any amount, $1 buys $1 of credit, optional auto-reload", + "Add credit any time. $1 buys $1 of credit, with optional automatic refills", "Priority support on Discord", ], description: "", @@ -95,7 +95,7 @@ const faqData: FAQSection[] = [ { question: "How does Pay As You Go work?", answer: - "There is no monthly subscription. Start with $5 of free credit, and when you need more, top up any amount. $1 buys exactly $1 of credit, added to your balance immediately. You can enable auto-reload to top up automatically when your balance runs low, and turn it off any time.", + "There is no monthly subscription. Start with $5 of free credit, and when you need more, add any amount. $1 buys exactly $1 of credit, added to your balance immediately. You can enable automatic refills when your balance runs low, and turn them off any time." }, { question: "What happens if I run out of credit?", diff --git a/surfsense_web/components/settings/auto-reload-settings.tsx b/surfsense_web/components/settings/auto-reload-settings.tsx index b11a64353..ede826dd0 100644 --- a/surfsense_web/components/settings/auto-reload-settings.tsx +++ b/surfsense_web/components/settings/auto-reload-settings.tsx @@ -2,15 +2,15 @@ import { useQuery as useZeroQuery } from "@rocicorp/zero/react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { AlertTriangle, CreditCard, RefreshCw } from "lucide-react"; +import { AlertTriangle, Info } from "lucide-react"; import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { toast } from "sonner"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; import { Spinner } from "@/components/ui/spinner"; import { Switch } from "@/components/ui/switch"; import { stripeApiService } from "@/lib/apis/stripe-api.service"; @@ -69,7 +69,7 @@ export function AutoReloadSettings() { const setupResult = searchParams.get("auto_reload_setup"); if (!setupResult) return; if (setupResult === "success") { - toast.success("Card saved. You can now enable auto-reload."); + toast.success("Card saved. You can now enable top-ups."); queryClient.invalidateQueries({ queryKey: ["auto-reload-settings"] }); } else if (setupResult === "cancel") { toast.info("Card setup canceled."); @@ -92,19 +92,19 @@ export function AutoReloadSettings() { mutationFn: stripeApiService.updateAutoReloadSettings, onSuccess: (updated) => { queryClient.setQueryData(["auto-reload-settings"], updated); - toast.success(updated.enabled ? "Auto-reload is on." : "Auto-reload settings saved."); + toast.success(updated.enabled ? "Top-ups are on." : "Top-up settings saved."); }, onError: (error) => { if (error instanceof AppError && error.message) { toast.error(error.message); return; } - toast.error("Couldn't save auto-reload settings. Please try again."); + toast.error("Couldn't save top-up settings. Please try again."); }, }); // Render nothing while loading (avoids a spinner flash on pages where the - // feature flag turns out to be off) and when auto-reload is disabled + // feature flag turns out to be off) and when top-ups are disabled // server-side. if (isLoading || !settings || !settings.feature_enabled) { return null; @@ -131,7 +131,7 @@ export function AutoReloadSettings() { return; } if (amountMicros == null || amountMicros < settings.min_amount_micros) { - toast.error(`Reload amount must be at least $${minAmountDollars}.`); + toast.error(`Top-up amount must be at least $${minAmountDollars}.`); return; } @@ -142,135 +142,168 @@ export function AutoReloadSettings() { }); }; - return ( - - - - - Auto-reload - - - Automatically top up your credit balance when it drops below a threshold, using a saved - card. Current balance:{" "} - {formatUsd(balanceMicros)}. - - - + const addCardButton = ( + + ); + + if (!hasCard) { + return ( +
{settings.failed_at && ( - Last auto-reload failed + Last top-up failed - Your saved card was declined and auto-reload was turned off. Update your card and - re-enable it below to keep topping up automatically. + Your saved card was declined and top-ups were turned off. Update your card and + re-enable top-ups below. )} - {!hasCard ? ( -
-
- - Add a card to enable automatic top-ups. +
+ +
+ +

+ Automatically top up your credit balance when it drops below a threshold, using a + saved card. Current balance:{" "} + {formatUsd(balanceMicros)}. +

- + {addCardButton} +
+ +
+
+ ); + } + + return ( +
+ {settings.failed_at && ( + + + Last top-up failed + + Your saved card was declined and top-ups were turned off. Update your card and + re-enable top-ups below. + + + )} + +
+
+
+

Automatic top-ups

+

+ Current balance:{" "} + {formatUsd(balanceMicros)} +

- ) : ( - <> -
-
- -

- Charge your saved card when the balance gets low. -

-
- -
+ +
-
-
- -
- - $ - - setThresholdInput(e.target.value)} - disabled={!enabled} - placeholder="5" - /> -
-
-
- -
- - $ - - setAmountInput(e.target.value)} - disabled={!enabled} - placeholder="10" - /> -
-

Minimum ${minAmountDollars}.

-
-
+
+
+ +

+ Charge your saved card when the balance gets low. +

+
+ +
-
- - +
+
+ +
+ + $ + + setThresholdInput(e.target.value)} + disabled={!enabled} + placeholder="5" + />
- - )} - - +
+
+ +
+ + $ + + setAmountInput(e.target.value)} + disabled={!enabled} + placeholder="10" + /> +
+

Minimum ${minAmountDollars}.

+
+
+ +
+ +
+
+ + +
); }