diff --git a/ui/app/page.tsx b/ui/app/page.tsx index de744045..a3686a94 100644 --- a/ui/app/page.tsx +++ b/ui/app/page.tsx @@ -155,30 +155,28 @@ export default function DashboardPage() { - - Last Hour - Last 6 Hours - Last 24 Hours - Last 3 Days - Last Week - Last Month - Last Year - - + + Last Hour + Last 6 Hours + Last 24 Hours + Last 3 Days + Last Week + Last Month + Last Year + - - - - - - -
+ {providers.map((provider) => { + const modelsContent = expandedProviders.has(provider.id) && ( +
+ {isLoadingModels && viewingModels === provider.id ? (
- {provider.api_version && ( -
- - API Version: - - - {provider.api_version} - -
- )} + +
- - {expandedProviders.has(provider.id) && ( -
- {isLoadingModels && - viewingModels === provider.id ? ( -
- - -
- ) : providerModels && - viewingModels === provider.id ? ( - 0 - ? 'provided' - : 'custom' - } - className='w-full' + ) : providerModels && viewingModels === provider.id ? ( + 0 + ? 'provided' + : 'custom' + } + className='w-full' + > + + + + Provided Models + + Provided + - - - - Provided Models - - Provided - - {providerModels.remote_models.length} - - - - - Custom Models - - Custom - - {providerModels.db_models.length} - - - - + + + + Custom Models + + Custom + + {providerModels.db_models.length} + + + + +
+ {providerModels.db_models.length > 0 && ( +
+ Custom models override or extend the + provider's catalog. +
+ )} +
+ + +
+
+ {providerModels.db_models.length === 0 ? ( +
+ No custom models configured +
+ ) : ( +
+ {providerModels.db_models.map((model) => ( +
+
+
+ + {model.id} + + + {model.enabled + ? 'Enabled' + : 'Disabled'} + +
+
+ {model.description || model.name} +
+
+
+
+ {model.context_length?.toLocaleString()}{' '} + tokens
- )} -
- {providerModels.db_models.length === 0 ? ( -
- No custom models configured -
- ) : ( -
- {providerModels.db_models.map((model) => ( -
-
-
- - {model.id} - - - {model.enabled - ? 'Enabled' - : 'Disabled'} - -
-
- {model.description || model.name} -
-
-
-
- {model.context_length?.toLocaleString()}{' '} - tokens -
- - -
-
- ))} -
- )} - - + )} + + +
+ These models are provided directly by the upstream + service. +
+
+ {providerModels.remote_models.map((model) => ( +
- {providerModels.remote_models.length > 0 ? ( - <> -
- Models automatically discovered from the - provider's catalog. -
-
- {providerModels.remote_models.map( - (model) => ( -
-
-
- {model.id} -
-
- {model.description || - model.name} -
-
-
-
- {model.context_length?.toLocaleString()}{' '} - tokens -
- -
-
- ) - )} -
- - ) : ( -
- No provided models available +
+
+ + {model.id} +
- )} - - - ) : null} +
+ {model.name} +
+
+
+
+ {model.context_length?.toLocaleString()}{' '} + tokens +
+ +
+
+ ))} +
+ + + ) : null} +
+ ); + + if (provider.provider_type === 'routstr') { + return ( + + toggleProviderExpansion(provider.id) + } + onEdit={() => handleEdit(provider)} + onDelete={() => handleDelete(provider.id)} + balanceComponent={ + + } + > + +
+
+ {provider.api_version && ( +
+ + API Version: + + + {provider.api_version} + +
+ )} +
+ {modelsContent}
- )} -
- - - ))} + + + ); + } + + return ( + + +
+
+
+ + {provider.provider_type} + + + {provider.enabled ? 'Enabled' : 'Disabled'} + +
+ + {provider.base_url} + +
+
+ {canShowBalance(provider.provider_type) && + provider.api_key && ( +
+ +
+ )} + + + +
+
+
+ +
+
+ {provider.api_version && ( +
+ + API Version: + + + {provider.api_version} + +
+ )} +
+ {modelsContent} +
+
+
+ ); + })}
)}
@@ -1418,7 +1440,6 @@ export default function ProvidersPage() { void; + onEdit: () => void; + onDelete: () => void; + balanceComponent: React.ReactNode; + children?: React.ReactNode; +} + +export function RoutstrProviderCard({ + provider, + expanded, + onToggleExpand, + onEdit, + onDelete, + balanceComponent, + children, +}: RoutstrProviderCardProps) { + const queryClient = useQueryClient(); + + const refundMutation = useMutation({ + mutationFn: () => RoutstrProviderService.refundBalance(provider.id), + onSuccess: (data) => { + if (data.ok) { + toast.success('Refund successful', { + description: data.message, + }); + queryClient.invalidateQueries({ + queryKey: ['provider-balance', provider.id], + }); + queryClient.invalidateQueries({ queryKey: ['balances'] }); // Global wallet balance + } else { + toast.error('Refund failed', { + description: data.message, + }); + } + }, + onError: (error: Error) => { + toast.error(`Refund error: ${error.message}`); + }, + }); + + return ( + + +
+
+
+ Routstr Node + + {provider.enabled ? 'Enabled' : 'Disabled'} + + + NIP-91 + +
+ + {provider.base_url} + +
+
+
{balanceComponent}
+ + + + + + +
+
+
+ {children} +
+ ); +}