diff --git a/ui/app/page.tsx b/ui/app/page.tsx index da8ffba3..bd01094c 100644 --- a/ui/app/page.tsx +++ b/ui/app/page.tsx @@ -10,6 +10,12 @@ import { TemporaryBalances } from '@/components/temporary-balances'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import type { DisplayUnit } from '@/lib/types/units'; import { fetchBtcUsdPrice, btcToSatsRate } from '@/lib/exchange-rate'; +import { AdminService } from '@/lib/api/services/admin'; +import { InitialSetupOnboarding } from '@/components/initial-setup-onboarding'; + +type AdminSettingsResponse = Record & { + admin_password?: string | null; +}; export default function Page() { const [displayUnit, setDisplayUnit] = useState('sat'); @@ -21,6 +27,38 @@ export default function Page() { staleTime: 60_000, }); + const { + data: settingsData, + isLoading: settingsLoading, + isError: settingsError, + } = useQuery({ + queryKey: ['admin-settings'], + queryFn: async () => (await AdminService.getSettings()) as AdminSettingsResponse, + staleTime: 60_000, + }); + + const { + data: upstreamProviders, + isLoading: providersLoading, + isError: providersError, + } = useQuery({ + queryKey: ['upstream-providers'], + queryFn: AdminService.getUpstreamProviders, + staleTime: 60_000, + }); + + const adminPasswordValue = + typeof settingsData?.admin_password === 'string' ? settingsData.admin_password : ''; + const hasAdminPassword = adminPasswordValue.trim().length > 0; + const hasUpstreamProviders = (upstreamProviders?.length ?? 0) > 0; + const shouldShowOnboarding = + !settingsLoading && + !providersLoading && + !settingsError && + !providersError && + !hasAdminPassword && + !hasUpstreamProviders; + const usdPerSat = btcUsdPrice ? btcToSatsRate(btcUsdPrice) : null; useEffect(() => { @@ -37,12 +75,8 @@ export default function Page() {
-

- Admin Dashboard -

-

- Monitor and manage wallet balances -

+

Admin Dashboard

+

Monitor and manage wallet balances

+ {shouldShowOnboarding && ( +
+ +
+ )} +
step.completed).length; + const progress = (completedSteps / criticalSteps.length) * 100; + + return ( + + + + Initial setup + + Finish onboarding your Routstr node + + Lock down the admin dashboard, add an upstream provider, and review the remaining + configuration so routing works end-to-end. + + + +
+
+ + {completedSteps} of {criticalSteps.length} critical steps complete + + {Math.round(progress)}% +
+ +
+ +
    + {criticalSteps.map((step, index) => { + const Icon = step.icon; + const StatusIcon = step.completed ? CheckCircle2 : Circle; + return ( +
  1. +
    +
    + + {index + 1} + +
    +
    + +

    {step.title}

    +
    +

    {step.description}

    +

    {step.helper}

    +
    +
    +
    +
    + + {step.completed ? 'Completed' : 'Action required'} +
    + +
    +
    +
  2. + ); + })} +
+ +
+ {focusAreas.map((area) => { + const Icon = area.icon; + return ( +
+
+ + {area.title} +
+

{area.description}

+
    + {area.bullets.map((bullet) => ( +
  • + • + {bullet} +
  • + ))} +
+ +
+ ); + })} +
+ +
+

How the flow works

+

+ Routstr reserves the maximum cost for each request up front, relays it to the selected + upstream provider, and then reconciles the actual token usage when the response returns. + Keeping an admin password, provider credentials, and Cashu balances in sync ensures every + client request can be authorized and settled automatically. +

+

+ As soon as you set the password and register a provider this onboarding guide disappears + automatically. +

+
+
+
+ ); +}