(props: { showProviderInfo?: boolean })
| 50 | .join(" "); |
| 51 | |
| 52 | export function SecretsPage(props: { showProviderInfo?: boolean }) { |
| 53 | useExecutorDocumentTitle("Providers"); |
| 54 | const showProviderInfo = props.showProviderInfo ?? true; |
| 55 | const secretProviderPlugins = useSecretProviderPlugins(); |
| 56 | const providers = useAtomValue(providersAtom); |
| 57 | const refreshProviders = useAtomRefresh(providersAtom); |
| 58 | |
| 59 | return ( |
| 60 | <PageContainer> |
| 61 | <PageHeader |
| 62 | title="Providers" |
| 63 | description="Where your connections' credential values live: the default store for pasted values, or an external backend like 1Password or your system keychain." |
| 64 | /> |
| 65 | |
| 66 | {/* Provider plugins (settings cards) */} |
| 67 | {showProviderInfo && secretProviderPlugins.length > 0 && ( |
| 68 | <div className="mb-10"> |
| 69 | <CardStack> |
| 70 | <CardStackHeader>Configure providers</CardStackHeader> |
| 71 | <CardStackContent> |
| 72 | {secretProviderPlugins.map((plugin) => ( |
| 73 | <Suspense |
| 74 | key={plugin.key} |
| 75 | fallback={ |
| 76 | <div className="px-4 py-3 animate-pulse"> |
| 77 | <div className="h-4 w-24 rounded bg-muted" /> |
| 78 | </div> |
| 79 | } |
| 80 | > |
| 81 | <plugin.settings /> |
| 82 | </Suspense> |
| 83 | ))} |
| 84 | </CardStackContent> |
| 85 | </CardStack> |
| 86 | </div> |
| 87 | )} |
| 88 | |
| 89 | {/* Registered providers */} |
| 90 | {isAsyncResultLoading(providers) ? ( |
| 91 | <div className="flex items-center gap-2 py-8"> |
| 92 | <div className="size-1.5 animate-pulse rounded-full bg-muted-foreground/30" /> |
| 93 | <p className="text-sm text-muted-foreground">Loading providers…</p> |
| 94 | </div> |
| 95 | ) : ( |
| 96 | AsyncResult.match(providers, { |
| 97 | onInitial: () => ( |
| 98 | <div className="flex items-center gap-2 py-8"> |
| 99 | <div className="size-1.5 animate-pulse rounded-full bg-muted-foreground/30" /> |
| 100 | <p className="text-sm text-muted-foreground">Loading providers…</p> |
| 101 | </div> |
| 102 | ), |
| 103 | onFailure: () => ( |
| 104 | <ErrorState message="Failed to load providers" onRetry={refreshProviders} /> |
| 105 | ), |
| 106 | onSuccess: ({ value }) => ( |
| 107 | <CardStack> |
| 108 | <CardStackHeader>Available providers</CardStackHeader> |
| 109 | <CardStackContent> |
nothing calls this directly
no test coverage detected