@stigmer/react 0.0.83 → 0.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/fixtures.d.ts +4 -0
- package/demo/fixtures.d.ts.map +1 -1
- package/demo/fixtures.js +4 -0
- package/demo/fixtures.js.map +1 -1
- package/index.d.ts +5 -3
- package/index.d.ts.map +1 -1
- package/index.js +3 -1
- package/index.js.map +1 -1
- package/library/ResourceListView.d.ts +57 -7
- package/library/ResourceListView.d.ts.map +1 -1
- package/library/ResourceListView.js +147 -37
- package/library/ResourceListView.js.map +1 -1
- package/library/index.d.ts +1 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js.map +1 -1
- package/mcp-server/McpServerConfigPanel.d.ts +45 -0
- package/mcp-server/McpServerConfigPanel.d.ts.map +1 -1
- package/mcp-server/McpServerConfigPanel.js +90 -14
- package/mcp-server/McpServerConfigPanel.js.map +1 -1
- package/mcp-server/McpServerConnectDialog.d.ts +51 -0
- package/mcp-server/McpServerConnectDialog.d.ts.map +1 -0
- package/mcp-server/McpServerConnectDialog.js +164 -0
- package/mcp-server/McpServerConnectDialog.js.map +1 -0
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +168 -23
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +9 -3
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/OAuthAppForm.d.ts +58 -0
- package/mcp-server/OAuthAppForm.d.ts.map +1 -0
- package/mcp-server/OAuthAppForm.js +67 -0
- package/mcp-server/OAuthAppForm.js.map +1 -0
- package/mcp-server/index.d.ts +8 -0
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +4 -0
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/useDisconnectOAuth.d.ts +40 -0
- package/mcp-server/useDisconnectOAuth.d.ts.map +1 -0
- package/mcp-server/useDisconnectOAuth.js +46 -0
- package/mcp-server/useDisconnectOAuth.js.map +1 -0
- package/mcp-server/useMcpServerCredentials.d.ts +48 -0
- package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
- package/mcp-server/useMcpServerCredentials.js +18 -2
- package/mcp-server/useMcpServerCredentials.js.map +1 -1
- package/mcp-server/useOAuthGrantStatus.d.ts +9 -0
- package/mcp-server/useOAuthGrantStatus.d.ts.map +1 -1
- package/mcp-server/useOAuthGrantStatus.js +6 -1
- package/mcp-server/useOAuthGrantStatus.js.map +1 -1
- package/mcp-server/useOrgOAuthApp.d.ts +82 -0
- package/mcp-server/useOrgOAuthApp.d.ts.map +1 -0
- package/mcp-server/useOrgOAuthApp.js +160 -0
- package/mcp-server/useOrgOAuthApp.js.map +1 -0
- package/oauth-app/CreateOAuthAppForm.d.ts +41 -0
- package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -0
- package/oauth-app/CreateOAuthAppForm.js +140 -0
- package/oauth-app/CreateOAuthAppForm.js.map +1 -0
- package/oauth-app/OAuthAppDetailPanel.d.ts +43 -0
- package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -0
- package/oauth-app/OAuthAppDetailPanel.js +202 -0
- package/oauth-app/OAuthAppDetailPanel.js.map +1 -0
- package/oauth-app/OAuthAppListPanel.d.ts +43 -0
- package/oauth-app/OAuthAppListPanel.d.ts.map +1 -0
- package/oauth-app/OAuthAppListPanel.js +79 -0
- package/oauth-app/OAuthAppListPanel.js.map +1 -0
- package/oauth-app/index.d.ts +15 -0
- package/oauth-app/index.d.ts.map +1 -0
- package/oauth-app/index.js +8 -0
- package/oauth-app/index.js.map +1 -0
- package/oauth-app/useCreateOAuthApp.d.ts +39 -0
- package/oauth-app/useCreateOAuthApp.d.ts.map +1 -0
- package/oauth-app/useCreateOAuthApp.js +50 -0
- package/oauth-app/useCreateOAuthApp.js.map +1 -0
- package/oauth-app/useDeleteOAuthApp.d.ts +31 -0
- package/oauth-app/useDeleteOAuthApp.d.ts.map +1 -0
- package/oauth-app/useDeleteOAuthApp.js +43 -0
- package/oauth-app/useDeleteOAuthApp.js.map +1 -0
- package/oauth-app/useOAuthAppList.d.ts +32 -0
- package/oauth-app/useOAuthAppList.d.ts.map +1 -0
- package/oauth-app/useOAuthAppList.js +61 -0
- package/oauth-app/useOAuthAppList.js.map +1 -0
- package/oauth-app/useUpdateOAuthApp.d.ts +38 -0
- package/oauth-app/useUpdateOAuthApp.d.ts.map +1 -0
- package/oauth-app/useUpdateOAuthApp.js +49 -0
- package/oauth-app/useUpdateOAuthApp.js.map +1 -0
- package/package.json +4 -4
- package/src/demo/fixtures.ts +8 -0
- package/src/index.ts +25 -0
- package/src/library/ResourceListView.tsx +303 -46
- package/src/library/index.ts +4 -1
- package/src/mcp-server/McpServerConfigPanel.tsx +370 -45
- package/src/mcp-server/McpServerConnectDialog.tsx +527 -0
- package/src/mcp-server/McpServerDetailView.tsx +448 -47
- package/src/mcp-server/McpServerPicker.tsx +10 -3
- package/src/mcp-server/OAuthAppForm.tsx +304 -0
- package/src/mcp-server/index.ts +12 -0
- package/src/mcp-server/useDisconnectOAuth.ts +76 -0
- package/src/mcp-server/useMcpServerCredentials.ts +70 -2
- package/src/mcp-server/useOAuthGrantStatus.ts +19 -1
- package/src/mcp-server/useOrgOAuthApp.ts +250 -0
- package/src/oauth-app/CreateOAuthAppForm.tsx +449 -0
- package/src/oauth-app/OAuthAppDetailPanel.tsx +671 -0
- package/src/oauth-app/OAuthAppListPanel.tsx +237 -0
- package/src/oauth-app/index.ts +14 -0
- package/src/oauth-app/useCreateOAuthApp.ts +70 -0
- package/src/oauth-app/useDeleteOAuthApp.ts +62 -0
- package/src/oauth-app/useOAuthAppList.ts +84 -0
- package/src/oauth-app/useUpdateOAuthApp.ts +69 -0
- package/styles.css +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useCallback } from "react";
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { getUserMessage, isRetryableError } from "@stigmer/sdk";
|
|
5
6
|
import type { EnvVarInput } from "@stigmer/sdk";
|
|
7
|
+
import { OAuthConnectionHealth } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/io_pb";
|
|
6
8
|
import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
7
9
|
import type { DiscoveredTool } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/status_pb";
|
|
8
10
|
import type { ToolApprovalPolicy } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
|
|
@@ -72,6 +74,23 @@ export interface McpServerOAuthSignInProps {
|
|
|
72
74
|
readonly phase: OAuthConnectPhase;
|
|
73
75
|
/** `true` when the OAuth token already exists in the personal environment. */
|
|
74
76
|
readonly isConnected: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Health of the OAuth connection. Drives the status dot color and
|
|
79
|
+
* label beyond the binary `isConnected` boolean.
|
|
80
|
+
*/
|
|
81
|
+
readonly connectionHealth?: OAuthConnectionHealth;
|
|
82
|
+
/**
|
|
83
|
+
* Called to disconnect the OAuth grant. When provided and the user
|
|
84
|
+
* is connected, a "Disconnect" link is shown. The parent is responsible
|
|
85
|
+
* for refreshing credentials after the promise resolves.
|
|
86
|
+
*/
|
|
87
|
+
readonly onDisconnect?: () => Promise<void>;
|
|
88
|
+
/** `true` while a disconnect operation is in flight. */
|
|
89
|
+
readonly isDisconnecting?: boolean;
|
|
90
|
+
/** Error from the most recent failed disconnect, or `null`. */
|
|
91
|
+
readonly disconnectError?: Error | null;
|
|
92
|
+
/** Clear the disconnect error state. */
|
|
93
|
+
readonly onClearDisconnectError?: () => void;
|
|
75
94
|
/** Error from the most recent failed OAuth attempt, or `null`. */
|
|
76
95
|
readonly error: Error | null;
|
|
77
96
|
/** Clear the OAuth error state. */
|
|
@@ -81,11 +100,38 @@ export interface McpServerOAuthSignInProps {
|
|
|
81
100
|
* Disables the sign-in button and shows an informational message.
|
|
82
101
|
*/
|
|
83
102
|
readonly isVendorApprovalPending?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* `true` when the platform OAuth app's vendor approval is PENDING
|
|
105
|
+
* or REJECTED — the platform sign-in flow is blocked. Covers both
|
|
106
|
+
* statuses. When omitted, falls back to `isVendorApprovalPending`.
|
|
107
|
+
*/
|
|
108
|
+
readonly isVendorApprovalBlocked?: boolean;
|
|
84
109
|
/**
|
|
85
110
|
* Documentation URL for bringing your own OAuth token.
|
|
86
111
|
* Shown as a help link when `isVendorApprovalPending` is `true`.
|
|
87
112
|
*/
|
|
88
113
|
readonly vendorApprovalDocsUrl?: string | null;
|
|
114
|
+
/**
|
|
115
|
+
* `true` when the BYOA (Bring Your Own App) option is relevant:
|
|
116
|
+
* the server uses vendor OAuth and no org override exists.
|
|
117
|
+
*/
|
|
118
|
+
readonly canBringOwnApp?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* `true` when an org-level BYOA override is active.
|
|
121
|
+
*/
|
|
122
|
+
readonly isOrgOAuthApp?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Open the BYOA form. The parent is responsible for rendering the
|
|
125
|
+
* form/dialog and handling the mutation.
|
|
126
|
+
*/
|
|
127
|
+
readonly onBringOwnApp?: () => void;
|
|
128
|
+
/**
|
|
129
|
+
* Remove the org's BYOA override. The parent is responsible for
|
|
130
|
+
* refreshing state after the promise resolves.
|
|
131
|
+
*/
|
|
132
|
+
readonly onRemoveOrgApp?: () => Promise<void>;
|
|
133
|
+
/** `true` while a remove-org-app operation is in flight. */
|
|
134
|
+
readonly isRemovingOrgApp?: boolean;
|
|
89
135
|
}
|
|
90
136
|
|
|
91
137
|
// ---------------------------------------------------------------------------
|
|
@@ -277,12 +323,23 @@ export function McpServerConfigPanel({
|
|
|
277
323
|
<InlineOAuthSignIn
|
|
278
324
|
serverName={serverName}
|
|
279
325
|
isConnected={oauthSignIn.isConnected}
|
|
326
|
+
connectionHealth={oauthSignIn.connectionHealth}
|
|
280
327
|
phase={oauthSignIn.phase}
|
|
281
328
|
onSignIn={oauthSignIn.onSignIn}
|
|
329
|
+
onDisconnect={oauthSignIn.onDisconnect}
|
|
330
|
+
isDisconnecting={oauthSignIn.isDisconnecting}
|
|
331
|
+
disconnectError={oauthSignIn.disconnectError}
|
|
332
|
+
onClearDisconnectError={oauthSignIn.onClearDisconnectError}
|
|
282
333
|
error={oauthSignIn.error}
|
|
283
334
|
onClearError={oauthSignIn.onClearError}
|
|
284
335
|
isVendorApprovalPending={oauthSignIn.isVendorApprovalPending}
|
|
336
|
+
isVendorApprovalBlocked={oauthSignIn.isVendorApprovalBlocked}
|
|
285
337
|
vendorApprovalDocsUrl={oauthSignIn.vendorApprovalDocsUrl}
|
|
338
|
+
canBringOwnApp={oauthSignIn.canBringOwnApp}
|
|
339
|
+
isOrgOAuthApp={oauthSignIn.isOrgOAuthApp}
|
|
340
|
+
onBringOwnApp={oauthSignIn.onBringOwnApp}
|
|
341
|
+
onRemoveOrgApp={oauthSignIn.onRemoveOrgApp}
|
|
342
|
+
isRemovingOrgApp={oauthSignIn.isRemovingOrgApp}
|
|
286
343
|
onSwitchToManual={onSwitchToManual}
|
|
287
344
|
/>
|
|
288
345
|
)}
|
|
@@ -319,7 +376,7 @@ export function McpServerConfigPanel({
|
|
|
319
376
|
role="alert"
|
|
320
377
|
className="rounded-md border border-destructive/30 bg-destructive/10 px-2.5 py-2 text-xs text-destructive"
|
|
321
378
|
>
|
|
322
|
-
{error
|
|
379
|
+
{getUserMessage(error)}
|
|
323
380
|
</div>
|
|
324
381
|
)}
|
|
325
382
|
|
|
@@ -339,68 +396,279 @@ export function McpServerConfigPanel({
|
|
|
339
396
|
// Inline OAuth sign-in (compact, for config panel context)
|
|
340
397
|
// ---------------------------------------------------------------------------
|
|
341
398
|
|
|
399
|
+
/** Maps OAuthConnectionHealth to compact status dot + label for InlineOAuthSignIn. */
|
|
400
|
+
function inlineHealthProps(
|
|
401
|
+
health: OAuthConnectionHealth | undefined,
|
|
402
|
+
isConnected: boolean,
|
|
403
|
+
isVendorApprovalPending: boolean,
|
|
404
|
+
): { textClass: string; dotClass: string; label: string } {
|
|
405
|
+
if (isVendorApprovalPending && !isConnected) {
|
|
406
|
+
return {
|
|
407
|
+
textClass: "text-amber-600 dark:text-amber-400",
|
|
408
|
+
dotClass: "bg-amber-500",
|
|
409
|
+
label: "Pending approval",
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
switch (health) {
|
|
413
|
+
case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY:
|
|
414
|
+
return {
|
|
415
|
+
textClass: "text-success",
|
|
416
|
+
dotClass: "bg-success",
|
|
417
|
+
label: "Signed in",
|
|
418
|
+
};
|
|
419
|
+
case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED_REFRESHABLE:
|
|
420
|
+
return {
|
|
421
|
+
textClass: "text-amber-600 dark:text-amber-400",
|
|
422
|
+
dotClass: "bg-amber-500",
|
|
423
|
+
label: "Token expired",
|
|
424
|
+
};
|
|
425
|
+
case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED:
|
|
426
|
+
return {
|
|
427
|
+
textClass: "text-destructive",
|
|
428
|
+
dotClass: "bg-destructive",
|
|
429
|
+
label: "Re-auth needed",
|
|
430
|
+
};
|
|
431
|
+
default:
|
|
432
|
+
return {
|
|
433
|
+
textClass: "text-muted-foreground",
|
|
434
|
+
dotClass: "bg-muted-foreground",
|
|
435
|
+
label: "Sign-in required",
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
type InlineDisconnectPhase = "idle" | "confirming" | "disconnecting";
|
|
441
|
+
type InlineRemoveOrgAppPhase = "idle" | "confirming" | "removing";
|
|
442
|
+
|
|
342
443
|
function InlineOAuthSignIn({
|
|
343
444
|
serverName,
|
|
344
445
|
isConnected,
|
|
446
|
+
connectionHealth,
|
|
345
447
|
phase,
|
|
346
448
|
onSignIn,
|
|
449
|
+
onDisconnect,
|
|
450
|
+
isDisconnecting,
|
|
451
|
+
disconnectError,
|
|
452
|
+
onClearDisconnectError,
|
|
347
453
|
error,
|
|
348
454
|
onClearError,
|
|
349
455
|
isVendorApprovalPending,
|
|
456
|
+
isVendorApprovalBlocked,
|
|
350
457
|
vendorApprovalDocsUrl,
|
|
458
|
+
canBringOwnApp,
|
|
459
|
+
isOrgOAuthApp,
|
|
460
|
+
onBringOwnApp,
|
|
461
|
+
onRemoveOrgApp,
|
|
462
|
+
isRemovingOrgApp,
|
|
351
463
|
onSwitchToManual,
|
|
352
464
|
}: {
|
|
353
465
|
readonly serverName: string;
|
|
354
466
|
readonly isConnected: boolean;
|
|
467
|
+
readonly connectionHealth?: OAuthConnectionHealth;
|
|
355
468
|
readonly phase: OAuthConnectPhase;
|
|
356
469
|
readonly onSignIn: () => void;
|
|
470
|
+
readonly onDisconnect?: () => Promise<void>;
|
|
471
|
+
readonly isDisconnecting?: boolean;
|
|
472
|
+
readonly disconnectError?: Error | null;
|
|
473
|
+
readonly onClearDisconnectError?: () => void;
|
|
357
474
|
readonly error: Error | null;
|
|
358
475
|
readonly onClearError: () => void;
|
|
359
476
|
readonly isVendorApprovalPending?: boolean;
|
|
477
|
+
readonly isVendorApprovalBlocked?: boolean;
|
|
360
478
|
readonly vendorApprovalDocsUrl?: string | null;
|
|
479
|
+
readonly canBringOwnApp?: boolean;
|
|
480
|
+
readonly isOrgOAuthApp?: boolean;
|
|
481
|
+
readonly onBringOwnApp?: () => void;
|
|
482
|
+
readonly onRemoveOrgApp?: () => Promise<void>;
|
|
483
|
+
readonly isRemovingOrgApp?: boolean;
|
|
361
484
|
readonly onSwitchToManual?: () => void;
|
|
362
485
|
}) {
|
|
486
|
+
const [disconnectPhase, setDisconnectPhase] = useState<InlineDisconnectPhase>("idle");
|
|
487
|
+
const [removeOrgAppPhase, setRemoveOrgAppPhase] = useState<InlineRemoveOrgAppPhase>("idle");
|
|
488
|
+
|
|
489
|
+
const blocked = isVendorApprovalBlocked ?? isVendorApprovalPending;
|
|
490
|
+
|
|
363
491
|
const isBusy =
|
|
364
492
|
phase === "initiating" ||
|
|
365
493
|
phase === "awaiting-callback" ||
|
|
366
494
|
phase === "completing" ||
|
|
367
495
|
phase === "connecting";
|
|
368
496
|
|
|
369
|
-
const signInDisabled = isBusy || !!
|
|
497
|
+
const signInDisabled = isBusy || (!!blocked && !isOrgOAuthApp);
|
|
498
|
+
const anyBusy = isBusy || !!isDisconnecting || !!isRemovingOrgApp;
|
|
499
|
+
|
|
500
|
+
const needsReAuth =
|
|
501
|
+
connectionHealth === OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED;
|
|
502
|
+
|
|
503
|
+
const status = inlineHealthProps(
|
|
504
|
+
connectionHealth,
|
|
505
|
+
isConnected,
|
|
506
|
+
!!isVendorApprovalPending && !isOrgOAuthApp,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
const showDisconnectLink =
|
|
510
|
+
isConnected && onDisconnect && !anyBusy && disconnectPhase === "idle";
|
|
511
|
+
|
|
512
|
+
const showRemoveOrgAppLink =
|
|
513
|
+
isOrgOAuthApp && onRemoveOrgApp && !anyBusy && removeOrgAppPhase === "idle";
|
|
514
|
+
|
|
515
|
+
// Inline disconnect confirmation
|
|
516
|
+
if (disconnectPhase === "confirming" || disconnectPhase === "disconnecting") {
|
|
517
|
+
return (
|
|
518
|
+
<div className="space-y-1.5">
|
|
519
|
+
<p className="text-[0.65rem] text-foreground">
|
|
520
|
+
Remove credentials? You can reconnect at any time.
|
|
521
|
+
</p>
|
|
522
|
+
<div className="flex items-center gap-1.5">
|
|
523
|
+
<button
|
|
524
|
+
type="button"
|
|
525
|
+
disabled={!!isDisconnecting}
|
|
526
|
+
onClick={async () => {
|
|
527
|
+
if (!onDisconnect) return;
|
|
528
|
+
setDisconnectPhase("disconnecting");
|
|
529
|
+
try {
|
|
530
|
+
await onDisconnect();
|
|
531
|
+
setDisconnectPhase("idle");
|
|
532
|
+
} catch {
|
|
533
|
+
setDisconnectPhase("confirming");
|
|
534
|
+
}
|
|
535
|
+
}}
|
|
536
|
+
className={cn(
|
|
537
|
+
"inline-flex items-center gap-1 rounded px-2 py-0.5 text-[0.65rem] font-medium",
|
|
538
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
539
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
540
|
+
)}
|
|
541
|
+
>
|
|
542
|
+
{isDisconnecting && <InlineSpinner />}
|
|
543
|
+
Disconnect
|
|
544
|
+
</button>
|
|
545
|
+
<button
|
|
546
|
+
type="button"
|
|
547
|
+
disabled={!!isDisconnecting}
|
|
548
|
+
onClick={() => {
|
|
549
|
+
setDisconnectPhase("idle");
|
|
550
|
+
onClearDisconnectError?.();
|
|
551
|
+
}}
|
|
552
|
+
className={cn(
|
|
553
|
+
"inline-flex items-center rounded px-2 py-0.5 text-[0.65rem] font-medium",
|
|
554
|
+
"text-muted-foreground hover:text-foreground hover:bg-accent/50",
|
|
555
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
556
|
+
)}
|
|
557
|
+
>
|
|
558
|
+
Cancel
|
|
559
|
+
</button>
|
|
560
|
+
</div>
|
|
561
|
+
{disconnectError && (
|
|
562
|
+
<div className="flex items-start gap-1.5 text-[0.65rem] text-destructive" role="alert">
|
|
563
|
+
<span className="flex-1">{getUserMessage(disconnectError)}</span>
|
|
564
|
+
<button
|
|
565
|
+
type="button"
|
|
566
|
+
onClick={() => onClearDisconnectError?.()}
|
|
567
|
+
className="shrink-0 underline underline-offset-2 hover:no-underline"
|
|
568
|
+
>
|
|
569
|
+
Dismiss
|
|
570
|
+
</button>
|
|
571
|
+
</div>
|
|
572
|
+
)}
|
|
573
|
+
</div>
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Inline "remove custom app" confirmation
|
|
578
|
+
if (removeOrgAppPhase === "confirming" || removeOrgAppPhase === "removing") {
|
|
579
|
+
return (
|
|
580
|
+
<div className="space-y-1.5">
|
|
581
|
+
<p className="text-[0.65rem] text-foreground">
|
|
582
|
+
Remove your custom OAuth app? The server will revert to the
|
|
583
|
+
platform's app.
|
|
584
|
+
</p>
|
|
585
|
+
<div className="flex items-center gap-1.5">
|
|
586
|
+
<button
|
|
587
|
+
type="button"
|
|
588
|
+
disabled={!!isRemovingOrgApp}
|
|
589
|
+
onClick={async () => {
|
|
590
|
+
if (!onRemoveOrgApp) return;
|
|
591
|
+
setRemoveOrgAppPhase("removing");
|
|
592
|
+
try {
|
|
593
|
+
await onRemoveOrgApp();
|
|
594
|
+
setRemoveOrgAppPhase("idle");
|
|
595
|
+
} catch {
|
|
596
|
+
setRemoveOrgAppPhase("confirming");
|
|
597
|
+
}
|
|
598
|
+
}}
|
|
599
|
+
className={cn(
|
|
600
|
+
"inline-flex items-center gap-1 rounded px-2 py-0.5 text-[0.65rem] font-medium",
|
|
601
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
602
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
603
|
+
)}
|
|
604
|
+
>
|
|
605
|
+
{isRemovingOrgApp && <InlineSpinner />}
|
|
606
|
+
Remove
|
|
607
|
+
</button>
|
|
608
|
+
<button
|
|
609
|
+
type="button"
|
|
610
|
+
disabled={!!isRemovingOrgApp}
|
|
611
|
+
onClick={() => setRemoveOrgAppPhase("idle")}
|
|
612
|
+
className={cn(
|
|
613
|
+
"inline-flex items-center rounded px-2 py-0.5 text-[0.65rem] font-medium",
|
|
614
|
+
"text-muted-foreground hover:text-foreground hover:bg-accent/50",
|
|
615
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
616
|
+
)}
|
|
617
|
+
>
|
|
618
|
+
Cancel
|
|
619
|
+
</button>
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
);
|
|
623
|
+
}
|
|
370
624
|
|
|
371
625
|
return (
|
|
372
626
|
<div className="space-y-1.5">
|
|
373
627
|
<div className="flex items-center justify-between">
|
|
374
|
-
<
|
|
375
|
-
className={cn(
|
|
376
|
-
"inline-flex items-center gap-1 text-[0.65rem] font-medium",
|
|
377
|
-
isConnected
|
|
378
|
-
? "text-success"
|
|
379
|
-
: isVendorApprovalPending
|
|
380
|
-
? "text-amber-600 dark:text-amber-400"
|
|
381
|
-
: "text-muted-foreground",
|
|
382
|
-
)}
|
|
383
|
-
>
|
|
628
|
+
<div className="flex items-center gap-1.5">
|
|
384
629
|
<span
|
|
385
630
|
className={cn(
|
|
386
|
-
"
|
|
387
|
-
|
|
388
|
-
? "bg-success"
|
|
389
|
-
: isVendorApprovalPending
|
|
390
|
-
? "bg-amber-500"
|
|
391
|
-
: "bg-muted-foreground",
|
|
631
|
+
"inline-flex items-center gap-1 text-[0.65rem] font-medium",
|
|
632
|
+
status.textClass,
|
|
392
633
|
)}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
634
|
+
>
|
|
635
|
+
<span
|
|
636
|
+
className={cn("size-1.5 rounded-full", status.dotClass)}
|
|
637
|
+
aria-hidden="true"
|
|
638
|
+
/>
|
|
639
|
+
{status.label}
|
|
640
|
+
</span>
|
|
641
|
+
{isOrgOAuthApp && isConnected && (
|
|
642
|
+
<span className="text-[0.6rem] text-muted-foreground">
|
|
643
|
+
Your app
|
|
644
|
+
</span>
|
|
645
|
+
)}
|
|
646
|
+
{showDisconnectLink && (
|
|
647
|
+
<button
|
|
648
|
+
type="button"
|
|
649
|
+
onClick={() => setDisconnectPhase("confirming")}
|
|
650
|
+
className="text-[0.65rem] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
|
651
|
+
>
|
|
652
|
+
Disconnect
|
|
653
|
+
</button>
|
|
654
|
+
)}
|
|
655
|
+
{showRemoveOrgAppLink && (
|
|
656
|
+
<button
|
|
657
|
+
type="button"
|
|
658
|
+
onClick={() => setRemoveOrgAppPhase("confirming")}
|
|
659
|
+
className="text-[0.65rem] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
|
660
|
+
>
|
|
661
|
+
Remove custom app
|
|
662
|
+
</button>
|
|
663
|
+
)}
|
|
664
|
+
</div>
|
|
397
665
|
<button
|
|
398
666
|
type="button"
|
|
399
667
|
onClick={onSignIn}
|
|
400
668
|
disabled={signInDisabled}
|
|
401
669
|
className={cn(
|
|
402
670
|
"inline-flex items-center gap-1 rounded px-2 py-0.5 text-[0.65rem] font-medium",
|
|
403
|
-
isConnected
|
|
671
|
+
isConnected && !needsReAuth
|
|
404
672
|
? "text-muted-foreground hover:text-foreground hover:bg-accent/50"
|
|
405
673
|
: "bg-primary text-primary-foreground hover:bg-primary-hover",
|
|
406
674
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
@@ -408,17 +676,37 @@ function InlineOAuthSignIn({
|
|
|
408
676
|
>
|
|
409
677
|
{isBusy ? (
|
|
410
678
|
<InlineSpinner />
|
|
411
|
-
) : isConnected ? (
|
|
679
|
+
) : isOrgOAuthApp && !isConnected ? (
|
|
680
|
+
"Sign in with your app"
|
|
681
|
+
) : isConnected && !needsReAuth ? (
|
|
412
682
|
"Re-authenticate"
|
|
683
|
+
) : needsReAuth ? (
|
|
684
|
+
"Sign in to reconnect"
|
|
413
685
|
) : (
|
|
414
686
|
`Sign in with ${serverName}`
|
|
415
687
|
)}
|
|
416
688
|
</button>
|
|
417
689
|
</div>
|
|
418
|
-
|
|
690
|
+
|
|
691
|
+
{/* Vendor approval blocked message with BYOA CTA */}
|
|
692
|
+
{blocked && !isConnected && !isOrgOAuthApp && (
|
|
419
693
|
<div className="text-[0.65rem] text-amber-700 dark:text-amber-300">
|
|
420
|
-
<p>
|
|
421
|
-
|
|
694
|
+
<p>
|
|
695
|
+
OAuth sign-in is pending vendor approval.
|
|
696
|
+
{canBringOwnApp && onBringOwnApp
|
|
697
|
+
? " You can use your own OAuth app or enter a token manually."
|
|
698
|
+
: ""}
|
|
699
|
+
</p>
|
|
700
|
+
{canBringOwnApp && onBringOwnApp && (
|
|
701
|
+
<button
|
|
702
|
+
type="button"
|
|
703
|
+
onClick={onBringOwnApp}
|
|
704
|
+
className="mt-1 inline-flex items-center gap-1 rounded bg-amber-600 px-2 py-0.5 text-[0.6rem] font-medium text-white hover:bg-amber-700 dark:bg-amber-500 dark:text-amber-950 dark:hover:bg-amber-400"
|
|
705
|
+
>
|
|
706
|
+
Use your own OAuth app
|
|
707
|
+
</button>
|
|
708
|
+
)}
|
|
709
|
+
{vendorApprovalDocsUrl && !canBringOwnApp && (
|
|
422
710
|
<a
|
|
423
711
|
href={vendorApprovalDocsUrl}
|
|
424
712
|
target="_blank"
|
|
@@ -430,26 +718,63 @@ function InlineOAuthSignIn({
|
|
|
430
718
|
)}
|
|
431
719
|
</div>
|
|
432
720
|
)}
|
|
721
|
+
|
|
722
|
+
{/* Org override indicator when not connected */}
|
|
723
|
+
{isOrgOAuthApp && !isConnected && (
|
|
724
|
+
<span className="text-[0.6rem] text-muted-foreground">
|
|
725
|
+
Using your OAuth app
|
|
726
|
+
</span>
|
|
727
|
+
)}
|
|
728
|
+
|
|
433
729
|
{error && (
|
|
434
|
-
<div className="flex items-start gap-1.5 text-[0.65rem] text-destructive">
|
|
435
|
-
<span className="flex-1">{error
|
|
436
|
-
<
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
730
|
+
<div className="flex items-start gap-1.5 text-[0.65rem] text-destructive" role="alert">
|
|
731
|
+
<span className="flex-1">{getUserMessage(error)}</span>
|
|
732
|
+
<div className="flex shrink-0 items-center gap-1.5">
|
|
733
|
+
{isRetryableError(error) && (
|
|
734
|
+
<button
|
|
735
|
+
type="button"
|
|
736
|
+
onClick={() => {
|
|
737
|
+
onClearError();
|
|
738
|
+
onSignIn();
|
|
739
|
+
}}
|
|
740
|
+
className="font-medium underline underline-offset-2 hover:no-underline"
|
|
741
|
+
>
|
|
742
|
+
Try again
|
|
743
|
+
</button>
|
|
744
|
+
)}
|
|
745
|
+
<button
|
|
746
|
+
type="button"
|
|
747
|
+
onClick={onClearError}
|
|
748
|
+
className="underline underline-offset-2 hover:no-underline"
|
|
749
|
+
>
|
|
750
|
+
Dismiss
|
|
751
|
+
</button>
|
|
752
|
+
</div>
|
|
443
753
|
</div>
|
|
444
754
|
)}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
755
|
+
|
|
756
|
+
{/* Secondary actions: manual entry, BYOA */}
|
|
757
|
+
{!isConnected && !isBusy && (
|
|
758
|
+
<div className="flex items-center gap-2">
|
|
759
|
+
{onSwitchToManual && (
|
|
760
|
+
<button
|
|
761
|
+
type="button"
|
|
762
|
+
onClick={onSwitchToManual}
|
|
763
|
+
className="text-[0.65rem] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
|
764
|
+
>
|
|
765
|
+
Enter token manually
|
|
766
|
+
</button>
|
|
767
|
+
)}
|
|
768
|
+
{canBringOwnApp && onBringOwnApp && !blocked && (
|
|
769
|
+
<button
|
|
770
|
+
type="button"
|
|
771
|
+
onClick={onBringOwnApp}
|
|
772
|
+
className="text-[0.65rem] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
|
773
|
+
>
|
|
774
|
+
Use your own OAuth app
|
|
775
|
+
</button>
|
|
776
|
+
)}
|
|
777
|
+
</div>
|
|
453
778
|
)}
|
|
454
779
|
</div>
|
|
455
780
|
);
|