@solidxai/core-ui 0.1.8-beta.8 → 0.1.8-beta.9
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/dist/components/auth/AuthLayout.d.ts.map +1 -1
- package/dist/components/auth/AuthLayout.js +16 -32
- package/dist/components/auth/AuthLayout.js.map +1 -1
- package/dist/components/auth/AuthLayout.tsx +4 -20
- package/dist/components/auth/AuthTabs.d.ts.map +1 -1
- package/dist/components/auth/AuthTabs.js +9 -9
- package/dist/components/auth/AuthTabs.js.map +1 -1
- package/dist/components/auth/AuthTabs.tsx +14 -15
- package/dist/components/auth/SolidRegister.d.ts.map +1 -1
- package/dist/components/auth/SolidRegister.js +1 -1
- package/dist/components/auth/SolidRegister.js.map +1 -1
- package/dist/components/auth/SolidRegister.tsx +12 -5
- package/dist/components/common/GeneralSettings.d.ts.map +1 -1
- package/dist/components/common/GeneralSettings.js +41 -46
- package/dist/components/common/GeneralSettings.js.map +1 -1
- package/dist/components/common/GeneralSettings.tsx +0 -19
- package/dist/components/common/SolidThemeProvider.d.ts.map +1 -1
- package/dist/components/common/SolidThemeProvider.js +5 -21
- package/dist/components/common/SolidThemeProvider.js.map +1 -1
- package/dist/components/common/SolidThemeProvider.tsx +6 -24
- package/dist/components/core/form/SolidFormView.js +42 -41
- package/dist/components/core/form/SolidFormView.js.map +1 -1
- package/dist/components/core/form/SolidFormView.tsx +16 -16
- package/dist/components/core/form/fields/widgets/SolidS3FileViewerWidget.d.ts +1 -1
- package/dist/components/core/form/fields/widgets/SolidS3FileViewerWidget.js +1 -1
- package/dist/components/core/form/fields/widgets/SolidS3FileViewerWidget.js.map +1 -1
- package/dist/components/core/form/fields/widgets/SolidS3FileViewerWidget.tsx +1 -1
- package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.js +41 -44
- package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.tsx +16 -25
- package/dist/components/core/list/SolidListView.d.ts.map +1 -1
- package/dist/components/core/list/SolidListView.js +3 -11
- package/dist/components/core/list/SolidListView.js.map +1 -1
- package/dist/components/core/list/SolidListView.tsx +27 -48
- package/dist/components/core/users/ApiKeysTab/ApiKeysTab.css +283 -9
- package/dist/components/core/users/ApiKeysTab/ApiKeysTab.d.ts.map +1 -1
- package/dist/components/core/users/ApiKeysTab/ApiKeysTab.js +35 -28
- package/dist/components/core/users/ApiKeysTab/ApiKeysTab.js.map +1 -1
- package/dist/components/core/users/ApiKeysTab/ApiKeysTab.tsx +64 -62
- package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.d.ts +2 -1
- package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.d.ts.map +1 -1
- package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js +4 -4
- package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js.map +1 -1
- package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx +17 -10
- package/dist/components/core/users/ApiKeysTab/RevealApiKeyModal.d.ts.map +1 -1
- package/dist/components/core/users/ApiKeysTab/RevealApiKeyModal.js +2 -19
- package/dist/components/core/users/ApiKeysTab/RevealApiKeyModal.js.map +1 -1
- package/dist/components/core/users/ApiKeysTab/RevealApiKeyModal.tsx +24 -43
- package/dist/components/core/users/CreateUser.css +114 -0
- package/dist/components/core/users/CreateUser.d.ts +1 -0
- package/dist/components/core/users/CreateUser.d.ts.map +1 -1
- package/dist/components/core/users/CreateUser.js +4 -3
- package/dist/components/core/users/CreateUser.js.map +1 -1
- package/dist/components/core/users/CreateUser.tsx +32 -26
- package/dist/components/layout/context/layoutcontext.js +2 -2
- package/dist/components/layout/context/layoutcontext.js.map +1 -1
- package/dist/components/layout/context/layoutcontext.tsx +2 -2
- package/dist/components/shad-cn-ui/SolidConfirmDialog.d.ts +6 -1
- package/dist/components/shad-cn-ui/SolidConfirmDialog.d.ts.map +1 -1
- package/dist/components/shad-cn-ui/SolidConfirmDialog.js +3 -3
- package/dist/components/shad-cn-ui/SolidConfirmDialog.js.map +1 -1
- package/dist/components/shad-cn-ui/SolidConfirmDialog.tsx +15 -3
- package/dist/components/shad-cn-ui/SolidTabs.d.ts.map +1 -1
- package/dist/components/shad-cn-ui/SolidTabs.js +2 -2
- package/dist/components/shad-cn-ui/SolidTabs.js.map +1 -1
- package/dist/components/shad-cn-ui/SolidTabs.tsx +0 -2
- package/dist/resources/globals.css +92 -77
- package/dist/resources/shadcn-base.css +62 -304
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
2
|
import { useDispatch } from "react-redux";
|
|
3
3
|
import { KeyRound, Plus } from "lucide-react";
|
|
4
4
|
import "./ApiKeysTab.css";
|
|
@@ -33,33 +33,43 @@ function ApiKeysTable({
|
|
|
33
33
|
keys,
|
|
34
34
|
onToggleActive,
|
|
35
35
|
isTogglingId,
|
|
36
|
+
onGenerateFirstKey,
|
|
37
|
+
canCreate,
|
|
36
38
|
}: {
|
|
37
39
|
keys: ApiKeyRecord[];
|
|
38
40
|
onToggleActive: (key: ApiKeyRecord) => void;
|
|
39
41
|
isTogglingId: string | null;
|
|
42
|
+
onGenerateFirstKey: () => void;
|
|
43
|
+
canCreate: boolean;
|
|
40
44
|
}) {
|
|
41
45
|
if (keys.length === 0) {
|
|
42
46
|
return (
|
|
43
|
-
<div
|
|
44
|
-
className="
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<KeyRound size={32} strokeWidth={1.5} />
|
|
47
|
+
<div className="solid-api-keys-empty">
|
|
48
|
+
<div className="solid-api-keys-empty-icon">
|
|
49
|
+
<KeyRound size={30} strokeWidth={1.6} />
|
|
50
|
+
</div>
|
|
48
51
|
<div className="text-center">
|
|
49
|
-
<p className="m-0"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
<p className="solid-api-keys-empty-title m-0">No API keys yet</p>
|
|
53
|
+
<p className="solid-api-keys-empty-copy m-0">
|
|
54
|
+
{canCreate
|
|
55
|
+
? "Generate a key to enable secure programmatic access for this user."
|
|
56
|
+
: "API key generation is disabled for this user account."}
|
|
54
57
|
</p>
|
|
55
58
|
</div>
|
|
59
|
+
{canCreate ? (
|
|
60
|
+
<SolidButton type="button" onClick={onGenerateFirstKey}>
|
|
61
|
+
<Plus size={14} />
|
|
62
|
+
Generate First Key
|
|
63
|
+
</SolidButton>
|
|
64
|
+
) : null}
|
|
56
65
|
</div>
|
|
57
66
|
);
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
return (
|
|
61
|
-
<div
|
|
62
|
-
<
|
|
70
|
+
<div className="solid-api-keys-table-shell">
|
|
71
|
+
<div className="solid-api-keys-table-wrap">
|
|
72
|
+
<table className="solid-api-keys-table">
|
|
63
73
|
<colgroup>
|
|
64
74
|
<col style={{ width: "18%" }} />
|
|
65
75
|
<col style={{ width: "22%" }} />
|
|
@@ -75,7 +85,7 @@ function ApiKeysTable({
|
|
|
75
85
|
<th className="solid-api-keys-th">Status</th>
|
|
76
86
|
<th className="solid-api-keys-th">Expires</th>
|
|
77
87
|
<th className="solid-api-keys-th">Last Used</th>
|
|
78
|
-
<th className="solid-api-keys-th
|
|
88
|
+
<th className="solid-api-keys-th solid-api-keys-th--right">Active</th>
|
|
79
89
|
</tr>
|
|
80
90
|
</thead>
|
|
81
91
|
<tbody>
|
|
@@ -86,21 +96,11 @@ function ApiKeysTable({
|
|
|
86
96
|
return (
|
|
87
97
|
<tr key={key.id} className={isExpired ? "solid-api-keys-row--expired" : undefined}>
|
|
88
98
|
<td className="solid-api-keys-td">
|
|
89
|
-
<span
|
|
99
|
+
<span className="solid-api-keys-name">{key.name}</span>
|
|
90
100
|
</td>
|
|
91
101
|
|
|
92
102
|
<td className="solid-api-keys-td">
|
|
93
|
-
<code
|
|
94
|
-
style={{
|
|
95
|
-
fontFamily: "monospace",
|
|
96
|
-
fontSize: 13,
|
|
97
|
-
background: "var(--solid-surface-secondary, #f5f5f5)",
|
|
98
|
-
padding: "2px 6px",
|
|
99
|
-
borderRadius: 4,
|
|
100
|
-
}}
|
|
101
|
-
>
|
|
102
|
-
{key.maskedKey}
|
|
103
|
-
</code>
|
|
103
|
+
<code className="solid-api-keys-code">{key.maskedKey}</code>
|
|
104
104
|
</td>
|
|
105
105
|
|
|
106
106
|
<td className="solid-api-keys-td">
|
|
@@ -115,29 +115,19 @@ function ApiKeysTable({
|
|
|
115
115
|
)}
|
|
116
116
|
</td>
|
|
117
117
|
|
|
118
|
-
<td
|
|
119
|
-
className="solid-api-keys-td"
|
|
120
|
-
style={{
|
|
121
|
-
color:
|
|
122
|
-
expiryStatus === "expired"
|
|
123
|
-
? "var(--solid-danger-color, #ef4444)"
|
|
124
|
-
: expiryStatus === "expiring-soon"
|
|
125
|
-
? "var(--solid-warn-color, #f59e0b)"
|
|
126
|
-
: undefined,
|
|
127
|
-
}}
|
|
128
|
-
>
|
|
118
|
+
<td className={`solid-api-keys-td ${expiryStatus === "expired" ? "solid-api-keys-date--expired" : ""} ${expiryStatus === "expiring-soon" ? "solid-api-keys-date--warning" : ""}`}>
|
|
129
119
|
{expiryStatus === "never" ? (
|
|
130
|
-
<span
|
|
120
|
+
<span className="solid-api-keys-muted">Never</span>
|
|
131
121
|
) : (
|
|
132
122
|
formatDate(key.expiresAt)
|
|
133
123
|
)}
|
|
134
124
|
</td>
|
|
135
125
|
|
|
136
|
-
<td className="solid-api-keys-td
|
|
126
|
+
<td className="solid-api-keys-td solid-api-keys-muted">
|
|
137
127
|
{formatDate(key.lastUsedAt)}
|
|
138
128
|
</td>
|
|
139
129
|
|
|
140
|
-
<td className="solid-api-keys-td
|
|
130
|
+
<td className="solid-api-keys-td solid-api-keys-td--right">
|
|
141
131
|
{isTogglingId === key.id ? (
|
|
142
132
|
<SolidSpinner />
|
|
143
133
|
) : (
|
|
@@ -152,7 +142,8 @@ function ApiKeysTable({
|
|
|
152
142
|
);
|
|
153
143
|
})}
|
|
154
144
|
</tbody>
|
|
155
|
-
|
|
145
|
+
</table>
|
|
146
|
+
</div>
|
|
156
147
|
</div>
|
|
157
148
|
);
|
|
158
149
|
}
|
|
@@ -164,13 +155,18 @@ interface ApiKeysTabProps {
|
|
|
164
155
|
|
|
165
156
|
export function ApiKeysTab({ userId, canCreate = false }: ApiKeysTabProps) {
|
|
166
157
|
const dispatch = useDispatch();
|
|
167
|
-
const { data, isLoading, isError } = useGetUserApiKeysQuery(userId);
|
|
158
|
+
const { data, isLoading, isError, refetch } = useGetUserApiKeysQuery(userId);
|
|
168
159
|
const [updateApiKey] = useUpdateApiKeyMutation();
|
|
169
160
|
const [togglingId, setTogglingId] = useState<string | null>(null);
|
|
170
161
|
const [showGenerate, setShowGenerate] = useState(false);
|
|
171
162
|
const [revealKey, setRevealKey] = useState<{ apiKey: string; keyName: string } | null>(null);
|
|
163
|
+
const [optimisticKeys, setOptimisticKeys] = useState<ApiKeyRecord[]>([]);
|
|
172
164
|
|
|
173
|
-
const keys
|
|
165
|
+
const keys = useMemo(() => {
|
|
166
|
+
const serverKeys = data?.data?.apiKeys ?? [];
|
|
167
|
+
const merged = [...optimisticKeys.filter((key) => !serverKeys.some((serverKey) => serverKey.id === key.id)), ...serverKeys];
|
|
168
|
+
return merged.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
169
|
+
}, [data?.data?.apiKeys, optimisticKeys]);
|
|
174
170
|
|
|
175
171
|
const handleToggle = async (key: ApiKeyRecord) => {
|
|
176
172
|
setTogglingId(key.id);
|
|
@@ -199,37 +195,41 @@ export function ApiKeysTab({ userId, canCreate = false }: ApiKeysTabProps) {
|
|
|
199
195
|
return (
|
|
200
196
|
<>
|
|
201
197
|
<div className="solid-api-keys-tab">
|
|
202
|
-
<div className="
|
|
203
|
-
<div>
|
|
204
|
-
<p className="m-0"
|
|
205
|
-
|
|
206
|
-
</p>
|
|
207
|
-
<p className="m-0 mt-1" style={{ fontSize: 12, color: "var(--solid-text-secondary, #888)" }}>
|
|
198
|
+
<div className="solid-api-keys-hero">
|
|
199
|
+
<div className="solid-api-keys-copy">
|
|
200
|
+
<p className="solid-api-keys-title m-0">API Keys</p>
|
|
201
|
+
<p className="solid-api-keys-subtitle m-0">
|
|
208
202
|
Keys grant programmatic access. Store them securely — they are shown only once.
|
|
209
203
|
</p>
|
|
210
204
|
</div>
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
205
|
+
<div className="solid-api-keys-actions">
|
|
206
|
+
<span className="solid-api-keys-count">{keys.length} key{keys.length === 1 ? "" : "s"}</span>
|
|
207
|
+
{canCreate ? (
|
|
208
|
+
<SolidButton size="small" type="button" onClick={() => setShowGenerate(true)}>
|
|
209
|
+
<Plus size={14} />
|
|
210
|
+
Generate Key
|
|
211
|
+
</SolidButton>
|
|
212
|
+
) : null}
|
|
213
|
+
</div>
|
|
217
214
|
</div>
|
|
218
215
|
|
|
219
216
|
{isLoading ? (
|
|
220
|
-
<div className="
|
|
217
|
+
<div className="solid-api-keys-state">
|
|
221
218
|
<SolidSpinner />
|
|
222
219
|
</div>
|
|
223
220
|
) : isError && keys.length === 0 ? (
|
|
224
|
-
<div
|
|
225
|
-
className="flex flex-column align-items-center justify-content-center gap-2 py-5"
|
|
226
|
-
style={{ color: "var(--solid-text-secondary, #888)", fontSize: 13 }}
|
|
227
|
-
>
|
|
221
|
+
<div className="solid-api-keys-error">
|
|
228
222
|
<p className="m-0">Something went wrong while loading API keys.</p>
|
|
229
223
|
<p className="m-0">Please refresh the page and try again.</p>
|
|
230
224
|
</div>
|
|
231
225
|
) : (
|
|
232
|
-
<ApiKeysTable
|
|
226
|
+
<ApiKeysTable
|
|
227
|
+
keys={keys}
|
|
228
|
+
onToggleActive={handleToggle}
|
|
229
|
+
isTogglingId={togglingId}
|
|
230
|
+
onGenerateFirstKey={() => setShowGenerate(true)}
|
|
231
|
+
canCreate={canCreate}
|
|
232
|
+
/>
|
|
233
233
|
)}
|
|
234
234
|
</div>
|
|
235
235
|
|
|
@@ -237,8 +237,10 @@ export function ApiKeysTab({ userId, canCreate = false }: ApiKeysTabProps) {
|
|
|
237
237
|
open={showGenerate}
|
|
238
238
|
userId={Number(userId)}
|
|
239
239
|
onClose={() => setShowGenerate(false)}
|
|
240
|
-
onCreated={(apiKey, keyName) => {
|
|
240
|
+
onCreated={(apiKey, keyName, record) => {
|
|
241
241
|
setShowGenerate(false);
|
|
242
|
+
setOptimisticKeys((previous) => [record, ...previous.filter((existing) => existing.id !== record.id)]);
|
|
243
|
+
void refetch();
|
|
242
244
|
setRevealKey({ apiKey, keyName });
|
|
243
245
|
}}
|
|
244
246
|
/>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type ApiKeyRecord } from "../../../../redux/api/apiKeyApi";
|
|
1
2
|
interface GenerateApiKeyModalProps {
|
|
2
3
|
open: boolean;
|
|
3
4
|
onClose: () => void;
|
|
4
|
-
onCreated: (rawApiKey: string, keyName: string) => void;
|
|
5
|
+
onCreated: (rawApiKey: string, keyName: string, record: ApiKeyRecord) => void;
|
|
5
6
|
userId?: number;
|
|
6
7
|
}
|
|
7
8
|
export declare function GenerateApiKeyModal({ open, onClose, onCreated, userId }: GenerateApiKeyModalProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenerateApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GenerateApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAoC,MAAM,iCAAiC,CAAC;AAiCtG,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAkIjG"}
|
|
@@ -50,7 +50,7 @@ import { useEffect, useState } from "react";
|
|
|
50
50
|
import { useDispatch } from "react-redux";
|
|
51
51
|
import { useFormik } from "formik";
|
|
52
52
|
import * as Yup from "yup";
|
|
53
|
-
import { SolidButton, SolidDatePicker, SolidDialog, SolidDialogBody, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, SolidInput, SolidMessage, SolidSelect, } from "../../../shad-cn-ui";
|
|
53
|
+
import { SolidButton, SolidDatePicker, SolidDialog, SolidDialogBody, SolidDialogDescription, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, SolidInput, SolidMessage, SolidSelect, } from "../../../shad-cn-ui";
|
|
54
54
|
import { showToast } from "../../../../redux/features/toastSlice";
|
|
55
55
|
import { useGenerateApiKeyForUserMutation } from "../../../../redux/api/apiKeyApi";
|
|
56
56
|
var EXPIRY_OPTIONS = [
|
|
@@ -118,7 +118,7 @@ export function GenerateApiKeyModal(_a) {
|
|
|
118
118
|
return [4 /*yield*/, generateApiKeyForUser({ userId: userId, body: body }).unwrap()];
|
|
119
119
|
case 2:
|
|
120
120
|
response = _b.sent();
|
|
121
|
-
onCreated(response.data.apiKey, values.name.trim());
|
|
121
|
+
onCreated(response.data.apiKey, values.name.trim(), response.data.record);
|
|
122
122
|
handleClose();
|
|
123
123
|
return [3 /*break*/, 4];
|
|
124
124
|
case 3:
|
|
@@ -147,11 +147,11 @@ export function GenerateApiKeyModal(_a) {
|
|
|
147
147
|
var expiryError = formik.touched.expiryOption && formik.errors.expiryOption
|
|
148
148
|
? String(formik.errors.expiryOption)
|
|
149
149
|
: "";
|
|
150
|
-
return (_jsxs(SolidDialog, { open: open, onOpenChange: handleClose, style: { maxWidth: 480 }, children: [
|
|
150
|
+
return (_jsxs(SolidDialog, { open: open, onOpenChange: handleClose, style: { maxWidth: 480 }, className: "solid-api-key-dialog", children: [_jsxs(SolidDialogHeader, { className: "solid-api-key-dialog-header", children: [_jsx(SolidDialogTitle, { children: "Generate API Key" }), _jsx(SolidDialogDescription, { children: "Create a named key for this user. The raw key will be shown only once after generation." })] }), _jsx(SolidDialogSeparator, {}), _jsx(SolidDialogBody, { className: "solid-api-key-dialog-body", children: _jsx("form", { id: "generate-api-key-form", onSubmit: formik.handleSubmit, children: _jsxs("div", { className: "solid-api-key-dialog-fields", children: [_jsxs("div", { className: "solid-api-key-dialog-field", children: [_jsxs("label", { htmlFor: "api-key-name", className: "form-field-label", children: ["Key Name ", _jsx("span", { style: { color: "var(--solid-danger-color, #ef4444)" }, children: "*" })] }), _jsx(SolidInput, { id: "api-key-name", name: "name", autoComplete: "off", placeholder: "e.g. CI/CD pipeline, Mobile app", value: formik.values.name, onChange: formik.handleChange, onBlur: formik.handleBlur }), nameError && _jsx(SolidMessage, { severity: "error", text: nameError })] }), _jsxs("div", { className: "solid-api-key-dialog-field", children: [_jsx("label", { htmlFor: "api-key-expiry", className: "form-field-label", children: "Expires" }), _jsx(SolidSelect, { value: formik.values.expiryOption, options: EXPIRY_OPTIONS, optionLabel: "label", optionValue: "value", onChange: function (_a) {
|
|
151
151
|
var value = _a.value;
|
|
152
152
|
formik.setFieldValue("expiryOption", value);
|
|
153
153
|
if (value !== "custom")
|
|
154
154
|
setCustomDate(null);
|
|
155
|
-
} }), formik.values.expiryOption === "custom" && (_jsx(SolidDatePicker, { selected: customDate, onChange: function (date) { return setCustomDate(date); }, minDate: new Date(), placeholderText: "Select expiry date", dateFormat: "dd/MM/yyyy" })), expiryError && _jsx(SolidMessage, { severity: "error", text: expiryError })] })] }) }) }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { children: [_jsx(SolidButton, { variant: "outline", type: "button", onClick: handleClose, disabled: isLoading, children: "Cancel" }), _jsx(SolidButton, { type: "submit", form: "generate-api-key-form", loading: isLoading, children: "Generate Key" })] })] }));
|
|
155
|
+
} }), formik.values.expiryOption === "custom" && (_jsx(SolidDatePicker, { selected: customDate, onChange: function (date) { return setCustomDate(date); }, minDate: new Date(), placeholderText: "Select expiry date", dateFormat: "dd/MM/yyyy" })), formik.values.expiryOption !== "custom" ? (_jsx("p", { className: "solid-api-key-dialog-hint m-0", children: "Choose how long this key should remain valid." })) : null, expiryError && _jsx(SolidMessage, { severity: "error", text: expiryError })] })] }) }) }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { className: "solid-api-key-dialog-footer", children: [_jsx(SolidButton, { variant: "outline", type: "button", onClick: handleClose, disabled: isLoading, children: "Cancel" }), _jsx(SolidButton, { type: "submit", form: "generate-api-key-form", loading: isLoading, children: "Generate Key" })] })] }));
|
|
156
156
|
}
|
|
157
157
|
//# sourceMappingURL=GenerateApiKeyModal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenerateApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useDispatch } from \"react-redux\";\nimport { useFormik } from \"formik\";\nimport * as Yup from \"yup\";\nimport {\n SolidButton,\n SolidDatePicker,\n SolidDialog,\n SolidDialogBody,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogSeparator,\n SolidDialogTitle,\n SolidInput,\n SolidMessage,\n SolidSelect,\n} from \"../../../shad-cn-ui\";\nimport { showToast } from \"../../../../redux/features/toastSlice\";\nimport { useGenerateApiKeyForUserMutation } from \"../../../../redux/api/apiKeyApi\";\n\nconst EXPIRY_OPTIONS = [\n { label: \"30 days\", value: \"30d\" },\n { label: \"60 days\", value: \"60d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"Never\", value: \"never\" },\n { label: \"Custom date\", value: \"custom\" },\n];\n\nfunction addDays(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nfunction resolveExpiresAt(expiryOption: string, customDate: Date | null): string | undefined {\n switch (expiryOption) {\n case \"30d\":\n return addDays(30);\n case \"60d\":\n return addDays(60);\n case \"90d\":\n return addDays(90);\n case \"never\":\n return undefined;\n case \"custom\":\n return customDate ? customDate.toISOString() : undefined;\n default:\n return undefined;\n }\n}\n\ninterface GenerateApiKeyModalProps {\n open: boolean;\n onClose: () => void;\n onCreated: (rawApiKey: string, keyName: string) => void;\n userId?: number;\n}\n\nexport function GenerateApiKeyModal({ open, onClose, onCreated, userId }: GenerateApiKeyModalProps) {\n const dispatch = useDispatch();\n const [generateApiKeyForUser, { isLoading }] = useGenerateApiKeyForUserMutation();\n const [customDate, setCustomDate] = useState<Date | null>(null);\n\n const formik = useFormik({\n initialValues: {\n name: \"\",\n expiryOption: \"30d\",\n },\n validationSchema: Yup.object({\n name: Yup.string()\n .trim()\n .min(2, \"Name must be at least 2 characters\")\n .max(64, \"Name must be 64 characters or fewer\")\n .required(\"Key name is required\"),\n expiryOption: Yup.string().required(\"Expiry is required\"),\n }),\n onSubmit: async (values, helpers) => {\n if (values.expiryOption === \"custom\" && !customDate) {\n helpers.setFieldError(\"expiryOption\", \"Please select a custom expiry date\");\n return;\n }\n\n const expiresAt = resolveExpiresAt(values.expiryOption, customDate);\n\n try {\n const body = { name: values.name.trim(), ...(expiresAt ? { expiresAt } : {}) };\n const response = await generateApiKeyForUser({ userId: userId!, body }).unwrap();\n\n onCreated(response.data.apiKey, values.name.trim());\n handleClose();\n } catch (err: any) {\n const detail = err?.data?.message || \"Failed to generate API key. Please try again.\";\n dispatch(showToast({ severity: \"error\", summary: \"Error\", detail }));\n }\n },\n });\n\n const handleClose = () => {\n formik.resetForm();\n setCustomDate(null);\n onClose();\n };\n\n // Reset form when modal opens\n useEffect(() => {\n if (open) {\n formik.resetForm();\n setCustomDate(null);\n }\n }, [open]);\n\n const nameError =\n formik.touched.name && formik.errors.name ? String(formik.errors.name) : \"\";\n const expiryError =\n formik.touched.expiryOption && formik.errors.expiryOption\n ? String(formik.errors.expiryOption)\n : \"\";\n\n return (\n <SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }}>\n <SolidDialogHeader>\n <SolidDialogTitle>Generate API Key</SolidDialogTitle>\n </SolidDialogHeader>\n <SolidDialogSeparator />\n <SolidDialogBody>\n <form id=\"generate-api-key-form\" onSubmit={formik.handleSubmit}>\n <div className=\"flex flex-column gap-3\">\n <div className=\"flex flex-column gap-2\">\n <label htmlFor=\"api-key-name\" className=\"form-field-label\">\n Key Name <span style={{ color: \"var(--solid-danger-color, #ef4444)\" }}>*</span>\n </label>\n <SolidInput\n id=\"api-key-name\"\n name=\"name\"\n autoComplete=\"off\"\n placeholder=\"e.g. CI/CD pipeline, Mobile app\"\n value={formik.values.name}\n onChange={formik.handleChange}\n onBlur={formik.handleBlur}\n />\n {nameError && <SolidMessage severity=\"error\" text={nameError} />}\n </div>\n\n <div className=\"flex flex-column gap-2\">\n <label htmlFor=\"api-key-expiry\" className=\"form-field-label\">\n Expires\n </label>\n <SolidSelect\n value={formik.values.expiryOption}\n options={EXPIRY_OPTIONS}\n optionLabel=\"label\"\n optionValue=\"value\"\n onChange={({ value }) => {\n formik.setFieldValue(\"expiryOption\", value);\n if (value !== \"custom\") setCustomDate(null);\n }}\n />\n {formik.values.expiryOption === \"custom\" && (\n <SolidDatePicker\n selected={customDate}\n onChange={(date: Date | null) => setCustomDate(date)}\n minDate={new Date()}\n placeholderText=\"Select expiry date\"\n dateFormat=\"dd/MM/yyyy\"\n />\n )}\n {expiryError && <SolidMessage severity=\"error\" text={expiryError} />}\n </div>\n </div>\n </form>\n </SolidDialogBody>\n <SolidDialogSeparator />\n <SolidDialogFooter>\n <SolidButton variant=\"outline\" type=\"button\" onClick={handleClose} disabled={isLoading}>\n Cancel\n </SolidButton>\n <SolidButton type=\"submit\" form=\"generate-api-key-form\" loading={isLoading}>\n Generate Key\n </SolidButton>\n </SolidDialogFooter>\n </SolidDialog>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"GenerateApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useDispatch } from \"react-redux\";\nimport { useFormik } from \"formik\";\nimport * as Yup from \"yup\";\nimport {\n SolidButton,\n SolidDatePicker,\n SolidDialog,\n SolidDialogBody,\n SolidDialogDescription,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogSeparator,\n SolidDialogTitle,\n SolidInput,\n SolidMessage,\n SolidSelect,\n} from \"../../../shad-cn-ui\";\nimport { showToast } from \"../../../../redux/features/toastSlice\";\nimport { type ApiKeyRecord, useGenerateApiKeyForUserMutation } from \"../../../../redux/api/apiKeyApi\";\n\nconst EXPIRY_OPTIONS = [\n { label: \"30 days\", value: \"30d\" },\n { label: \"60 days\", value: \"60d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"Never\", value: \"never\" },\n { label: \"Custom date\", value: \"custom\" },\n];\n\nfunction addDays(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nfunction resolveExpiresAt(expiryOption: string, customDate: Date | null): string | undefined {\n switch (expiryOption) {\n case \"30d\":\n return addDays(30);\n case \"60d\":\n return addDays(60);\n case \"90d\":\n return addDays(90);\n case \"never\":\n return undefined;\n case \"custom\":\n return customDate ? customDate.toISOString() : undefined;\n default:\n return undefined;\n }\n}\n\ninterface GenerateApiKeyModalProps {\n open: boolean;\n onClose: () => void;\n onCreated: (rawApiKey: string, keyName: string, record: ApiKeyRecord) => void;\n userId?: number;\n}\n\nexport function GenerateApiKeyModal({ open, onClose, onCreated, userId }: GenerateApiKeyModalProps) {\n const dispatch = useDispatch();\n const [generateApiKeyForUser, { isLoading }] = useGenerateApiKeyForUserMutation();\n const [customDate, setCustomDate] = useState<Date | null>(null);\n\n const formik = useFormik({\n initialValues: {\n name: \"\",\n expiryOption: \"30d\",\n },\n validationSchema: Yup.object({\n name: Yup.string()\n .trim()\n .min(2, \"Name must be at least 2 characters\")\n .max(64, \"Name must be 64 characters or fewer\")\n .required(\"Key name is required\"),\n expiryOption: Yup.string().required(\"Expiry is required\"),\n }),\n onSubmit: async (values, helpers) => {\n if (values.expiryOption === \"custom\" && !customDate) {\n helpers.setFieldError(\"expiryOption\", \"Please select a custom expiry date\");\n return;\n }\n\n const expiresAt = resolveExpiresAt(values.expiryOption, customDate);\n\n try {\n const body = { name: values.name.trim(), ...(expiresAt ? { expiresAt } : {}) };\n const response = await generateApiKeyForUser({ userId: userId!, body }).unwrap();\n\n onCreated(response.data.apiKey, values.name.trim(), response.data.record);\n handleClose();\n } catch (err: any) {\n const detail = err?.data?.message || \"Failed to generate API key. Please try again.\";\n dispatch(showToast({ severity: \"error\", summary: \"Error\", detail }));\n }\n },\n });\n\n const handleClose = () => {\n formik.resetForm();\n setCustomDate(null);\n onClose();\n };\n\n // Reset form when modal opens\n useEffect(() => {\n if (open) {\n formik.resetForm();\n setCustomDate(null);\n }\n }, [open]);\n\n const nameError =\n formik.touched.name && formik.errors.name ? String(formik.errors.name) : \"\";\n const expiryError =\n formik.touched.expiryOption && formik.errors.expiryOption\n ? String(formik.errors.expiryOption)\n : \"\";\n\n return (\n <SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }} className=\"solid-api-key-dialog\">\n <SolidDialogHeader className=\"solid-api-key-dialog-header\">\n <SolidDialogTitle>Generate API Key</SolidDialogTitle>\n <SolidDialogDescription>\n Create a named key for this user. The raw key will be shown only once after generation.\n </SolidDialogDescription>\n </SolidDialogHeader>\n <SolidDialogSeparator />\n <SolidDialogBody className=\"solid-api-key-dialog-body\">\n <form id=\"generate-api-key-form\" onSubmit={formik.handleSubmit}>\n <div className=\"solid-api-key-dialog-fields\">\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-name\" className=\"form-field-label\">\n Key Name <span style={{ color: \"var(--solid-danger-color, #ef4444)\" }}>*</span>\n </label>\n <SolidInput\n id=\"api-key-name\"\n name=\"name\"\n autoComplete=\"off\"\n placeholder=\"e.g. CI/CD pipeline, Mobile app\"\n value={formik.values.name}\n onChange={formik.handleChange}\n onBlur={formik.handleBlur}\n />\n {nameError && <SolidMessage severity=\"error\" text={nameError} />}\n </div>\n\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-expiry\" className=\"form-field-label\">\n Expires\n </label>\n <SolidSelect\n value={formik.values.expiryOption}\n options={EXPIRY_OPTIONS}\n optionLabel=\"label\"\n optionValue=\"value\"\n onChange={({ value }) => {\n formik.setFieldValue(\"expiryOption\", value);\n if (value !== \"custom\") setCustomDate(null);\n }}\n />\n {formik.values.expiryOption === \"custom\" && (\n <SolidDatePicker\n selected={customDate}\n onChange={(date: Date | null) => setCustomDate(date)}\n minDate={new Date()}\n placeholderText=\"Select expiry date\"\n dateFormat=\"dd/MM/yyyy\"\n />\n )}\n {formik.values.expiryOption !== \"custom\" ? (\n <p className=\"solid-api-key-dialog-hint m-0\">Choose how long this key should remain valid.</p>\n ) : null}\n {expiryError && <SolidMessage severity=\"error\" text={expiryError} />}\n </div>\n </div>\n </form>\n </SolidDialogBody>\n <SolidDialogSeparator />\n <SolidDialogFooter className=\"solid-api-key-dialog-footer\">\n <SolidButton variant=\"outline\" type=\"button\" onClick={handleClose} disabled={isLoading}>\n Cancel\n </SolidButton>\n <SolidButton type=\"submit\" form=\"generate-api-key-form\" loading={isLoading}>\n Generate Key\n </SolidButton>\n </SolidDialogFooter>\n </SolidDialog>\n );\n}\n"]}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
SolidDatePicker,
|
|
8
8
|
SolidDialog,
|
|
9
9
|
SolidDialogBody,
|
|
10
|
+
SolidDialogDescription,
|
|
10
11
|
SolidDialogFooter,
|
|
11
12
|
SolidDialogHeader,
|
|
12
13
|
SolidDialogSeparator,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
SolidSelect,
|
|
17
18
|
} from "../../../shad-cn-ui";
|
|
18
19
|
import { showToast } from "../../../../redux/features/toastSlice";
|
|
19
|
-
import { useGenerateApiKeyForUserMutation } from "../../../../redux/api/apiKeyApi";
|
|
20
|
+
import { type ApiKeyRecord, useGenerateApiKeyForUserMutation } from "../../../../redux/api/apiKeyApi";
|
|
20
21
|
|
|
21
22
|
const EXPIRY_OPTIONS = [
|
|
22
23
|
{ label: "30 days", value: "30d" },
|
|
@@ -52,7 +53,7 @@ function resolveExpiresAt(expiryOption: string, customDate: Date | null): string
|
|
|
52
53
|
interface GenerateApiKeyModalProps {
|
|
53
54
|
open: boolean;
|
|
54
55
|
onClose: () => void;
|
|
55
|
-
onCreated: (rawApiKey: string, keyName: string) => void;
|
|
56
|
+
onCreated: (rawApiKey: string, keyName: string, record: ApiKeyRecord) => void;
|
|
56
57
|
userId?: number;
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -86,7 +87,7 @@ export function GenerateApiKeyModal({ open, onClose, onCreated, userId }: Genera
|
|
|
86
87
|
const body = { name: values.name.trim(), ...(expiresAt ? { expiresAt } : {}) };
|
|
87
88
|
const response = await generateApiKeyForUser({ userId: userId!, body }).unwrap();
|
|
88
89
|
|
|
89
|
-
onCreated(response.data.apiKey, values.name.trim());
|
|
90
|
+
onCreated(response.data.apiKey, values.name.trim(), response.data.record);
|
|
90
91
|
handleClose();
|
|
91
92
|
} catch (err: any) {
|
|
92
93
|
const detail = err?.data?.message || "Failed to generate API key. Please try again.";
|
|
@@ -117,15 +118,18 @@ export function GenerateApiKeyModal({ open, onClose, onCreated, userId }: Genera
|
|
|
117
118
|
: "";
|
|
118
119
|
|
|
119
120
|
return (
|
|
120
|
-
<SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }}>
|
|
121
|
-
<SolidDialogHeader>
|
|
121
|
+
<SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }} className="solid-api-key-dialog">
|
|
122
|
+
<SolidDialogHeader className="solid-api-key-dialog-header">
|
|
122
123
|
<SolidDialogTitle>Generate API Key</SolidDialogTitle>
|
|
124
|
+
<SolidDialogDescription>
|
|
125
|
+
Create a named key for this user. The raw key will be shown only once after generation.
|
|
126
|
+
</SolidDialogDescription>
|
|
123
127
|
</SolidDialogHeader>
|
|
124
128
|
<SolidDialogSeparator />
|
|
125
|
-
<SolidDialogBody>
|
|
129
|
+
<SolidDialogBody className="solid-api-key-dialog-body">
|
|
126
130
|
<form id="generate-api-key-form" onSubmit={formik.handleSubmit}>
|
|
127
|
-
<div className="
|
|
128
|
-
<div className="
|
|
131
|
+
<div className="solid-api-key-dialog-fields">
|
|
132
|
+
<div className="solid-api-key-dialog-field">
|
|
129
133
|
<label htmlFor="api-key-name" className="form-field-label">
|
|
130
134
|
Key Name <span style={{ color: "var(--solid-danger-color, #ef4444)" }}>*</span>
|
|
131
135
|
</label>
|
|
@@ -141,7 +145,7 @@ export function GenerateApiKeyModal({ open, onClose, onCreated, userId }: Genera
|
|
|
141
145
|
{nameError && <SolidMessage severity="error" text={nameError} />}
|
|
142
146
|
</div>
|
|
143
147
|
|
|
144
|
-
<div className="
|
|
148
|
+
<div className="solid-api-key-dialog-field">
|
|
145
149
|
<label htmlFor="api-key-expiry" className="form-field-label">
|
|
146
150
|
Expires
|
|
147
151
|
</label>
|
|
@@ -164,13 +168,16 @@ export function GenerateApiKeyModal({ open, onClose, onCreated, userId }: Genera
|
|
|
164
168
|
dateFormat="dd/MM/yyyy"
|
|
165
169
|
/>
|
|
166
170
|
)}
|
|
171
|
+
{formik.values.expiryOption !== "custom" ? (
|
|
172
|
+
<p className="solid-api-key-dialog-hint m-0">Choose how long this key should remain valid.</p>
|
|
173
|
+
) : null}
|
|
167
174
|
{expiryError && <SolidMessage severity="error" text={expiryError} />}
|
|
168
175
|
</div>
|
|
169
176
|
</div>
|
|
170
177
|
</form>
|
|
171
178
|
</SolidDialogBody>
|
|
172
179
|
<SolidDialogSeparator />
|
|
173
|
-
<SolidDialogFooter>
|
|
180
|
+
<SolidDialogFooter className="solid-api-key-dialog-footer">
|
|
174
181
|
<SolidButton variant="outline" type="button" onClick={handleClose} disabled={isLoading}>
|
|
175
182
|
Cancel
|
|
176
183
|
</SolidButton>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RevealApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/RevealApiKeyModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RevealApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/RevealApiKeyModal.tsx"],"names":[],"mappings":"AAaA,UAAU,sBAAsB;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,sBAAsB,2CAuE3F"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { AlertTriangle, CheckCircle, Copy } from "lucide-react";
|
|
4
|
-
import { SolidButton, SolidDialog, SolidDialogBody, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, } from "../../../shad-cn-ui";
|
|
4
|
+
import { SolidButton, SolidDialog, SolidDialogBody, SolidDialogDescription, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, } from "../../../shad-cn-ui";
|
|
5
5
|
export function RevealApiKeyModal(_a) {
|
|
6
6
|
var open = _a.open, apiKey = _a.apiKey, keyName = _a.keyName, onClose = _a.onClose;
|
|
7
7
|
var _b = useState(false), copied = _b[0], setCopied = _b[1];
|
|
@@ -15,23 +15,6 @@ export function RevealApiKeyModal(_a) {
|
|
|
15
15
|
setCopied(false);
|
|
16
16
|
onClose();
|
|
17
17
|
};
|
|
18
|
-
return (_jsxs(SolidDialog, { open: open, onOpenChange: handleClose, dismissible: false, style: { maxWidth: 520 }, children: [
|
|
19
|
-
background: "var(--solid-surface-secondary, #f5f5f5)",
|
|
20
|
-
borderRadius: 6,
|
|
21
|
-
border: "1px solid var(--solid-border-color, #e0e0e0)",
|
|
22
|
-
}, children: [_jsx("code", { className: "flex-1", style: {
|
|
23
|
-
fontFamily: "monospace",
|
|
24
|
-
fontSize: 13,
|
|
25
|
-
wordBreak: "break-all",
|
|
26
|
-
userSelect: "all",
|
|
27
|
-
}, children: apiKey }), _jsx("button", { type: "button", title: copied ? "Copied!" : "Copy to clipboard", onClick: handleCopy, style: {
|
|
28
|
-
background: "none",
|
|
29
|
-
border: "none",
|
|
30
|
-
cursor: "pointer",
|
|
31
|
-
padding: "4px",
|
|
32
|
-
display: "flex",
|
|
33
|
-
alignItems: "center",
|
|
34
|
-
flexShrink: 0,
|
|
35
|
-
}, children: copied ? (_jsx(CheckCircle, { size: 16, style: { color: "var(--solid-success-color, #22c55e)" } })) : (_jsx(Copy, { size: 16 })) })] }), copied && (_jsx("p", { className: "m-0 mt-2", style: { fontSize: 12, color: "var(--solid-success-color, #22c55e)" }, children: "Copied to clipboard!" }))] }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { children: [_jsx(SolidButton, { variant: "outline", onClick: handleClose, children: "Close" }), _jsx(SolidButton, { onClick: handleClose, children: "I've Saved This Key" })] })] }));
|
|
18
|
+
return (_jsxs(SolidDialog, { open: open, onOpenChange: handleClose, dismissible: false, style: { maxWidth: 520 }, className: "solid-api-key-dialog solid-api-key-dialog--reveal", children: [_jsxs(SolidDialogHeader, { className: "solid-api-key-dialog-header", children: [_jsx(SolidDialogTitle, { children: "API Key Created" }), _jsx(SolidDialogDescription, { children: "Copy this key now and store it in a secure place before closing this dialog." })] }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogBody, { className: "solid-api-key-dialog-body", children: [_jsxs("div", { className: "solid-api-key-reveal-warning", children: [_jsx(AlertTriangle, { size: 16, className: "solid-api-key-reveal-warning-icon" }), _jsxs("p", { className: "m-0", children: [_jsx("strong", { children: "Shown only once." }), " After this dialog closes, the raw key cannot be retrieved again."] })] }), _jsx("p", { className: "form-field-label mb-2", children: keyName }), _jsxs("div", { className: "solid-api-key-reveal-box", children: [_jsx("code", { className: "solid-api-key-reveal-code", children: apiKey }), _jsx("button", { type: "button", title: copied ? "Copied!" : "Copy to clipboard", onClick: handleCopy, className: "solid-api-key-reveal-copy", children: copied ? (_jsx(CheckCircle, { size: 16, className: "solid-api-key-reveal-copy-success" })) : (_jsx(Copy, { size: 16 })) })] }), copied && (_jsx("p", { className: "solid-api-key-reveal-copied m-0 mt-2", children: "Copied to clipboard." }))] }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { className: "solid-api-key-dialog-footer", children: [_jsx(SolidButton, { variant: "outline", onClick: handleClose, children: "Close" }), _jsx(SolidButton, { onClick: handleClose, children: "I've Saved This Key" })] })] }));
|
|
36
19
|
}
|
|
37
20
|
//# sourceMappingURL=RevealApiKeyModal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RevealApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/RevealApiKeyModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAS7B,MAAM,UAAU,iBAAiB,CAAC,EAA0D;QAAxD,IAAI,UAAA,EAAE,MAAM,YAAA,EAAE,OAAO,aAAA,EAAE,OAAO,aAAA;IAC1D,IAAA,KAAsB,QAAQ,CAAC,KAAK,CAAC,EAApC,MAAM,QAAA,EAAE,SAAS,QAAmB,CAAC;IAE5C,IAAM,UAAU,GAAG;QACjB,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YACzC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,UAAU,CAAC,cAAM,OAAA,SAAS,CAAC,KAAK,CAAC,EAAhB,CAAgB,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAM,WAAW,GAAG;QAClB,SAAS,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"RevealApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/RevealApiKeyModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAS7B,MAAM,UAAU,iBAAiB,CAAC,EAA0D;QAAxD,IAAI,UAAA,EAAE,MAAM,YAAA,EAAE,OAAO,aAAA,EAAE,OAAO,aAAA;IAC1D,IAAA,KAAsB,QAAQ,CAAC,KAAK,CAAC,EAApC,MAAM,QAAA,EAAE,SAAS,QAAmB,CAAC;IAE5C,IAAM,UAAU,GAAG;QACjB,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YACzC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,UAAU,CAAC,cAAM,OAAA,SAAS,CAAC,KAAK,CAAC,EAAhB,CAAgB,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAM,WAAW,GAAG;QAClB,SAAS,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,WAAW,IACV,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,WAAW,EACzB,WAAW,EAAE,KAAK,EAClB,KAAK,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,EACxB,SAAS,EAAC,mDAAmD,aAE7D,MAAC,iBAAiB,IAAC,SAAS,EAAC,6BAA6B,aACxD,KAAC,gBAAgB,kCAAmC,EACpD,KAAC,sBAAsB,+FAEE,IACP,EACpB,KAAC,oBAAoB,KAAG,EACxB,MAAC,eAAe,IAAC,SAAS,EAAC,2BAA2B,aACpD,eAAK,SAAS,EAAC,8BAA8B,aAC3C,KAAC,aAAa,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,mCAAmC,GAAG,EACzE,aAAG,SAAS,EAAC,KAAK,aAChB,gDAAiC,yEAC/B,IACA,EAEN,YAAG,SAAS,EAAC,uBAAuB,YAAE,OAAO,GAAK,EAElD,eAAK,SAAS,EAAC,0BAA0B,aACvC,eAAM,SAAS,EAAC,2BAA2B,YAAE,MAAM,GAAQ,EAC3D,iBACE,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,EAC/C,OAAO,EAAE,UAAU,EACnB,SAAS,EAAC,2BAA2B,YAEpC,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,WAAW,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,mCAAmC,GAAG,CACxE,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,IAAI,EAAE,EAAE,GAAI,CACnB,GACM,IACL,EAEL,MAAM,IAAI,CACT,YAAG,SAAS,EAAC,sCAAsC,qCAAyB,CAC7E,IACe,EAClB,KAAC,oBAAoB,KAAG,EACxB,MAAC,iBAAiB,IAAC,SAAS,EAAC,6BAA6B,aACxD,KAAC,WAAW,IAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAE,WAAW,sBAErC,EACd,KAAC,WAAW,IAAC,OAAO,EAAE,WAAW,oCAEnB,IACI,IACR,CACf,CAAC;AACJ,CAAC","sourcesContent":["import { useState } from \"react\";\nimport { AlertTriangle, CheckCircle, Copy } from \"lucide-react\";\nimport {\n SolidButton,\n SolidDialog,\n SolidDialogBody,\n SolidDialogDescription,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogSeparator,\n SolidDialogTitle,\n} from \"../../../shad-cn-ui\";\n\ninterface RevealApiKeyModalProps {\n open: boolean;\n apiKey: string;\n keyName: string;\n onClose: () => void;\n}\n\nexport function RevealApiKeyModal({ open, apiKey, keyName, onClose }: RevealApiKeyModalProps) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(apiKey).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 3000);\n });\n };\n\n const handleClose = () => {\n setCopied(false);\n onClose();\n };\n\n return (\n <SolidDialog\n open={open}\n onOpenChange={handleClose}\n dismissible={false}\n style={{ maxWidth: 520 }}\n className=\"solid-api-key-dialog solid-api-key-dialog--reveal\"\n >\n <SolidDialogHeader className=\"solid-api-key-dialog-header\">\n <SolidDialogTitle>API Key Created</SolidDialogTitle>\n <SolidDialogDescription>\n Copy this key now and store it in a secure place before closing this dialog.\n </SolidDialogDescription>\n </SolidDialogHeader>\n <SolidDialogSeparator />\n <SolidDialogBody className=\"solid-api-key-dialog-body\">\n <div className=\"solid-api-key-reveal-warning\">\n <AlertTriangle size={16} className=\"solid-api-key-reveal-warning-icon\" />\n <p className=\"m-0\">\n <strong>Shown only once.</strong> After this dialog closes, the raw key cannot be retrieved again.\n </p>\n </div>\n\n <p className=\"form-field-label mb-2\">{keyName}</p>\n\n <div className=\"solid-api-key-reveal-box\">\n <code className=\"solid-api-key-reveal-code\">{apiKey}</code>\n <button\n type=\"button\"\n title={copied ? \"Copied!\" : \"Copy to clipboard\"}\n onClick={handleCopy}\n className=\"solid-api-key-reveal-copy\"\n >\n {copied ? (\n <CheckCircle size={16} className=\"solid-api-key-reveal-copy-success\" />\n ) : (\n <Copy size={16} />\n )}\n </button>\n </div>\n\n {copied && (\n <p className=\"solid-api-key-reveal-copied m-0 mt-2\">Copied to clipboard.</p>\n )}\n </SolidDialogBody>\n <SolidDialogSeparator />\n <SolidDialogFooter className=\"solid-api-key-dialog-footer\">\n <SolidButton variant=\"outline\" onClick={handleClose}>\n Close\n </SolidButton>\n <SolidButton onClick={handleClose}>\n I've Saved This Key\n </SolidButton>\n </SolidDialogFooter>\n </SolidDialog>\n );\n}\n"]}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SolidButton,
|
|
5
5
|
SolidDialog,
|
|
6
6
|
SolidDialogBody,
|
|
7
|
+
SolidDialogDescription,
|
|
7
8
|
SolidDialogFooter,
|
|
8
9
|
SolidDialogHeader,
|
|
9
10
|
SolidDialogSeparator,
|
|
@@ -33,58 +34,40 @@ export function RevealApiKeyModal({ open, apiKey, keyName, onClose }: RevealApiK
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
return (
|
|
36
|
-
<SolidDialog
|
|
37
|
-
|
|
37
|
+
<SolidDialog
|
|
38
|
+
open={open}
|
|
39
|
+
onOpenChange={handleClose}
|
|
40
|
+
dismissible={false}
|
|
41
|
+
style={{ maxWidth: 520 }}
|
|
42
|
+
className="solid-api-key-dialog solid-api-key-dialog--reveal"
|
|
43
|
+
>
|
|
44
|
+
<SolidDialogHeader className="solid-api-key-dialog-header">
|
|
38
45
|
<SolidDialogTitle>API Key Created</SolidDialogTitle>
|
|
46
|
+
<SolidDialogDescription>
|
|
47
|
+
Copy this key now and store it in a secure place before closing this dialog.
|
|
48
|
+
</SolidDialogDescription>
|
|
39
49
|
</SolidDialogHeader>
|
|
40
50
|
<SolidDialogSeparator />
|
|
41
|
-
<SolidDialogBody>
|
|
42
|
-
<div className="solid-api-key-reveal-warning
|
|
43
|
-
<AlertTriangle size={16} className="
|
|
44
|
-
<p className="m-0"
|
|
45
|
-
<strong>
|
|
51
|
+
<SolidDialogBody className="solid-api-key-dialog-body">
|
|
52
|
+
<div className="solid-api-key-reveal-warning">
|
|
53
|
+
<AlertTriangle size={16} className="solid-api-key-reveal-warning-icon" />
|
|
54
|
+
<p className="m-0">
|
|
55
|
+
<strong>Shown only once.</strong> After this dialog closes, the raw key cannot be retrieved again.
|
|
46
56
|
</p>
|
|
47
57
|
</div>
|
|
48
58
|
|
|
49
|
-
<p className="form-field-label mb-2">
|
|
50
|
-
{keyName}
|
|
51
|
-
</p>
|
|
59
|
+
<p className="form-field-label mb-2">{keyName}</p>
|
|
52
60
|
|
|
53
|
-
<div
|
|
54
|
-
className="solid-api-key-reveal-
|
|
55
|
-
style={{
|
|
56
|
-
background: "var(--solid-surface-secondary, #f5f5f5)",
|
|
57
|
-
borderRadius: 6,
|
|
58
|
-
border: "1px solid var(--solid-border-color, #e0e0e0)",
|
|
59
|
-
}}
|
|
60
|
-
>
|
|
61
|
-
<code
|
|
62
|
-
className="flex-1"
|
|
63
|
-
style={{
|
|
64
|
-
fontFamily: "monospace",
|
|
65
|
-
fontSize: 13,
|
|
66
|
-
wordBreak: "break-all",
|
|
67
|
-
userSelect: "all",
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{apiKey}
|
|
71
|
-
</code>
|
|
61
|
+
<div className="solid-api-key-reveal-box">
|
|
62
|
+
<code className="solid-api-key-reveal-code">{apiKey}</code>
|
|
72
63
|
<button
|
|
73
64
|
type="button"
|
|
74
65
|
title={copied ? "Copied!" : "Copy to clipboard"}
|
|
75
66
|
onClick={handleCopy}
|
|
76
|
-
|
|
77
|
-
background: "none",
|
|
78
|
-
border: "none",
|
|
79
|
-
cursor: "pointer",
|
|
80
|
-
padding: "4px",
|
|
81
|
-
display: "flex",
|
|
82
|
-
alignItems: "center",
|
|
83
|
-
flexShrink: 0,
|
|
84
|
-
}}
|
|
67
|
+
className="solid-api-key-reveal-copy"
|
|
85
68
|
>
|
|
86
69
|
{copied ? (
|
|
87
|
-
<CheckCircle size={16}
|
|
70
|
+
<CheckCircle size={16} className="solid-api-key-reveal-copy-success" />
|
|
88
71
|
) : (
|
|
89
72
|
<Copy size={16} />
|
|
90
73
|
)}
|
|
@@ -92,13 +75,11 @@ export function RevealApiKeyModal({ open, apiKey, keyName, onClose }: RevealApiK
|
|
|
92
75
|
</div>
|
|
93
76
|
|
|
94
77
|
{copied && (
|
|
95
|
-
<p className="m-0 mt-2"
|
|
96
|
-
Copied to clipboard!
|
|
97
|
-
</p>
|
|
78
|
+
<p className="solid-api-key-reveal-copied m-0 mt-2">Copied to clipboard.</p>
|
|
98
79
|
)}
|
|
99
80
|
</SolidDialogBody>
|
|
100
81
|
<SolidDialogSeparator />
|
|
101
|
-
<SolidDialogFooter>
|
|
82
|
+
<SolidDialogFooter className="solid-api-key-dialog-footer">
|
|
102
83
|
<SolidButton variant="outline" onClick={handleClose}>
|
|
103
84
|
Close
|
|
104
85
|
</SolidButton>
|