History

Show loading badge while credentials resolve (#772)

Moritz Meyer committed 5 months ago

Commit eefb8b51 parent 81b628f4 files · +18 −19Browse files

Unified · Split

4 files
  1. packages/plugins/graphql/src/react/GraphqlSourceSummary.tsx +2 −3
  2. packages/plugins/mcp/src/react/McpSourceSummary.tsx +2 −3
  3. packages/plugins/openapi/src/react/OpenApiSourceSummary.tsx +3 −13
  4. packages/react/src/plugins/source-credential-status.tsx +11 −0
packages/plugins/graphql/src/react/GraphqlSourceSummary.tsx+2−3
@@ -4,6 +4,7 @@ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; · 3 unmodified lines
4import { connectionsAtom } from "@executor-js/react/api/atoms";4import { connectionsAtom } from "@executor-js/react/api/atoms";
5import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";5import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";
6import {6import {
7 SourceCredentialLoadingBadge,
7 SourceCredentialNotice,8 SourceCredentialNotice,
8 SourceCredentialStatusBadge,9 SourceCredentialStatusBadge,
9 missingSourceCredentialLabels,10 missingSourceCredentialLabels,
@@ -53,9 +54,7 @@ export default function GraphqlSourceSummary(props: { · 43 unmodified lines
53 const slots = sourceCredentialSlots(source as StoredGraphqlSource);54 const slots = sourceCredentialSlots(source as StoredGraphqlSource);
54 if (slots.length === 0) return null;55 if (slots.length === 0) return null;
55 if (!AsyncResult.isSuccess(bindingsResult) || !AsyncResult.isSuccess(connectionsResult)) {56 if (!AsyncResult.isSuccess(bindingsResult) || !AsyncResult.isSuccess(connectionsResult)) {
56 return props.variant === "panel" ? null : (57 return props.variant === "panel" ? null : <SourceCredentialLoadingBadge />;
57 <SourceCredentialStatusBadge missing={["credentials"]} />
58 );
59 }58 }
60 59
61 const scopeRanks = new Map(scopeStack.map((scope, index) => [scope.id, index] as const));60 const scopeRanks = new Map(scopeStack.map((scope, index) => [scope.id, index] as const));
packages/plugins/mcp/src/react/McpSourceSummary.tsx+2−3
@@ -5,6 +5,7 @@ import { connectionsAtom } from "@executor-js/react/api/atoms"; · 4 unmodified lines
5import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";5import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";
6import { Button } from "@executor-js/react/components/button";6import { Button } from "@executor-js/react/components/button";
7import {7import {
8 SourceCredentialLoadingBadge,
8 SourceCredentialNotice,9 SourceCredentialNotice,
9 SourceCredentialStatusBadge,10 SourceCredentialStatusBadge,
10 missingSourceCredentialLabels,11 missingSourceCredentialLabels,
@@ -72,9 +73,7 @@ export default function McpSourceSummary(props: { · 61 unmodified lines
72 const slots = sourceCredentialSlots(source as McpStoredSourceSchemaType);73 const slots = sourceCredentialSlots(source as McpStoredSourceSchemaType);
73 if (slots.length === 0) return null;74 if (slots.length === 0) return null;
74 if (!AsyncResult.isSuccess(bindingsResult) || !AsyncResult.isSuccess(connectionsResult)) {75 if (!AsyncResult.isSuccess(bindingsResult) || !AsyncResult.isSuccess(connectionsResult)) {
75 return props.variant === "panel" ? null : (76 return props.variant === "panel" ? null : <SourceCredentialLoadingBadge />;
76 <SourceCredentialStatusBadge missing={["credentials"]} />
77 );
78 }77 }
79 78
80 const scopeRanks = new Map(scopeStack.map((scope, index) => [scope.id, index] as const));79 const scopeRanks = new Map(scopeStack.map((scope, index) => [scope.id, index] as const));
packages/plugins/openapi/src/react/OpenApiSourceSummary.tsx+3−13
@@ -6,6 +6,7 @@ import { Badge } from "@executor-js/react/components/badge"; · 5 unmodified lines
6import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";6import { useScope, useScopeStack, useUserScope } from "@executor-js/react/api/scope-context";
7import { ScopeId } from "@executor-js/sdk/shared";7import { ScopeId } from "@executor-js/sdk/shared";
8import {8import {
9 SourceCredentialLoadingBadge,
9 SourceCredentialNotice,10 SourceCredentialNotice,
10 SourceCredentialStatusBadge,11 SourceCredentialStatusBadge,
11 missingSourceCredentialLabels,12 missingSourceCredentialLabels,
@@ -20,17 +21,6 @@ function OAuthBadge() { · 8 unmodified lines
20 return <Badge variant="secondary">OAuth</Badge>;21 return <Badge variant="secondary">OAuth</Badge>;
21}22}
22 23
23function CheckingCredentialsBadge() {
24 return (
25 <Badge
26 variant="outline"
27 className="border-border bg-muted/50 text-[10px] text-muted-foreground"
28 >
29 Checking credentials
30 </Badge>
31 );
32}
33
34const effectiveClientSecretSlot = (oauth2: {24const effectiveClientSecretSlot = (oauth2: {
35 readonly securitySchemeName: string;25 readonly securitySchemeName: string;
36 readonly clientSecretSlot: string | null;26 readonly clientSecretSlot: string | null;
@@ -92,12 +82,12 @@ export default function OpenApiSourceSummary(props: { · 55 unmodified lines
92 const bindingsLoaded = AsyncResult.isSuccess(bindingsResult);82 const bindingsLoaded = AsyncResult.isSuccess(bindingsResult);
93 const connectionsLoaded = AsyncResult.isSuccess(connectionsResult);83 const connectionsLoaded = AsyncResult.isSuccess(connectionsResult);
94 if (!bindingsLoaded) {84 if (!bindingsLoaded) {
95 return props.variant === "panel" ? null : <CheckingCredentialsBadge />;85 return props.variant === "panel" ? null : <SourceCredentialLoadingBadge />;
96 }86 }
97 87
98 const bindings = AsyncResult.isSuccess(bindingsResult) ? bindingsResult.value : [];88 const bindings = AsyncResult.isSuccess(bindingsResult) ? bindingsResult.value : [];
99 if (oauth2 && !connectionsLoaded) {89 if (oauth2 && !connectionsLoaded) {
100 return props.variant === "panel" ? null : <CheckingCredentialsBadge />;90 return props.variant === "panel" ? null : <SourceCredentialLoadingBadge />;
101 }91 }
102 const connections = AsyncResult.isSuccess(connectionsResult) ? connectionsResult.value : [];92 const connections = AsyncResult.isSuccess(connectionsResult) ? connectionsResult.value : [];
103 const liveConnectionIds = new Set(connections.map((connection) => connection.id));93 const liveConnectionIds = new Set(connections.map((connection) => connection.id));
packages/react/src/plugins/source-credential-status.tsx+11−0
@@ -8,6 +8,17 @@ export { · 7 unmodified lines
8 type SourceCredentialSlot,8 type SourceCredentialSlot,
9} from "./source-credential-status-core";9} from "./source-credential-status-core";
10 10
11export function SourceCredentialLoadingBadge() {
12 return (
13 <Badge
14 variant="outline"
15 className="animate-pulse border-border bg-muted/40 text-[10px] text-muted-foreground"
16 >
17 loading
18 </Badge>
19 );
20}
21
11export function SourceCredentialStatusBadge(props: { readonly missing: readonly string[] }) {22export function SourceCredentialStatusBadge(props: { readonly missing: readonly string[] }) {
12 if (props.missing.length === 0) {23 if (props.missing.length === 0) {
13 return (24 return (