@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
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import { GetOrgOAuthAppInputSchema, SetOrgOAuthAppInputSchema, DeleteOrgOAuthAppInputSchema, } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/io_pb";
|
|
5
|
+
import { useStigmer } from "../hooks";
|
|
6
|
+
import { toError } from "../internal/toError";
|
|
7
|
+
const IDLE = {
|
|
8
|
+
hasOverride: false,
|
|
9
|
+
oauthAppId: null,
|
|
10
|
+
clientId: null,
|
|
11
|
+
isLoading: false,
|
|
12
|
+
error: null,
|
|
13
|
+
refetch: () => { },
|
|
14
|
+
setOrgOAuthApp: () => Promise.resolve(""),
|
|
15
|
+
isSetting: false,
|
|
16
|
+
setError: null,
|
|
17
|
+
deleteOrgOAuthApp: () => Promise.resolve(false),
|
|
18
|
+
isDeleting: false,
|
|
19
|
+
deleteError: null,
|
|
20
|
+
clearErrors: () => { },
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Hybrid data + behavior hook for managing org-level BYOA OAuth app overrides.
|
|
24
|
+
*
|
|
25
|
+
* **Data side**: Auto-fetches `getOrgOAuthApp` when both parameters are
|
|
26
|
+
* non-null. Returns override existence, OAuthApp ID, and client ID for
|
|
27
|
+
* display. Follows the same fetch-on-mount pattern as
|
|
28
|
+
* {@link useOAuthGrantStatus}.
|
|
29
|
+
*
|
|
30
|
+
* **Behavior side**: Exposes `setOrgOAuthApp` and `deleteOrgOAuthApp`
|
|
31
|
+
* mutations bound to the hook's resource + org context, eliminating
|
|
32
|
+
* parameter repetition at call sites.
|
|
33
|
+
*
|
|
34
|
+
* Pass `null` for either parameter to skip fetching (stable no-op).
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* const orgApp = useOrgOAuthApp(mcpServer?.metadata?.id ?? null, org);
|
|
39
|
+
*
|
|
40
|
+
* if (orgApp.hasOverride) {
|
|
41
|
+
* return <span>Using your OAuth app (client: {orgApp.clientId})</span>;
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* const handleSubmit = async (clientId: string, clientSecret: string) => {
|
|
45
|
+
* await orgApp.setOrgOAuthApp(clientId, clientSecret);
|
|
46
|
+
* orgApp.refetch();
|
|
47
|
+
* credentials.refetch();
|
|
48
|
+
* };
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useOrgOAuthApp(resourceId, org) {
|
|
52
|
+
const stigmer = useStigmer();
|
|
53
|
+
const [hasOverride, setHasOverride] = useState(false);
|
|
54
|
+
const [oauthAppId, setOauthAppId] = useState(null);
|
|
55
|
+
const [clientId, setClientId] = useState(null);
|
|
56
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
57
|
+
const [error, setError] = useState(null);
|
|
58
|
+
const [fetchKey, setFetchKey] = useState(0);
|
|
59
|
+
const [isSetting, setIsSetting] = useState(false);
|
|
60
|
+
const [setError_, setSetError] = useState(null);
|
|
61
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
62
|
+
const [deleteError, setDeleteError] = useState(null);
|
|
63
|
+
const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
|
|
64
|
+
const clearErrors = useCallback(() => {
|
|
65
|
+
setSetError(null);
|
|
66
|
+
setDeleteError(null);
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!resourceId || !org) {
|
|
70
|
+
setHasOverride(false);
|
|
71
|
+
setOauthAppId(null);
|
|
72
|
+
setClientId(null);
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
setError(null);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const cancelled = { current: false };
|
|
78
|
+
setIsLoading(true);
|
|
79
|
+
setError(null);
|
|
80
|
+
stigmer.mcpServer
|
|
81
|
+
.getOrgOAuthApp(create(GetOrgOAuthAppInputSchema, { resourceId, org }))
|
|
82
|
+
.then((result) => {
|
|
83
|
+
if (cancelled.current)
|
|
84
|
+
return;
|
|
85
|
+
setHasOverride(result.hasOverride);
|
|
86
|
+
setOauthAppId(result.oauthAppId || null);
|
|
87
|
+
setClientId(result.clientId || null);
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
}, (err) => {
|
|
90
|
+
if (cancelled.current)
|
|
91
|
+
return;
|
|
92
|
+
setError(toError(err));
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
});
|
|
95
|
+
return () => {
|
|
96
|
+
cancelled.current = true;
|
|
97
|
+
};
|
|
98
|
+
}, [resourceId, org, stigmer, fetchKey]);
|
|
99
|
+
const setOrgOAuthApp = useCallback(async (newClientId, clientSecret) => {
|
|
100
|
+
if (!resourceId || !org) {
|
|
101
|
+
throw new Error("useOrgOAuthApp: resourceId and org are required for setOrgOAuthApp");
|
|
102
|
+
}
|
|
103
|
+
setIsSetting(true);
|
|
104
|
+
setSetError(null);
|
|
105
|
+
try {
|
|
106
|
+
const result = await stigmer.mcpServer.setOrgOAuthApp(create(SetOrgOAuthAppInputSchema, {
|
|
107
|
+
resourceId,
|
|
108
|
+
org,
|
|
109
|
+
clientId: newClientId,
|
|
110
|
+
clientSecret,
|
|
111
|
+
}));
|
|
112
|
+
return result.oauthAppId;
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
const wrapped = toError(err);
|
|
116
|
+
setSetError(wrapped);
|
|
117
|
+
throw wrapped;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
setIsSetting(false);
|
|
121
|
+
}
|
|
122
|
+
}, [stigmer, resourceId, org]);
|
|
123
|
+
const deleteOrgOAuthApp = useCallback(async () => {
|
|
124
|
+
if (!resourceId || !org) {
|
|
125
|
+
throw new Error("useOrgOAuthApp: resourceId and org are required for deleteOrgOAuthApp");
|
|
126
|
+
}
|
|
127
|
+
setIsDeleting(true);
|
|
128
|
+
setDeleteError(null);
|
|
129
|
+
try {
|
|
130
|
+
const result = await stigmer.mcpServer.deleteOrgOAuthApp(create(DeleteOrgOAuthAppInputSchema, { resourceId, org }));
|
|
131
|
+
return result.deleted;
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
const wrapped = toError(err);
|
|
135
|
+
setDeleteError(wrapped);
|
|
136
|
+
throw wrapped;
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
setIsDeleting(false);
|
|
140
|
+
}
|
|
141
|
+
}, [stigmer, resourceId, org]);
|
|
142
|
+
if (!resourceId || !org)
|
|
143
|
+
return { ...IDLE, refetch, clearErrors };
|
|
144
|
+
return {
|
|
145
|
+
hasOverride,
|
|
146
|
+
oauthAppId,
|
|
147
|
+
clientId,
|
|
148
|
+
isLoading,
|
|
149
|
+
error,
|
|
150
|
+
refetch,
|
|
151
|
+
setOrgOAuthApp,
|
|
152
|
+
isSetting,
|
|
153
|
+
setError: setError_,
|
|
154
|
+
deleteOrgOAuthApp,
|
|
155
|
+
isDeleting,
|
|
156
|
+
deleteError,
|
|
157
|
+
clearErrors,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=useOrgOAuthApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrgOAuthApp.js","sourceRoot":"","sources":["../../src/mcp-server/useOrgOAuthApp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
2
|
+
/** Props for {@link CreateOAuthAppForm}. */
|
|
3
|
+
export interface CreateOAuthAppFormProps {
|
|
4
|
+
/** Organization slug — the OAuth app will be created in this org. */
|
|
5
|
+
readonly org: string;
|
|
6
|
+
/** Fired with the newly created OAuth app on success. */
|
|
7
|
+
readonly onCreated?: (app: OAuthApp) => void;
|
|
8
|
+
/** Fired when the user cancels creation. */
|
|
9
|
+
readonly onCancel?: () => void;
|
|
10
|
+
/** Additional CSS class names for the root container. */
|
|
11
|
+
readonly className?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Form for creating a new OAuth app within an organization.
|
|
15
|
+
*
|
|
16
|
+
* Collects the required OAuth configuration: **name**, **provider**,
|
|
17
|
+
* **client ID**, **client secret**, **authorization URL**, and
|
|
18
|
+
* **token URL**. An expandable "Advanced" section provides optional
|
|
19
|
+
* fields for scopes, userinfo URL, scope parameter name, and vendor
|
|
20
|
+
* approval settings.
|
|
21
|
+
*
|
|
22
|
+
* This is a pure presentational component with no dialog wrapper
|
|
23
|
+
* (headless-first). The parent is responsible for rendering it inside
|
|
24
|
+
* a card, dialog, or inline context as needed.
|
|
25
|
+
*
|
|
26
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <CreateOAuthAppForm
|
|
31
|
+
* org="acme"
|
|
32
|
+
* onCreated={(app) => {
|
|
33
|
+
* refetch();
|
|
34
|
+
* setShowForm(false);
|
|
35
|
+
* }}
|
|
36
|
+
* onCancel={() => setShowForm(false)}
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function CreateOAuthAppForm({ org, onCreated, onCancel, className, }: CreateOAuthAppFormProps): import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
//# sourceMappingURL=CreateOAuthAppForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateOAuthAppForm.d.ts","sourceRoot":"","sources":["../../src/oauth-app/CreateOAuthAppForm.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mDAAmD,CAAC;AAQlF,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC7C,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,uBAAuB,2CA6RzB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { getUserMessage } from "@stigmer/sdk";
|
|
6
|
+
import { VendorApprovalStatus } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/spec_pb";
|
|
7
|
+
import { useCreateOAuthApp } from "./useCreateOAuthApp";
|
|
8
|
+
/**
|
|
9
|
+
* Form for creating a new OAuth app within an organization.
|
|
10
|
+
*
|
|
11
|
+
* Collects the required OAuth configuration: **name**, **provider**,
|
|
12
|
+
* **client ID**, **client secret**, **authorization URL**, and
|
|
13
|
+
* **token URL**. An expandable "Advanced" section provides optional
|
|
14
|
+
* fields for scopes, userinfo URL, scope parameter name, and vendor
|
|
15
|
+
* approval settings.
|
|
16
|
+
*
|
|
17
|
+
* This is a pure presentational component with no dialog wrapper
|
|
18
|
+
* (headless-first). The parent is responsible for rendering it inside
|
|
19
|
+
* a card, dialog, or inline context as needed.
|
|
20
|
+
*
|
|
21
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <CreateOAuthAppForm
|
|
26
|
+
* org="acme"
|
|
27
|
+
* onCreated={(app) => {
|
|
28
|
+
* refetch();
|
|
29
|
+
* setShowForm(false);
|
|
30
|
+
* }}
|
|
31
|
+
* onCancel={() => setShowForm(false)}
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function CreateOAuthAppForm({ org, onCreated, onCancel, className, }) {
|
|
36
|
+
const { create, isCreating, error, clearError } = useCreateOAuthApp();
|
|
37
|
+
const [name, setName] = useState("");
|
|
38
|
+
const [provider, setProvider] = useState("");
|
|
39
|
+
const [clientId, setClientId] = useState("");
|
|
40
|
+
const [clientSecret, setClientSecret] = useState("");
|
|
41
|
+
const [authorizationUrl, setAuthorizationUrl] = useState("");
|
|
42
|
+
const [tokenUrl, setTokenUrl] = useState("");
|
|
43
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
44
|
+
const [scopes, setScopes] = useState("");
|
|
45
|
+
const [userinfoUrl, setUserinfoUrl] = useState("");
|
|
46
|
+
const [scopeParameterName, setScopeParameterName] = useState("");
|
|
47
|
+
const [vendorApprovalStatus, setVendorApprovalStatus] = useState("unspecified");
|
|
48
|
+
const [vendorApprovalDocsUrl, setVendorApprovalDocsUrl] = useState("");
|
|
49
|
+
const trimmedName = name.trim();
|
|
50
|
+
const trimmedProvider = provider.trim();
|
|
51
|
+
const trimmedClientId = clientId.trim();
|
|
52
|
+
const trimmedClientSecret = clientSecret.trim();
|
|
53
|
+
const trimmedAuthUrl = authorizationUrl.trim();
|
|
54
|
+
const trimmedTokenUrl = tokenUrl.trim();
|
|
55
|
+
const canSubmit = trimmedName !== "" &&
|
|
56
|
+
trimmedProvider !== "" &&
|
|
57
|
+
trimmedClientId !== "" &&
|
|
58
|
+
trimmedClientSecret !== "" &&
|
|
59
|
+
trimmedAuthUrl !== "" &&
|
|
60
|
+
trimmedTokenUrl !== "" &&
|
|
61
|
+
!isCreating;
|
|
62
|
+
const handleSubmit = useCallback(async (e) => {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
if (!canSubmit)
|
|
65
|
+
return;
|
|
66
|
+
clearError();
|
|
67
|
+
try {
|
|
68
|
+
const parsedScopes = scopes
|
|
69
|
+
.split(",")
|
|
70
|
+
.map((s) => s.trim())
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
const app = await create({
|
|
73
|
+
name: trimmedName,
|
|
74
|
+
org,
|
|
75
|
+
provider: trimmedProvider,
|
|
76
|
+
clientId: trimmedClientId,
|
|
77
|
+
clientSecret: trimmedClientSecret,
|
|
78
|
+
authorizationUrl: trimmedAuthUrl,
|
|
79
|
+
tokenUrl: trimmedTokenUrl,
|
|
80
|
+
...(parsedScopes.length > 0 && { scopes: parsedScopes }),
|
|
81
|
+
...(userinfoUrl.trim() && { userinfoUrl: userinfoUrl.trim() }),
|
|
82
|
+
...(scopeParameterName.trim() && {
|
|
83
|
+
scopeParameterName: scopeParameterName.trim(),
|
|
84
|
+
}),
|
|
85
|
+
...(vendorApprovalStatus !== "unspecified" && {
|
|
86
|
+
vendorApprovalStatus: APPROVAL_STATUS_MAP[vendorApprovalStatus],
|
|
87
|
+
}),
|
|
88
|
+
...(vendorApprovalDocsUrl.trim() && {
|
|
89
|
+
vendorApprovalDocsUrl: vendorApprovalDocsUrl.trim(),
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
onCreated?.(app);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// error state is managed by useCreateOAuthApp
|
|
96
|
+
}
|
|
97
|
+
}, [
|
|
98
|
+
canSubmit,
|
|
99
|
+
trimmedName,
|
|
100
|
+
org,
|
|
101
|
+
trimmedProvider,
|
|
102
|
+
trimmedClientId,
|
|
103
|
+
trimmedClientSecret,
|
|
104
|
+
trimmedAuthUrl,
|
|
105
|
+
trimmedTokenUrl,
|
|
106
|
+
scopes,
|
|
107
|
+
userinfoUrl,
|
|
108
|
+
scopeParameterName,
|
|
109
|
+
vendorApprovalStatus,
|
|
110
|
+
vendorApprovalDocsUrl,
|
|
111
|
+
create,
|
|
112
|
+
clearError,
|
|
113
|
+
onCreated,
|
|
114
|
+
]);
|
|
115
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: cn("space-y-3", className), children: [_jsxs("div", { className: "space-y-3", children: [_jsx(FormField, { id: "stgm-oauth-name", label: "Name", value: name, onChange: setName, placeholder: "e.g. My Slack App", disabled: isCreating, required: true }), _jsx(FormField, { id: "stgm-oauth-provider", label: "Provider", value: provider, onChange: setProvider, placeholder: "e.g. Slack, GitHub, Salesforce", hint: "Human-readable vendor name for display", disabled: isCreating, required: true }), _jsx(FormField, { id: "stgm-oauth-client-id", label: "Client ID", value: clientId, onChange: setClientId, placeholder: "OAuth client identifier", disabled: isCreating, required: true }), _jsx(FormField, { id: "stgm-oauth-client-secret", label: "Client secret", value: clientSecret, onChange: setClientSecret, placeholder: "OAuth client secret", type: "password", disabled: isCreating, required: true }), _jsx(FormField, { id: "stgm-oauth-auth-url", label: "Authorization URL", value: authorizationUrl, onChange: setAuthorizationUrl, placeholder: "https://vendor.com/oauth/authorize", hint: "Vendor's OAuth authorization endpoint", disabled: isCreating, required: true }), _jsx(FormField, { id: "stgm-oauth-token-url", label: "Token URL", value: tokenUrl, onChange: setTokenUrl, placeholder: "https://vendor.com/oauth/token", hint: "Vendor's OAuth token exchange endpoint", disabled: isCreating, required: true }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setShowAdvanced((v) => !v), className: "text-muted-foreground hover:text-foreground flex items-center gap-1 text-[0.65rem] font-medium transition-colors", children: [_jsx(ChevronIcon, { expanded: showAdvanced }), "Advanced settings"] }), showAdvanced && (_jsxs("div", { className: "mt-2 space-y-3 border-l-2 border-border/60 pl-3", children: [_jsx(FormField, { id: "stgm-oauth-scopes", label: "Scopes", value: scopes, onChange: setScopes, placeholder: "read, write, admin", hint: "Comma-separated OAuth scopes to request", disabled: isCreating }), _jsx(FormField, { id: "stgm-oauth-userinfo-url", label: "Userinfo URL", value: userinfoUrl, onChange: setUserinfoUrl, placeholder: "https://vendor.com/userinfo", hint: "OIDC endpoint for fetching user profile data (optional)", disabled: isCreating }), _jsx(FormField, { id: "stgm-oauth-scope-param", label: "Scope parameter name", value: scopeParameterName, onChange: setScopeParameterName, placeholder: "scope", hint: 'Defaults to "scope". Some vendors use a non-standard name (e.g. "user_scope" for Slack).', disabled: isCreating }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { htmlFor: "stgm-oauth-approval-status", className: "text-xs font-medium text-foreground", children: "Vendor approval status" }), _jsxs("select", { id: "stgm-oauth-approval-status", value: vendorApprovalStatus, onChange: (e) => setVendorApprovalStatus(e.target.value), disabled: isCreating, className: cn("w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:pointer-events-none disabled:opacity-50"), children: [_jsx("option", { value: "unspecified", children: "Unspecified (treated as approved)" }), _jsx("option", { value: "pending", children: "Pending" }), _jsx("option", { value: "approved", children: "Approved" }), _jsx("option", { value: "rejected", children: "Rejected" })] }), _jsx("p", { className: "text-[0.65rem] text-muted-foreground", children: "Vendor marketplace approval lifecycle status" })] }), _jsx(FormField, { id: "stgm-oauth-approval-docs", label: "Vendor approval docs URL", value: vendorApprovalDocsUrl, onChange: setVendorApprovalDocsUrl, placeholder: "https://docs.example.com/byoa", hint: "Help link shown when vendor approval is pending", disabled: isCreating })] }))] })] }), error && (_jsx("p", { className: "text-destructive text-[0.65rem]", role: "alert", children: getUserMessage(error) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "submit", disabled: !canSubmit, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", "bg-primary text-primary-foreground hover:bg-primary/90", "disabled:pointer-events-none disabled:opacity-40"), children: [isCreating && _jsx(SpinnerIcon, {}), "Create OAuth app"] }), onCancel && (_jsx("button", { type: "button", onClick: onCancel, disabled: isCreating, className: cn("rounded-md px-3 py-1.5 text-xs", "text-muted-foreground hover:text-foreground hover:bg-accent/50", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" }))] })] }));
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Constants
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
const APPROVAL_STATUS_MAP = {
|
|
121
|
+
pending: VendorApprovalStatus.PENDING,
|
|
122
|
+
approved: VendorApprovalStatus.APPROVED,
|
|
123
|
+
rejected: VendorApprovalStatus.REJECTED,
|
|
124
|
+
};
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// FormField (internal)
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
function FormField({ id, label, value, onChange, placeholder, hint, type = "text", disabled, required, }) {
|
|
129
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx("label", { htmlFor: id, className: "text-xs font-medium text-foreground", children: label }), _jsx("input", { id: id, type: type, value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled, required: required, className: cn("w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:pointer-events-none disabled:opacity-50") }), hint && (_jsx("p", { className: "text-[0.65rem] text-muted-foreground", children: hint }))] }));
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Icons
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
function ChevronIcon({ expanded }) {
|
|
135
|
+
return (_jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: cn("shrink-0 transition-transform", expanded && "rotate-90"), children: _jsx("path", { d: "M6 4l4 4-4 4" }) }));
|
|
136
|
+
}
|
|
137
|
+
function SpinnerIcon() {
|
|
138
|
+
return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", className: "animate-spin", "aria-hidden": "true", children: _jsx("path", { d: "M8 2a6 6 0 1 0 6 6" }) }));
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=CreateOAuthAppForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateOAuthAppForm.js","sourceRoot":"","sources":["../../src/oauth-app/CreateOAuthAppForm.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
2
|
+
/** Props for {@link OAuthAppDetailPanel}. */
|
|
3
|
+
export interface OAuthAppDetailPanelProps {
|
|
4
|
+
/** The OAuth app resource to display and edit. */
|
|
5
|
+
readonly oauthApp: OAuthApp;
|
|
6
|
+
/** Fired with the updated resource after a successful save. */
|
|
7
|
+
readonly onUpdated?: (app: OAuthApp) => void;
|
|
8
|
+
/** Fired after the resource is successfully deleted. */
|
|
9
|
+
readonly onDeleted?: () => void;
|
|
10
|
+
/** Fired when the user clicks the back button. */
|
|
11
|
+
readonly onBack?: () => void;
|
|
12
|
+
/** Additional CSS class names for the root container. */
|
|
13
|
+
readonly className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* View and edit panel for an existing OAuth app.
|
|
17
|
+
*
|
|
18
|
+
* In **view mode**, displays all OAuth configuration fields in a
|
|
19
|
+
* structured label/value layout with "Edit" and "Delete" buttons.
|
|
20
|
+
*
|
|
21
|
+
* In **edit mode**, fields become editable inputs. The client secret
|
|
22
|
+
* field shows a placeholder — leave it empty to keep the existing
|
|
23
|
+
* secret, or enter a new value to replace it. "Save" submits the
|
|
24
|
+
* update via {@link useUpdateOAuthApp}; "Cancel" discards changes
|
|
25
|
+
* and returns to view mode.
|
|
26
|
+
*
|
|
27
|
+
* Delete uses an inline confirmation pattern (no modal) to avoid
|
|
28
|
+
* portal/z-index issues for SDK embedders.
|
|
29
|
+
*
|
|
30
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <OAuthAppDetailPanel
|
|
35
|
+
* oauthApp={app}
|
|
36
|
+
* onUpdated={(updated) => refetch()}
|
|
37
|
+
* onDeleted={() => { refetch(); setFlow({ phase: "idle" }); }}
|
|
38
|
+
* onBack={() => setFlow({ phase: "idle" })}
|
|
39
|
+
* />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function OAuthAppDetailPanel({ oauthApp, onUpdated, onDeleted, onBack, className, }: OAuthAppDetailPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=OAuthAppDetailPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthAppDetailPanel.d.ts","sourceRoot":"","sources":["../../src/oauth-app/OAuthAppDetailPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mDAAmD,CAAC;AAUlF,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC7C,wDAAwD;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,SAAS,EACT,SAAS,EACT,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,2CAiY1B"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { getUserMessage } from "@stigmer/sdk";
|
|
6
|
+
import { VendorApprovalStatus } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/spec_pb";
|
|
7
|
+
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
8
|
+
import { useUpdateOAuthApp } from "./useUpdateOAuthApp";
|
|
9
|
+
import { useDeleteOAuthApp } from "./useDeleteOAuthApp";
|
|
10
|
+
/**
|
|
11
|
+
* View and edit panel for an existing OAuth app.
|
|
12
|
+
*
|
|
13
|
+
* In **view mode**, displays all OAuth configuration fields in a
|
|
14
|
+
* structured label/value layout with "Edit" and "Delete" buttons.
|
|
15
|
+
*
|
|
16
|
+
* In **edit mode**, fields become editable inputs. The client secret
|
|
17
|
+
* field shows a placeholder — leave it empty to keep the existing
|
|
18
|
+
* secret, or enter a new value to replace it. "Save" submits the
|
|
19
|
+
* update via {@link useUpdateOAuthApp}; "Cancel" discards changes
|
|
20
|
+
* and returns to view mode.
|
|
21
|
+
*
|
|
22
|
+
* Delete uses an inline confirmation pattern (no modal) to avoid
|
|
23
|
+
* portal/z-index issues for SDK embedders.
|
|
24
|
+
*
|
|
25
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <OAuthAppDetailPanel
|
|
30
|
+
* oauthApp={app}
|
|
31
|
+
* onUpdated={(updated) => refetch()}
|
|
32
|
+
* onDeleted={() => { refetch(); setFlow({ phase: "idle" }); }}
|
|
33
|
+
* onBack={() => setFlow({ phase: "idle" })}
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function OAuthAppDetailPanel({ oauthApp, onUpdated, onDeleted, onBack, className, }) {
|
|
38
|
+
const spec = oauthApp.spec;
|
|
39
|
+
const meta = oauthApp.metadata;
|
|
40
|
+
const { update, isUpdating, error: updateError, clearError: clearUpdateError } = useUpdateOAuthApp();
|
|
41
|
+
const { deleteApp, isDeleting, error: deleteError, clearError: clearDeleteError } = useDeleteOAuthApp();
|
|
42
|
+
const [mode, setMode] = useState("view");
|
|
43
|
+
const [confirmingDelete, setConfirmingDelete] = useState(false);
|
|
44
|
+
// Edit form state — initialized from current resource
|
|
45
|
+
const [provider, setProvider] = useState(spec?.provider ?? "");
|
|
46
|
+
const [clientId, setClientId] = useState(spec?.clientId ?? "");
|
|
47
|
+
const [clientSecret, setClientSecret] = useState("");
|
|
48
|
+
const [authorizationUrl, setAuthorizationUrl] = useState(spec?.authorizationUrl ?? "");
|
|
49
|
+
const [tokenUrl, setTokenUrl] = useState(spec?.tokenUrl ?? "");
|
|
50
|
+
const [scopes, setScopes] = useState(spec?.scopes.join(", ") ?? "");
|
|
51
|
+
const [userinfoUrl, setUserinfoUrl] = useState(spec?.userinfoUrl ?? "");
|
|
52
|
+
const [scopeParameterName, setScopeParameterName] = useState(spec?.scopeParameterName ?? "");
|
|
53
|
+
const [vendorApprovalStatus, setVendorApprovalStatus] = useState(approvalStatusToKey(spec?.vendorApprovalStatus));
|
|
54
|
+
const [vendorApprovalDocsUrl, setVendorApprovalDocsUrl] = useState(spec?.vendorApprovalDocsUrl ?? "");
|
|
55
|
+
const enterEdit = useCallback(() => {
|
|
56
|
+
setProvider(spec?.provider ?? "");
|
|
57
|
+
setClientId(spec?.clientId ?? "");
|
|
58
|
+
setClientSecret("");
|
|
59
|
+
setAuthorizationUrl(spec?.authorizationUrl ?? "");
|
|
60
|
+
setTokenUrl(spec?.tokenUrl ?? "");
|
|
61
|
+
setScopes(spec?.scopes.join(", ") ?? "");
|
|
62
|
+
setUserinfoUrl(spec?.userinfoUrl ?? "");
|
|
63
|
+
setScopeParameterName(spec?.scopeParameterName ?? "");
|
|
64
|
+
setVendorApprovalStatus(approvalStatusToKey(spec?.vendorApprovalStatus));
|
|
65
|
+
setVendorApprovalDocsUrl(spec?.vendorApprovalDocsUrl ?? "");
|
|
66
|
+
clearUpdateError();
|
|
67
|
+
setMode("edit");
|
|
68
|
+
}, [spec, clearUpdateError]);
|
|
69
|
+
const cancelEdit = useCallback(() => {
|
|
70
|
+
clearUpdateError();
|
|
71
|
+
setMode("view");
|
|
72
|
+
}, [clearUpdateError]);
|
|
73
|
+
const handleSave = useCallback(async (e) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
clearUpdateError();
|
|
76
|
+
const parsedScopes = scopes
|
|
77
|
+
.split(",")
|
|
78
|
+
.map((s) => s.trim())
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
try {
|
|
81
|
+
const updated = await update({
|
|
82
|
+
name: meta?.name ?? "",
|
|
83
|
+
slug: meta?.slug,
|
|
84
|
+
org: meta?.org ?? "",
|
|
85
|
+
provider: provider.trim(),
|
|
86
|
+
clientId: clientId.trim(),
|
|
87
|
+
...(clientSecret.trim() && { clientSecret: clientSecret.trim() }),
|
|
88
|
+
authorizationUrl: authorizationUrl.trim(),
|
|
89
|
+
tokenUrl: tokenUrl.trim(),
|
|
90
|
+
...(parsedScopes.length > 0 && { scopes: parsedScopes }),
|
|
91
|
+
...(userinfoUrl.trim() && { userinfoUrl: userinfoUrl.trim() }),
|
|
92
|
+
...(scopeParameterName.trim() && {
|
|
93
|
+
scopeParameterName: scopeParameterName.trim(),
|
|
94
|
+
}),
|
|
95
|
+
...(vendorApprovalStatus !== "unspecified" && {
|
|
96
|
+
vendorApprovalStatus: APPROVAL_STATUS_MAP[vendorApprovalStatus],
|
|
97
|
+
}),
|
|
98
|
+
...(vendorApprovalDocsUrl.trim() && {
|
|
99
|
+
vendorApprovalDocsUrl: vendorApprovalDocsUrl.trim(),
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
setMode("view");
|
|
103
|
+
onUpdated?.(updated);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// error state is managed by useUpdateOAuthApp
|
|
107
|
+
}
|
|
108
|
+
}, [
|
|
109
|
+
meta, provider, clientId, clientSecret, authorizationUrl, tokenUrl,
|
|
110
|
+
scopes, userinfoUrl, scopeParameterName, vendorApprovalStatus,
|
|
111
|
+
vendorApprovalDocsUrl, update, clearUpdateError, onUpdated,
|
|
112
|
+
]);
|
|
113
|
+
const handleDelete = useCallback(async () => {
|
|
114
|
+
const id = meta?.id;
|
|
115
|
+
if (!id)
|
|
116
|
+
return;
|
|
117
|
+
clearDeleteError();
|
|
118
|
+
try {
|
|
119
|
+
await deleteApp(id);
|
|
120
|
+
onDeleted?.();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// error state is managed by useDeleteOAuthApp
|
|
124
|
+
}
|
|
125
|
+
}, [meta, deleteApp, clearDeleteError, onDeleted]);
|
|
126
|
+
const canSave = provider.trim() !== "" &&
|
|
127
|
+
clientId.trim() !== "" &&
|
|
128
|
+
authorizationUrl.trim() !== "" &&
|
|
129
|
+
tokenUrl.trim() !== "" &&
|
|
130
|
+
!isUpdating;
|
|
131
|
+
const createdAt = oauthApp.status?.audit?.specAudit?.createdAt;
|
|
132
|
+
const updatedAt = oauthApp.status?.audit?.specAudit?.updatedAt;
|
|
133
|
+
return (_jsxs("div", { className: cn("space-y-4", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [onBack && (_jsxs("button", { type: "button", onClick: onBack, className: "text-muted-foreground hover:text-foreground mb-1 flex items-center gap-1 text-xs transition-colors", children: [_jsx(ArrowLeftIcon, {}), "Back to list"] })), _jsx("h3", { className: "text-foreground truncate text-sm font-semibold", children: spec?.provider || meta?.name || "OAuth App" }), meta?.slug && (_jsx("span", { className: "text-muted-foreground font-mono text-xs", children: meta.slug }))] }), mode === "view" && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: enterEdit, className: cn("shrink-0 rounded-md px-2.5 py-1.5 text-xs font-medium", "text-muted-foreground hover:text-foreground hover:bg-accent/50", "transition-colors"), children: "Edit" }), _jsx("button", { type: "button", onClick: () => {
|
|
134
|
+
clearDeleteError();
|
|
135
|
+
setConfirmingDelete(true);
|
|
136
|
+
}, className: cn("shrink-0 rounded-md px-2.5 py-1.5 text-xs font-medium", "text-destructive/70 hover:text-destructive hover:bg-destructive/10", "transition-colors"), children: "Delete" })] }))] }), confirmingDelete && (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3", role: "alert", children: [_jsx("p", { className: "text-foreground mb-2 text-xs font-medium", children: "Delete this OAuth app?" }), _jsx("p", { className: "text-muted-foreground mb-3 text-[0.65rem]", children: "This action is permanent. Any MCP server overrides referencing this app will lose their binding." }), deleteError && (_jsx("p", { className: "text-destructive mb-2 text-[0.65rem]", role: "alert", children: getUserMessage(deleteError) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", onClick: handleDelete, disabled: isDeleting, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", "bg-destructive text-destructive-foreground hover:bg-destructive/90", "disabled:pointer-events-none disabled:opacity-40"), children: [isDeleting && _jsx(SpinnerIcon, {}), "Delete permanently"] }), _jsx("button", { type: "button", onClick: () => {
|
|
137
|
+
setConfirmingDelete(false);
|
|
138
|
+
clearDeleteError();
|
|
139
|
+
}, disabled: isDeleting, className: cn("rounded-md px-2.5 py-1.5 text-xs", "text-muted-foreground hover:text-foreground hover:bg-accent/50", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] })), mode === "view" ? (_jsx(ViewMode, { spec: spec, createdAt: createdAt, updatedAt: updatedAt })) : (_jsxs("form", { onSubmit: handleSave, className: "space-y-3", children: [_jsx(FieldInput, { id: "stgm-oauth-edit-provider", label: "Provider", value: provider, onChange: setProvider, placeholder: "e.g. Slack", disabled: isUpdating, required: true }), _jsx(FieldInput, { id: "stgm-oauth-edit-client-id", label: "Client ID", value: clientId, onChange: setClientId, placeholder: "OAuth client identifier", disabled: isUpdating, required: true }), _jsx(FieldInput, { id: "stgm-oauth-edit-client-secret", label: "Client secret", value: clientSecret, onChange: setClientSecret, placeholder: "Leave empty to keep existing secret", type: "password", hint: "Only enter a value to replace the existing secret", disabled: isUpdating }), _jsx(FieldInput, { id: "stgm-oauth-edit-auth-url", label: "Authorization URL", value: authorizationUrl, onChange: setAuthorizationUrl, placeholder: "https://vendor.com/oauth/authorize", disabled: isUpdating, required: true }), _jsx(FieldInput, { id: "stgm-oauth-edit-token-url", label: "Token URL", value: tokenUrl, onChange: setTokenUrl, placeholder: "https://vendor.com/oauth/token", disabled: isUpdating, required: true }), _jsx(FieldInput, { id: "stgm-oauth-edit-scopes", label: "Scopes", value: scopes, onChange: setScopes, placeholder: "read, write, admin", hint: "Comma-separated OAuth scopes", disabled: isUpdating }), _jsx(FieldInput, { id: "stgm-oauth-edit-userinfo-url", label: "Userinfo URL", value: userinfoUrl, onChange: setUserinfoUrl, placeholder: "https://vendor.com/userinfo", hint: "OIDC endpoint for fetching user profile data (optional)", disabled: isUpdating }), _jsx(FieldInput, { id: "stgm-oauth-edit-scope-param", label: "Scope parameter name", value: scopeParameterName, onChange: setScopeParameterName, placeholder: "scope", hint: 'Defaults to "scope". Some vendors use a non-standard name.', disabled: isUpdating }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { htmlFor: "stgm-oauth-edit-approval-status", className: "text-xs font-medium text-foreground", children: "Vendor approval status" }), _jsxs("select", { id: "stgm-oauth-edit-approval-status", value: vendorApprovalStatus, onChange: (e) => setVendorApprovalStatus(e.target.value), disabled: isUpdating, className: cn("w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:pointer-events-none disabled:opacity-50"), children: [_jsx("option", { value: "unspecified", children: "Unspecified (treated as approved)" }), _jsx("option", { value: "pending", children: "Pending" }), _jsx("option", { value: "approved", children: "Approved" }), _jsx("option", { value: "rejected", children: "Rejected" })] })] }), _jsx(FieldInput, { id: "stgm-oauth-edit-approval-docs", label: "Vendor approval docs URL", value: vendorApprovalDocsUrl, onChange: setVendorApprovalDocsUrl, placeholder: "https://docs.example.com/byoa", hint: "Help link shown when vendor approval is pending", disabled: isUpdating }), updateError && (_jsx("p", { className: "text-destructive text-[0.65rem]", role: "alert", children: getUserMessage(updateError) })), _jsxs("div", { className: "flex items-center gap-2 pt-1", children: [_jsxs("button", { type: "submit", disabled: !canSave, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", "bg-primary text-primary-foreground hover:bg-primary/90", "disabled:pointer-events-none disabled:opacity-40"), children: [isUpdating && _jsx(SpinnerIcon, {}), "Save changes"] }), _jsx("button", { type: "button", onClick: cancelEdit, disabled: isUpdating, className: cn("rounded-md px-2.5 py-1.5 text-xs", "text-muted-foreground hover:text-foreground hover:bg-accent/50", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] }))] }));
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Constants
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
const APPROVAL_STATUS_MAP = {
|
|
145
|
+
pending: VendorApprovalStatus.PENDING,
|
|
146
|
+
approved: VendorApprovalStatus.APPROVED,
|
|
147
|
+
rejected: VendorApprovalStatus.REJECTED,
|
|
148
|
+
};
|
|
149
|
+
const APPROVAL_STATUS_LABELS = {
|
|
150
|
+
[VendorApprovalStatus.UNSPECIFIED]: "Unspecified",
|
|
151
|
+
[VendorApprovalStatus.PENDING]: "Pending",
|
|
152
|
+
[VendorApprovalStatus.APPROVED]: "Approved",
|
|
153
|
+
[VendorApprovalStatus.REJECTED]: "Rejected",
|
|
154
|
+
};
|
|
155
|
+
function approvalStatusToKey(status) {
|
|
156
|
+
switch (status) {
|
|
157
|
+
case VendorApprovalStatus.PENDING:
|
|
158
|
+
return "pending";
|
|
159
|
+
case VendorApprovalStatus.APPROVED:
|
|
160
|
+
return "approved";
|
|
161
|
+
case VendorApprovalStatus.REJECTED:
|
|
162
|
+
return "rejected";
|
|
163
|
+
default:
|
|
164
|
+
return "unspecified";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// View mode
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
function ViewMode({ spec, createdAt, updatedAt, }) {
|
|
171
|
+
return (_jsxs("dl", { className: "space-y-2.5", children: [_jsx(Field, { label: "Provider", value: spec?.provider }), _jsx(Field, { label: "Client ID", value: spec?.clientId, mono: true }), _jsx(Field, { label: "Client secret", value: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(Field, { label: "Authorization URL", value: spec?.authorizationUrl, mono: true }), _jsx(Field, { label: "Token URL", value: spec?.tokenUrl, mono: true }), spec?.scopes && spec.scopes.length > 0 && (_jsx(Field, { label: "Scopes", value: spec.scopes.join(", "), mono: true })), spec?.userinfoUrl && (_jsx(Field, { label: "Userinfo URL", value: spec.userinfoUrl, mono: true })), spec?.scopeParameterName && (_jsx(Field, { label: "Scope parameter name", value: spec.scopeParameterName, mono: true })), spec?.vendorApprovalStatus !== undefined &&
|
|
172
|
+
spec.vendorApprovalStatus !== VendorApprovalStatus.UNSPECIFIED && (_jsx(Field, { label: "Vendor approval", value: APPROVAL_STATUS_LABELS[spec.vendorApprovalStatus] ??
|
|
173
|
+
"Unknown" })), spec?.vendorApprovalDocsUrl && (_jsx(Field, { label: "Approval docs", value: spec.vendorApprovalDocsUrl, mono: true })), _jsxs("div", { className: "flex gap-6", children: [createdAt && (_jsx(Field, { label: "Created", value: formatDate(timestampDate(createdAt)) })), updatedAt && (_jsx(Field, { label: "Updated", value: formatDate(timestampDate(updatedAt)) }))] })] }));
|
|
174
|
+
}
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Shared primitives
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
function Field({ label, value, mono, }) {
|
|
179
|
+
if (!value)
|
|
180
|
+
return null;
|
|
181
|
+
return (_jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground text-[0.65rem] font-medium", children: label }), _jsx("dd", { className: cn("text-foreground mt-0.5 break-all text-xs", mono && "font-mono"), children: value })] }));
|
|
182
|
+
}
|
|
183
|
+
function FieldInput({ id, label, value, onChange, placeholder, hint, type = "text", disabled, required, }) {
|
|
184
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx("label", { htmlFor: id, className: "text-xs font-medium text-foreground", children: label }), _jsx("input", { id: id, type: type, value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled, required: required, className: cn("w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:pointer-events-none disabled:opacity-50") }), hint && (_jsx("p", { className: "text-[0.65rem] text-muted-foreground", children: hint }))] }));
|
|
185
|
+
}
|
|
186
|
+
function formatDate(date) {
|
|
187
|
+
return date.toLocaleDateString(undefined, {
|
|
188
|
+
month: "short",
|
|
189
|
+
day: "numeric",
|
|
190
|
+
year: "numeric",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Icons
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
function ArrowLeftIcon() {
|
|
197
|
+
return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M10 3L5 8l5 5" }) }));
|
|
198
|
+
}
|
|
199
|
+
function SpinnerIcon() {
|
|
200
|
+
return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", className: "animate-spin", "aria-hidden": "true", children: _jsx("path", { d: "M8 2a6 6 0 1 0 6 6" }) }));
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=OAuthAppDetailPanel.js.map
|