@shipfox/client-secrets 0.2.0
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/.storybook/main.ts +1 -0
- package/.storybook/preview.css +4 -0
- package/.storybook/preview.tsx +56 -0
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +102 -0
- package/LICENSE +21 -0
- package/dist/components/copy-key.d.ts +8 -0
- package/dist/components/copy-key.d.ts.map +1 -0
- package/dist/components/copy-key.js +21 -0
- package/dist/components/copy-key.js.map +1 -0
- package/dist/components/delete-entry-dialog.d.ts +9 -0
- package/dist/components/delete-entry-dialog.d.ts.map +1 -0
- package/dist/components/delete-entry-dialog.js +66 -0
- package/dist/components/delete-entry-dialog.js.map +1 -0
- package/dist/components/form-errors.d.ts +17 -0
- package/dist/components/form-errors.d.ts.map +1 -0
- package/dist/components/form-errors.js +81 -0
- package/dist/components/form-errors.js.map +1 -0
- package/dist/components/form-shell.d.ts +14 -0
- package/dist/components/form-shell.d.ts.map +1 -0
- package/dist/components/form-shell.js +28 -0
- package/dist/components/form-shell.js.map +1 -0
- package/dist/components/secret-form.d.ts +10 -0
- package/dist/components/secret-form.d.ts.map +1 -0
- package/dist/components/secret-form.js +176 -0
- package/dist/components/secret-form.js.map +1 -0
- package/dist/components/secret-form.stories.d.ts +9 -0
- package/dist/components/secret-form.stories.d.ts.map +1 -0
- package/dist/components/secret-form.stories.js +60 -0
- package/dist/components/secret-form.stories.js.map +1 -0
- package/dist/components/store-key.d.ts +20 -0
- package/dist/components/store-key.d.ts.map +1 -0
- package/dist/components/store-key.js +25 -0
- package/dist/components/store-key.js.map +1 -0
- package/dist/components/store-section-shell.d.ts +10 -0
- package/dist/components/store-section-shell.d.ts.map +1 -0
- package/dist/components/store-section-shell.js +40 -0
- package/dist/components/store-section-shell.js.map +1 -0
- package/dist/components/variable-form.d.ts +12 -0
- package/dist/components/variable-form.d.ts.map +1 -0
- package/dist/components/variable-form.js +186 -0
- package/dist/components/variable-form.js.map +1 -0
- package/dist/components/variable-form.stories.d.ts +9 -0
- package/dist/components/variable-form.stories.d.ts.map +1 -0
- package/dist/components/variable-form.stories.js +61 -0
- package/dist/components/variable-form.stories.js.map +1 -0
- package/dist/components/workspace-secrets-section.d.ts +4 -0
- package/dist/components/workspace-secrets-section.d.ts.map +1 -0
- package/dist/components/workspace-secrets-section.js +251 -0
- package/dist/components/workspace-secrets-section.js.map +1 -0
- package/dist/components/workspace-secrets-section.stories.d.ts +13 -0
- package/dist/components/workspace-secrets-section.stories.d.ts.map +1 -0
- package/dist/components/workspace-secrets-section.stories.js +140 -0
- package/dist/components/workspace-secrets-section.stories.js.map +1 -0
- package/dist/components/workspace-variables-section.d.ts +4 -0
- package/dist/components/workspace-variables-section.d.ts.map +1 -0
- package/dist/components/workspace-variables-section.js +255 -0
- package/dist/components/workspace-variables-section.js.map +1 -0
- package/dist/components/workspace-variables-section.stories.d.ts +11 -0
- package/dist/components/workspace-variables-section.stories.d.ts.map +1 -0
- package/dist/components/workspace-variables-section.stories.js +127 -0
- package/dist/components/workspace-variables-section.stories.js.map +1 -0
- package/dist/hooks/api/create-store-api.d.ts +56 -0
- package/dist/hooks/api/create-store-api.d.ts.map +1 -0
- package/dist/hooks/api/create-store-api.js +95 -0
- package/dist/hooks/api/create-store-api.js.map +1 -0
- package/dist/hooks/api/secrets.d.ts +61 -0
- package/dist/hooks/api/secrets.d.ts.map +1 -0
- package/dist/hooks/api/secrets.js +15 -0
- package/dist/hooks/api/secrets.js.map +1 -0
- package/dist/hooks/api/variables.d.ts +88 -0
- package/dist/hooks/api/variables.d.ts.map +1 -0
- package/dist/hooks/api/variables.js +52 -0
- package/dist/hooks/api/variables.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +89 -0
- package/src/components/copy-key.test.ts +44 -0
- package/src/components/copy-key.ts +21 -0
- package/src/components/delete-entry-dialog.tsx +57 -0
- package/src/components/form-errors.test.ts +67 -0
- package/src/components/form-errors.ts +59 -0
- package/src/components/form-shell.tsx +31 -0
- package/src/components/secret-form.stories.tsx +58 -0
- package/src/components/secret-form.test.tsx +37 -0
- package/src/components/secret-form.tsx +170 -0
- package/src/components/store-key.test.ts +53 -0
- package/src/components/store-key.ts +36 -0
- package/src/components/store-section-shell.tsx +32 -0
- package/src/components/variable-form.stories.tsx +58 -0
- package/src/components/variable-form.test.tsx +60 -0
- package/src/components/variable-form.tsx +182 -0
- package/src/components/workspace-secrets-section.stories.tsx +128 -0
- package/src/components/workspace-secrets-section.tsx +231 -0
- package/src/components/workspace-variables-section.stories.tsx +121 -0
- package/src/components/workspace-variables-section.tsx +238 -0
- package/src/hooks/api/create-store-api.ts +114 -0
- package/src/hooks/api/secrets.test.ts +157 -0
- package/src/hooks/api/secrets.ts +26 -0
- package/src/hooks/api/variables.ts +61 -0
- package/src/index.ts +5 -0
- package/test/fixtures/secrets.ts +59 -0
- package/test/setup.ts +3 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vercel.json +8 -0
- package/vitest.config.ts +75 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { QueryLoadError } from '@shipfox/client-ui';
|
|
3
|
+
import { Button, IconButton } from '@shipfox/react-ui/button';
|
|
4
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
|
|
5
|
+
import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
6
|
+
import { Modal, ModalContent, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
|
|
7
|
+
import { RelativeTime, RelativeTimeProvider } from '@shipfox/react-ui/relative-time';
|
|
8
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@shipfox/react-ui/table';
|
|
9
|
+
import { toast } from '@shipfox/react-ui/toast';
|
|
10
|
+
import { Code, Header, Text } from '@shipfox/react-ui/typography';
|
|
11
|
+
import { useMemo, useState } from 'react';
|
|
12
|
+
import { useDeleteVariableMutation, useVariablesQuery } from '#hooks/api/variables.js';
|
|
13
|
+
import { copyKeyName } from './copy-key.js';
|
|
14
|
+
import { DeleteEntryDialog } from './delete-entry-dialog.js';
|
|
15
|
+
import { secretsErrorToFormError } from './form-errors.js';
|
|
16
|
+
import { StoreRowsSkeleton, StoreSurface } from './store-section-shell.js';
|
|
17
|
+
import { VariableForm } from './variable-form.js';
|
|
18
|
+
const VARIABLES_DESCRIPTION = 'Plaintext configuration values for non-sensitive data like regions, flags, and log levels.';
|
|
19
|
+
const EMPTY_VARIABLES_DESCRIPTION = 'Create a variable to store non-sensitive configuration like regions, flags, and log levels.';
|
|
20
|
+
export function WorkspaceVariablesSection({ workspaceId }) {
|
|
21
|
+
const variablesQuery = useVariablesQuery(workspaceId);
|
|
22
|
+
const deleteVariable = useDeleteVariableMutation();
|
|
23
|
+
const [formState, setFormState] = useState(null);
|
|
24
|
+
const [deleteKey, setDeleteKey] = useState(null);
|
|
25
|
+
const [deleteError, setDeleteError] = useState();
|
|
26
|
+
const variables = useMemo(()=>[
|
|
27
|
+
...variablesQuery.data ?? []
|
|
28
|
+
].sort((a, b)=>a.key.localeCompare(b.key)), [
|
|
29
|
+
variablesQuery.data
|
|
30
|
+
]);
|
|
31
|
+
function closeDelete() {
|
|
32
|
+
setDeleteKey(null);
|
|
33
|
+
setDeleteError(undefined);
|
|
34
|
+
}
|
|
35
|
+
return /*#__PURE__*/ _jsxs(RelativeTimeProvider, {
|
|
36
|
+
children: [
|
|
37
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
38
|
+
className: "flex flex-col gap-16",
|
|
39
|
+
"aria-label": "Variables",
|
|
40
|
+
children: [
|
|
41
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
42
|
+
className: "flex items-start justify-between gap-16",
|
|
43
|
+
children: [
|
|
44
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
45
|
+
className: "flex flex-col gap-4",
|
|
46
|
+
children: [
|
|
47
|
+
/*#__PURE__*/ _jsx(Header, {
|
|
48
|
+
variant: "h3",
|
|
49
|
+
children: "Variables"
|
|
50
|
+
}),
|
|
51
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
52
|
+
size: "sm",
|
|
53
|
+
className: "text-foreground-neutral-muted",
|
|
54
|
+
children: VARIABLES_DESCRIPTION
|
|
55
|
+
})
|
|
56
|
+
]
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
59
|
+
size: "sm",
|
|
60
|
+
onClick: ()=>setFormState({
|
|
61
|
+
mode: 'create'
|
|
62
|
+
}),
|
|
63
|
+
children: "Create variable"
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
}),
|
|
67
|
+
variablesQuery.isPending ? /*#__PURE__*/ _jsx(StoreRowsSkeleton, {
|
|
68
|
+
label: "Loading variables"
|
|
69
|
+
}) : null,
|
|
70
|
+
variablesQuery.isError && variablesQuery.data === undefined ? /*#__PURE__*/ _jsx(StoreSurface, {
|
|
71
|
+
className: "px-16",
|
|
72
|
+
children: /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
73
|
+
query: variablesQuery,
|
|
74
|
+
subject: "variables"
|
|
75
|
+
})
|
|
76
|
+
}) : null,
|
|
77
|
+
variablesQuery.data !== undefined && variables.length === 0 ? /*#__PURE__*/ _jsx(StoreSurface, {
|
|
78
|
+
className: "px-16",
|
|
79
|
+
children: /*#__PURE__*/ _jsx(EmptyState, {
|
|
80
|
+
icon: "bracesLine",
|
|
81
|
+
title: "No variables yet",
|
|
82
|
+
description: EMPTY_VARIABLES_DESCRIPTION,
|
|
83
|
+
action: /*#__PURE__*/ _jsx(Button, {
|
|
84
|
+
size: "sm",
|
|
85
|
+
onClick: ()=>setFormState({
|
|
86
|
+
mode: 'create'
|
|
87
|
+
}),
|
|
88
|
+
children: "Create variable"
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
}) : null,
|
|
92
|
+
variables.length > 0 ? /*#__PURE__*/ _jsx(StoreSurface, {
|
|
93
|
+
children: /*#__PURE__*/ _jsxs(Table, {
|
|
94
|
+
children: [
|
|
95
|
+
/*#__PURE__*/ _jsx(TableHeader, {
|
|
96
|
+
children: /*#__PURE__*/ _jsxs(TableRow, {
|
|
97
|
+
children: [
|
|
98
|
+
/*#__PURE__*/ _jsx(TableHead, {
|
|
99
|
+
children: "Name"
|
|
100
|
+
}),
|
|
101
|
+
/*#__PURE__*/ _jsx(TableHead, {
|
|
102
|
+
children: "Value"
|
|
103
|
+
}),
|
|
104
|
+
/*#__PURE__*/ _jsx(TableHead, {
|
|
105
|
+
children: "Last edited"
|
|
106
|
+
}),
|
|
107
|
+
/*#__PURE__*/ _jsx(TableHead, {
|
|
108
|
+
className: "w-40 text-right",
|
|
109
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
110
|
+
className: "sr-only",
|
|
111
|
+
children: "Actions"
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
})
|
|
116
|
+
}),
|
|
117
|
+
/*#__PURE__*/ _jsx(TableBody, {
|
|
118
|
+
children: variables.map((variable)=>/*#__PURE__*/ _jsx(VariableRow, {
|
|
119
|
+
variable: variable,
|
|
120
|
+
onEdit: ()=>setFormState({
|
|
121
|
+
mode: 'edit',
|
|
122
|
+
variable
|
|
123
|
+
}),
|
|
124
|
+
onDelete: ()=>setDeleteKey(variable.key)
|
|
125
|
+
}, variable.key))
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
}) : null
|
|
130
|
+
]
|
|
131
|
+
}),
|
|
132
|
+
/*#__PURE__*/ _jsx(Modal, {
|
|
133
|
+
open: formState !== null,
|
|
134
|
+
onOpenChange: (open)=>{
|
|
135
|
+
if (!open) setFormState(null);
|
|
136
|
+
},
|
|
137
|
+
children: /*#__PURE__*/ _jsxs(ModalContent, {
|
|
138
|
+
children: [
|
|
139
|
+
/*#__PURE__*/ _jsx(ModalHeader, {
|
|
140
|
+
children: /*#__PURE__*/ _jsx(ModalTitle, {
|
|
141
|
+
children: formState?.mode === 'edit' ? 'Update variable' : 'Create variable'
|
|
142
|
+
})
|
|
143
|
+
}),
|
|
144
|
+
formState ? /*#__PURE__*/ _jsx(VariableForm, {
|
|
145
|
+
workspaceId: workspaceId,
|
|
146
|
+
mode: formState.mode,
|
|
147
|
+
existingKey: formState.mode === 'edit' ? formState.variable.key : undefined,
|
|
148
|
+
existingValue: formState.mode === 'edit' ? formState.variable.value : undefined,
|
|
149
|
+
existingValueTruncated: formState.mode === 'edit' ? formState.variable.value_truncated : undefined,
|
|
150
|
+
reservedKeys: variables.map((variable)=>variable.key),
|
|
151
|
+
onSaved: ()=>{
|
|
152
|
+
const wasEdit = formState.mode === 'edit';
|
|
153
|
+
setFormState(null);
|
|
154
|
+
toast.success(wasEdit ? 'Variable updated' : 'Variable created');
|
|
155
|
+
},
|
|
156
|
+
onCancel: ()=>setFormState(null)
|
|
157
|
+
}) : null
|
|
158
|
+
]
|
|
159
|
+
})
|
|
160
|
+
}),
|
|
161
|
+
/*#__PURE__*/ _jsx(DeleteEntryDialog, {
|
|
162
|
+
open: deleteKey !== null,
|
|
163
|
+
onOpenChange: (open)=>{
|
|
164
|
+
if (!open) closeDelete();
|
|
165
|
+
},
|
|
166
|
+
entryKey: deleteKey ?? '',
|
|
167
|
+
isLoading: deleteVariable.isPending,
|
|
168
|
+
errorMessage: deleteError,
|
|
169
|
+
onConfirm: async ()=>{
|
|
170
|
+
if (deleteKey === null) return;
|
|
171
|
+
setDeleteError(undefined);
|
|
172
|
+
try {
|
|
173
|
+
await deleteVariable.mutateAsync({
|
|
174
|
+
workspaceId,
|
|
175
|
+
key: deleteKey
|
|
176
|
+
});
|
|
177
|
+
toast.success('Variable deleted');
|
|
178
|
+
closeDelete();
|
|
179
|
+
} catch (error) {
|
|
180
|
+
setDeleteError(secretsErrorToFormError(error).message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function VariableRow({ variable, onEdit, onDelete }) {
|
|
188
|
+
return /*#__PURE__*/ _jsxs(TableRow, {
|
|
189
|
+
children: [
|
|
190
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
191
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
192
|
+
type: "button",
|
|
193
|
+
className: "inline-flex min-w-0 cursor-pointer rounded-4 border-none bg-transparent p-0 text-left text-foreground-neutral-base outline-none transition-colors hover:text-foreground-highlight-interactive focus-visible:shadow-border-interactive-with-active",
|
|
194
|
+
"aria-label": `Copy variable name ${variable.key}`,
|
|
195
|
+
onClick: ()=>void copyKeyName(variable.key),
|
|
196
|
+
children: /*#__PURE__*/ _jsx(Code, {
|
|
197
|
+
as: "span",
|
|
198
|
+
variant: "paragraph",
|
|
199
|
+
className: "truncate",
|
|
200
|
+
children: variable.key
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
}),
|
|
204
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
205
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
206
|
+
title: variable.value,
|
|
207
|
+
className: "block max-w-[280px] truncate font-code text-foreground-neutral-base",
|
|
208
|
+
children: variable.value === '' ? /*#__PURE__*/ _jsx("span", {
|
|
209
|
+
className: "text-foreground-neutral-muted",
|
|
210
|
+
children: "(empty)"
|
|
211
|
+
}) : variable.value
|
|
212
|
+
})
|
|
213
|
+
}),
|
|
214
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
215
|
+
className: "text-foreground-neutral-muted",
|
|
216
|
+
children: /*#__PURE__*/ _jsx(RelativeTime, {
|
|
217
|
+
value: variable.updated_at
|
|
218
|
+
})
|
|
219
|
+
}),
|
|
220
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
221
|
+
className: "text-right",
|
|
222
|
+
children: /*#__PURE__*/ _jsxs(DropdownMenu, {
|
|
223
|
+
children: [
|
|
224
|
+
/*#__PURE__*/ _jsx(DropdownMenuTrigger, {
|
|
225
|
+
asChild: true,
|
|
226
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
227
|
+
size: "sm",
|
|
228
|
+
variant: "transparent",
|
|
229
|
+
icon: "more2Line",
|
|
230
|
+
"aria-label": `Actions for ${variable.key}`
|
|
231
|
+
})
|
|
232
|
+
}),
|
|
233
|
+
/*#__PURE__*/ _jsxs(DropdownMenuContent, {
|
|
234
|
+
align: "end",
|
|
235
|
+
children: [
|
|
236
|
+
/*#__PURE__*/ _jsx(DropdownMenuItem, {
|
|
237
|
+
icon: "editLine",
|
|
238
|
+
onSelect: onEdit,
|
|
239
|
+
children: "Edit value"
|
|
240
|
+
}),
|
|
241
|
+
/*#__PURE__*/ _jsx(DropdownMenuItem, {
|
|
242
|
+
icon: "deleteBinLine",
|
|
243
|
+
onSelect: onDelete,
|
|
244
|
+
children: "Delete"
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
})
|
|
248
|
+
]
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//# sourceMappingURL=workspace-variables-section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-variables-section.tsx"],"sourcesContent":["import type {VariableListItemDto} from '@shipfox/api-secrets-dto';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Modal, ModalContent, ModalHeader, ModalTitle} from '@shipfox/react-ui/modal';\nimport {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {useMemo, useState} from 'react';\nimport {useDeleteVariableMutation, useVariablesQuery} from '#hooks/api/variables.js';\nimport {copyKeyName} from './copy-key.js';\nimport {DeleteEntryDialog} from './delete-entry-dialog.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {StoreRowsSkeleton, StoreSurface} from './store-section-shell.js';\nimport {VariableForm} from './variable-form.js';\n\nconst VARIABLES_DESCRIPTION =\n 'Plaintext configuration values for non-sensitive data like regions, flags, and log levels.';\nconst EMPTY_VARIABLES_DESCRIPTION =\n 'Create a variable to store non-sensitive configuration like regions, flags, and log levels.';\n\ntype FormState = {mode: 'create'} | {mode: 'edit'; variable: VariableListItemDto} | null;\n\nexport function WorkspaceVariablesSection({workspaceId}: {workspaceId: string}) {\n const variablesQuery = useVariablesQuery(workspaceId);\n const deleteVariable = useDeleteVariableMutation();\n const [formState, setFormState] = useState<FormState>(null);\n const [deleteKey, setDeleteKey] = useState<string | null>(null);\n const [deleteError, setDeleteError] = useState<string | undefined>();\n\n const variables = useMemo(\n () => [...(variablesQuery.data ?? [])].sort((a, b) => a.key.localeCompare(b.key)),\n [variablesQuery.data],\n );\n\n function closeDelete() {\n setDeleteKey(null);\n setDeleteError(undefined);\n }\n\n return (\n <RelativeTimeProvider>\n <section className=\"flex flex-col gap-16\" aria-label=\"Variables\">\n <div className=\"flex items-start justify-between gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Variables</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {VARIABLES_DESCRIPTION}\n </Text>\n </div>\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create variable\n </Button>\n </div>\n\n {variablesQuery.isPending ? <StoreRowsSkeleton label=\"Loading variables\" /> : null}\n\n {variablesQuery.isError && variablesQuery.data === undefined ? (\n <StoreSurface className=\"px-16\">\n <QueryLoadError query={variablesQuery} subject=\"variables\" />\n </StoreSurface>\n ) : null}\n\n {variablesQuery.data !== undefined && variables.length === 0 ? (\n <StoreSurface className=\"px-16\">\n <EmptyState\n icon=\"bracesLine\"\n title=\"No variables yet\"\n description={EMPTY_VARIABLES_DESCRIPTION}\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create variable\n </Button>\n }\n />\n </StoreSurface>\n ) : null}\n\n {variables.length > 0 ? (\n <StoreSurface>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Value</TableHead>\n <TableHead>Last edited</TableHead>\n <TableHead className=\"w-40 text-right\">\n <span className=\"sr-only\">Actions</span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {variables.map((variable) => (\n <VariableRow\n key={variable.key}\n variable={variable}\n onEdit={() => setFormState({mode: 'edit', variable})}\n onDelete={() => setDeleteKey(variable.key)}\n />\n ))}\n </TableBody>\n </Table>\n </StoreSurface>\n ) : null}\n </section>\n\n <Modal\n open={formState !== null}\n onOpenChange={(open) => {\n if (!open) setFormState(null);\n }}\n >\n <ModalContent>\n <ModalHeader>\n <ModalTitle>\n {formState?.mode === 'edit' ? 'Update variable' : 'Create variable'}\n </ModalTitle>\n </ModalHeader>\n {formState ? (\n <VariableForm\n workspaceId={workspaceId}\n mode={formState.mode}\n existingKey={formState.mode === 'edit' ? formState.variable.key : undefined}\n existingValue={formState.mode === 'edit' ? formState.variable.value : undefined}\n existingValueTruncated={\n formState.mode === 'edit' ? formState.variable.value_truncated : undefined\n }\n reservedKeys={variables.map((variable) => variable.key)}\n onSaved={() => {\n const wasEdit = formState.mode === 'edit';\n setFormState(null);\n toast.success(wasEdit ? 'Variable updated' : 'Variable created');\n }}\n onCancel={() => setFormState(null)}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <DeleteEntryDialog\n open={deleteKey !== null}\n onOpenChange={(open) => {\n if (!open) closeDelete();\n }}\n entryKey={deleteKey ?? ''}\n isLoading={deleteVariable.isPending}\n errorMessage={deleteError}\n onConfirm={async () => {\n if (deleteKey === null) return;\n setDeleteError(undefined);\n try {\n await deleteVariable.mutateAsync({workspaceId, key: deleteKey});\n toast.success('Variable deleted');\n closeDelete();\n } catch (error) {\n setDeleteError(secretsErrorToFormError(error).message);\n }\n }}\n />\n </RelativeTimeProvider>\n );\n}\n\nfunction VariableRow({\n variable,\n onEdit,\n onDelete,\n}: {\n variable: VariableListItemDto;\n onEdit: () => void;\n onDelete: () => void;\n}) {\n return (\n <TableRow>\n <TableCell>\n <button\n type=\"button\"\n className=\"inline-flex min-w-0 cursor-pointer rounded-4 border-none bg-transparent p-0 text-left text-foreground-neutral-base outline-none transition-colors hover:text-foreground-highlight-interactive focus-visible:shadow-border-interactive-with-active\"\n aria-label={`Copy variable name ${variable.key}`}\n onClick={() => void copyKeyName(variable.key)}\n >\n <Code as=\"span\" variant=\"paragraph\" className=\"truncate\">\n {variable.key}\n </Code>\n </button>\n </TableCell>\n <TableCell>\n <span\n title={variable.value}\n className=\"block max-w-[280px] truncate font-code text-foreground-neutral-base\"\n >\n {variable.value === '' ? (\n <span className=\"text-foreground-neutral-muted\">(empty)</span>\n ) : (\n variable.value\n )}\n </span>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={variable.updated_at} />\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Actions for ${variable.key}`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem icon=\"editLine\" onSelect={onEdit}>\n Edit value\n </DropdownMenuItem>\n <DropdownMenuItem icon=\"deleteBinLine\" onSelect={onDelete}>\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n}\n"],"names":["QueryLoadError","Button","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","EmptyState","Modal","ModalContent","ModalHeader","ModalTitle","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","useDeleteVariableMutation","useVariablesQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","StoreRowsSkeleton","StoreSurface","VariableForm","VARIABLES_DESCRIPTION","EMPTY_VARIABLES_DESCRIPTION","WorkspaceVariablesSection","workspaceId","variablesQuery","deleteVariable","formState","setFormState","deleteKey","setDeleteKey","deleteError","setDeleteError","variables","data","sort","a","b","key","localeCompare","closeDelete","undefined","section","className","aria-label","div","variant","size","onClick","mode","isPending","label","isError","query","subject","length","icon","title","description","action","span","map","variable","VariableRow","onEdit","onDelete","open","onOpenChange","existingKey","existingValue","value","existingValueTruncated","value_truncated","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","error","message","button","type","as","updated_at","asChild","align","onSelect"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
type Scenario = 'loaded' | 'empty';
|
|
3
|
+
declare function SectionStory({ scenario }: {
|
|
4
|
+
scenario: Scenario;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
declare const meta: Meta<typeof SectionStory>;
|
|
7
|
+
export default meta;
|
|
8
|
+
type Story = StoryObj<typeof SectionStory>;
|
|
9
|
+
export declare const Loaded: Story;
|
|
10
|
+
export declare const Empty: Story;
|
|
11
|
+
//# sourceMappingURL=workspace-variables-section.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-variables-section.stories.d.ts","sourceRoot":"","sources":["../../src/components/workspace-variables-section.stories.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,IAAI,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AA+DhE,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAcnC,iBAAS,YAAY,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAC,+BAcrD;AAED,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAInC,CAAC;AACF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C,eAAO,MAAM,MAAM,EAAE,KAQpB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { configureApiClient } from '@shipfox/client-api';
|
|
3
|
+
import { Toaster } from '@shipfox/react-ui/toast';
|
|
4
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
+
import { useEffect, useMemo } from 'react';
|
|
6
|
+
import { within } from 'storybook/test';
|
|
7
|
+
import { WorkspaceVariablesSection } from './workspace-variables-section.js';
|
|
8
|
+
// Freeze the clock only while a story is mounted so RelativeTime renders a stable
|
|
9
|
+
// string in argos snapshots, without leaking the override to other stories.
|
|
10
|
+
const FROZEN_NOW = Date.parse('2026-07-02T00:00:00.000Z');
|
|
11
|
+
const realDateNow = Date.now;
|
|
12
|
+
const withFrozenClock = (Story)=>{
|
|
13
|
+
Date.now = ()=>FROZEN_NOW;
|
|
14
|
+
useEffect(()=>()=>{
|
|
15
|
+
Date.now = realDateNow;
|
|
16
|
+
}, []);
|
|
17
|
+
return /*#__PURE__*/ _jsx(Story, {});
|
|
18
|
+
};
|
|
19
|
+
const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
|
|
20
|
+
const EDITOR_ID = '22222222-2222-4222-8222-222222222222';
|
|
21
|
+
const VARIABLES = [
|
|
22
|
+
{
|
|
23
|
+
key: 'LOG_LEVEL',
|
|
24
|
+
value: 'debug',
|
|
25
|
+
value_truncated: false,
|
|
26
|
+
project_id: null,
|
|
27
|
+
created_at: '2026-05-01T10:00:00.000Z',
|
|
28
|
+
updated_at: '2026-06-30T12:00:00.000Z',
|
|
29
|
+
last_edited_by: EDITOR_ID
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'FEATURE_FLAG',
|
|
33
|
+
value: '',
|
|
34
|
+
value_truncated: false,
|
|
35
|
+
project_id: null,
|
|
36
|
+
created_at: '2026-04-01T10:00:00.000Z',
|
|
37
|
+
updated_at: '2026-06-25T12:00:00.000Z',
|
|
38
|
+
last_edited_by: EDITOR_ID
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: 'TLS_CERT',
|
|
42
|
+
value: '-----BEGIN CERTIFICATE-----',
|
|
43
|
+
value_truncated: true,
|
|
44
|
+
project_id: null,
|
|
45
|
+
created_at: '2026-03-15T10:00:00.000Z',
|
|
46
|
+
updated_at: '2026-06-22T12:00:00.000Z',
|
|
47
|
+
last_edited_by: EDITOR_ID
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'REGION',
|
|
51
|
+
value: 'eu-west-1',
|
|
52
|
+
value_truncated: false,
|
|
53
|
+
project_id: null,
|
|
54
|
+
created_at: '2026-03-01T10:00:00.000Z',
|
|
55
|
+
updated_at: '2026-06-20T12:00:00.000Z',
|
|
56
|
+
last_edited_by: null
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
function fetchForScenario(scenario) {
|
|
60
|
+
return ()=>{
|
|
61
|
+
const variables = scenario === 'empty' ? [] : VARIABLES;
|
|
62
|
+
return Promise.resolve(new Response(JSON.stringify({
|
|
63
|
+
variables,
|
|
64
|
+
next_cursor: null
|
|
65
|
+
}), {
|
|
66
|
+
status: 200,
|
|
67
|
+
headers: {
|
|
68
|
+
'content-type': 'application/json'
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function SectionStory({ scenario }) {
|
|
74
|
+
configureApiClient({
|
|
75
|
+
baseUrl: 'https://api.example.test',
|
|
76
|
+
fetchImpl: fetchForScenario(scenario)
|
|
77
|
+
});
|
|
78
|
+
const queryClient = useMemo(()=>new QueryClient({
|
|
79
|
+
defaultOptions: {
|
|
80
|
+
queries: {
|
|
81
|
+
retry: false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}), []);
|
|
85
|
+
return /*#__PURE__*/ _jsxs(QueryClientProvider, {
|
|
86
|
+
client: queryClient,
|
|
87
|
+
children: [
|
|
88
|
+
/*#__PURE__*/ _jsx("div", {
|
|
89
|
+
className: "w-[720px] p-24",
|
|
90
|
+
children: /*#__PURE__*/ _jsx(WorkspaceVariablesSection, {
|
|
91
|
+
workspaceId: WORKSPACE_ID
|
|
92
|
+
})
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ _jsx(Toaster, {})
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const meta = {
|
|
99
|
+
title: 'Secrets/WorkspaceVariablesSection',
|
|
100
|
+
component: SectionStory,
|
|
101
|
+
decorators: [
|
|
102
|
+
withFrozenClock
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
export default meta;
|
|
106
|
+
export const Loaded = {
|
|
107
|
+
args: {
|
|
108
|
+
scenario: 'loaded'
|
|
109
|
+
},
|
|
110
|
+
play: async ({ canvasElement })=>{
|
|
111
|
+
const canvas = within(canvasElement);
|
|
112
|
+
await canvas.findByText('LOG_LEVEL');
|
|
113
|
+
await canvas.findByRole('button', {
|
|
114
|
+
name: 'Copy variable name LOG_LEVEL'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
export const Empty = {
|
|
119
|
+
args: {
|
|
120
|
+
scenario: 'empty'
|
|
121
|
+
},
|
|
122
|
+
play: async ({ canvasElement })=>{
|
|
123
|
+
await within(canvasElement).findByText('No variables yet');
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=workspace-variables-section.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-variables-section.stories.tsx"],"sourcesContent":["import type {VariableListItemDto} from '@shipfox/api-secrets-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Decorator, Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {useEffect, useMemo} from 'react';\nimport {within} from 'storybook/test';\nimport {WorkspaceVariablesSection} from './workspace-variables-section.js';\n\n// Freeze the clock only while a story is mounted so RelativeTime renders a stable\n// string in argos snapshots, without leaking the override to other stories.\nconst FROZEN_NOW = Date.parse('2026-07-02T00:00:00.000Z');\nconst realDateNow = Date.now;\nconst withFrozenClock: Decorator = (Story) => {\n Date.now = () => FROZEN_NOW;\n useEffect(\n () => () => {\n Date.now = realDateNow;\n },\n [],\n );\n return <Story />;\n};\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst EDITOR_ID = '22222222-2222-4222-8222-222222222222';\n\nconst VARIABLES: VariableListItemDto[] = [\n {\n key: 'LOG_LEVEL',\n value: 'debug',\n value_truncated: false,\n project_id: null,\n created_at: '2026-05-01T10:00:00.000Z',\n updated_at: '2026-06-30T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'FEATURE_FLAG',\n value: '',\n value_truncated: false,\n project_id: null,\n created_at: '2026-04-01T10:00:00.000Z',\n updated_at: '2026-06-25T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'TLS_CERT',\n value: '-----BEGIN CERTIFICATE-----',\n value_truncated: true,\n project_id: null,\n created_at: '2026-03-15T10:00:00.000Z',\n updated_at: '2026-06-22T12:00:00.000Z',\n last_edited_by: EDITOR_ID,\n },\n {\n key: 'REGION',\n value: 'eu-west-1',\n value_truncated: false,\n project_id: null,\n created_at: '2026-03-01T10:00:00.000Z',\n updated_at: '2026-06-20T12:00:00.000Z',\n last_edited_by: null,\n },\n];\n\ntype Scenario = 'loaded' | 'empty';\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (() => {\n const variables = scenario === 'empty' ? [] : VARIABLES;\n return Promise.resolve(\n new Response(JSON.stringify({variables, next_cursor: null}), {\n status: 200,\n headers: {'content-type': 'application/json'},\n }),\n );\n }) as unknown as typeof fetch;\n}\n\nfunction SectionStory({scenario}: {scenario: Scenario}) {\n configureApiClient({baseUrl: 'https://api.example.test', fetchImpl: fetchForScenario(scenario)});\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n return (\n <QueryClientProvider client={queryClient}>\n <div className=\"w-[720px] p-24\">\n <WorkspaceVariablesSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta: Meta<typeof SectionStory> = {\n title: 'Secrets/WorkspaceVariablesSection',\n component: SectionStory,\n decorators: [withFrozenClock],\n};\nexport default meta;\n\ntype Story = StoryObj<typeof SectionStory>;\n\nexport const Loaded: Story = {\n args: {scenario: 'loaded'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n\n await canvas.findByText('LOG_LEVEL');\n await canvas.findByRole('button', {name: 'Copy variable name LOG_LEVEL'});\n },\n};\n\nexport const Empty: Story = {\n args: {scenario: 'empty'},\n play: async ({canvasElement}) => {\n await within(canvasElement).findByText('No variables yet');\n },\n};\n"],"names":["configureApiClient","Toaster","QueryClient","QueryClientProvider","useEffect","useMemo","within","WorkspaceVariablesSection","FROZEN_NOW","Date","parse","realDateNow","now","withFrozenClock","Story","WORKSPACE_ID","EDITOR_ID","VARIABLES","key","value","value_truncated","project_id","created_at","updated_at","last_edited_by","fetchForScenario","scenario","variables","Promise","resolve","Response","JSON","stringify","next_cursor","status","headers","SectionStory","baseUrl","fetchImpl","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","decorators","Loaded","args","play","canvasElement","canvas","findByText","findByRole","name","Empty"],"mappings":";AACA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,OAAO,QAAO,0BAA0B;AAEhD,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SAAQC,SAAS,EAAEC,OAAO,QAAO,QAAQ;AACzC,SAAQC,MAAM,QAAO,iBAAiB;AACtC,SAAQC,yBAAyB,QAAO,mCAAmC;AAE3E,kFAAkF;AAClF,4EAA4E;AAC5E,MAAMC,aAAaC,KAAKC,KAAK,CAAC;AAC9B,MAAMC,cAAcF,KAAKG,GAAG;AAC5B,MAAMC,kBAA6B,CAACC;IAClCL,KAAKG,GAAG,GAAG,IAAMJ;IACjBJ,UACE,IAAM;YACJK,KAAKG,GAAG,GAAGD;QACb,GACA,EAAE;IAEJ,qBAAO,KAACG;AACV;AAEA,MAAMC,eAAe;AACrB,MAAMC,YAAY;AAElB,MAAMC,YAAmC;IACvC;QACEC,KAAK;QACLC,OAAO;QACPC,iBAAiB;QACjBC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgBR;IAClB;IACA;QACEE,KAAK;QACLC,OAAO;QACPC,iBAAiB;QACjBC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgBR;IAClB;IACA;QACEE,KAAK;QACLC,OAAO;QACPC,iBAAiB;QACjBC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgBR;IAClB;IACA;QACEE,KAAK;QACLC,OAAO;QACPC,iBAAiB;QACjBC,YAAY;QACZC,YAAY;QACZC,YAAY;QACZC,gBAAgB;IAClB;CACD;AAID,SAASC,iBAAiBC,QAAkB;IAC1C,OAAQ;QACN,MAAMC,YAAYD,aAAa,UAAU,EAAE,GAAGT;QAC9C,OAAOW,QAAQC,OAAO,CACpB,IAAIC,SAASC,KAAKC,SAAS,CAAC;YAACL;YAAWM,aAAa;QAAI,IAAI;YAC3DC,QAAQ;YACRC,SAAS;gBAAC,gBAAgB;YAAkB;QAC9C;IAEJ;AACF;AAEA,SAASC,aAAa,EAACV,QAAQ,EAAuB;IACpD1B,mBAAmB;QAACqC,SAAS;QAA4BC,WAAWb,iBAAiBC;IAAS;IAC9F,MAAMa,cAAclC,QAClB,IAAM,IAAIH,YAAY;YAACsC,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAEJ,qBACE,MAACvC;QAAoBwC,QAAQJ;;0BAC3B,KAACK;gBAAIC,WAAU;0BACb,cAAA,KAACtC;oBAA0BuC,aAAa/B;;;0BAE1C,KAACd;;;AAGP;AAEA,MAAM8C,OAAkC;IACtCC,OAAO;IACPC,WAAWb;IACXc,YAAY;QAACrC;KAAgB;AAC/B;AACA,eAAekC,KAAK;AAIpB,OAAO,MAAMI,SAAgB;IAC3BC,MAAM;QAAC1B,UAAU;IAAQ;IACzB2B,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMC,SAASjD,OAAOgD;QAEtB,MAAMC,OAAOC,UAAU,CAAC;QACxB,MAAMD,OAAOE,UAAU,CAAC,UAAU;YAACC,MAAM;QAA8B;IACzE;AACF,EAAE;AAEF,OAAO,MAAMC,QAAe;IAC1BP,MAAM;QAAC1B,UAAU;IAAO;IACxB2B,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMhD,OAAOgD,eAAeE,UAAU,CAAC;IACzC;AACF,EAAE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type SecretWriteWarningDto } from '@shipfox/api-secrets-dto';
|
|
2
|
+
/**
|
|
3
|
+
* Secrets and variables share an identical management surface (list / put /
|
|
4
|
+
* delete under `/workspaces/:id/{resource}`); only the response envelope keys
|
|
5
|
+
* and DTO types differ. This factory builds the typed transport functions,
|
|
6
|
+
* query keys, and React Query hooks for one resource so `secrets.ts` and
|
|
7
|
+
* `variables.ts` stay thin instantiations. The forms and sections diverge
|
|
8
|
+
* (write-only vs readable) and are kept separate on purpose.
|
|
9
|
+
*/
|
|
10
|
+
export interface CreateStoreApiConfig<TItem, TListResponse, TPutResponse> {
|
|
11
|
+
/** URL segment and query-key namespace, e.g. `'secrets'` or `'variables'`. */
|
|
12
|
+
resource: string;
|
|
13
|
+
/** Pulls the item array out of a list response. */
|
|
14
|
+
listItems: (response: TListResponse) => TItem[];
|
|
15
|
+
/** Pulls the single item out of a put response. */
|
|
16
|
+
putItem: (response: TPutResponse) => TItem;
|
|
17
|
+
}
|
|
18
|
+
export interface PutResult<TItem> {
|
|
19
|
+
item: TItem;
|
|
20
|
+
warnings: SecretWriteWarningDto[];
|
|
21
|
+
}
|
|
22
|
+
export declare function createStoreApi<TItem, TListResponse, TPutBody, TPutResponse extends {
|
|
23
|
+
warnings: SecretWriteWarningDto[];
|
|
24
|
+
}>(config: CreateStoreApiConfig<TItem, TListResponse, TPutResponse>): {
|
|
25
|
+
queryKeys: {
|
|
26
|
+
all: readonly [string];
|
|
27
|
+
list: (workspaceId: string) => readonly [string, "list", string];
|
|
28
|
+
};
|
|
29
|
+
listAll: (params: {
|
|
30
|
+
workspaceId: string;
|
|
31
|
+
projectId?: string | undefined;
|
|
32
|
+
signal?: AbortSignal | undefined;
|
|
33
|
+
}) => Promise<TItem[]>;
|
|
34
|
+
put: (params: {
|
|
35
|
+
workspaceId: string;
|
|
36
|
+
key: string;
|
|
37
|
+
body: TPutBody;
|
|
38
|
+
}) => Promise<PutResult<TItem>>;
|
|
39
|
+
remove: (params: {
|
|
40
|
+
workspaceId: string;
|
|
41
|
+
key: string;
|
|
42
|
+
projectId?: string | undefined;
|
|
43
|
+
}) => Promise<void>;
|
|
44
|
+
useListQuery: (workspaceId: string | undefined) => import("@tanstack/react-query").UseQueryResult<NoInfer<TItem[]>, Error>;
|
|
45
|
+
usePutMutation: () => import("@tanstack/react-query").UseMutationResult<PutResult<TItem>, Error, {
|
|
46
|
+
workspaceId: string;
|
|
47
|
+
key: string;
|
|
48
|
+
body: TPutBody;
|
|
49
|
+
}, unknown>;
|
|
50
|
+
useDeleteMutation: () => import("@tanstack/react-query").UseMutationResult<void, Error, {
|
|
51
|
+
workspaceId: string;
|
|
52
|
+
key: string;
|
|
53
|
+
projectId?: string | undefined;
|
|
54
|
+
}, unknown>;
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=create-store-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-store-api.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/create-store-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,qBAAqB,EAAC,MAAM,0BAA0B,CAAC;AAI5F;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY;IACtE,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,SAAS,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,KAAK,EAAE,CAAC;IAChD,mDAAmD;IACnD,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,KAAK,CAAC;CAC5C;AAED,MAAM,WAAW,SAAS,CAAC,KAAK;IAC9B,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,wBAAgB,cAAc,CAC5B,KAAK,EACL,aAAa,EACb,QAAQ,EACR,YAAY,SAAS;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAC,EACxD,MAAM,EAAE,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,CAAC;;;4BAK1C,MAAM;;sBAMG;QAC7B,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;KAClC,KAAG,OAAO,CAAC,KAAK,EAAE,CAAC;kBAUO;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,QAAQ,CAAC;KAChB,KAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;qBAQC;QAC5B,WAAW,EAAE,MAAM,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,KAAG,OAAO,CAAC,IAAI,CAAC;gCAUkB,MAAM,GAAG,SAAS;;qBAzBtC,MAAM;aACd,MAAM;cACL,QAAQ;;;qBAUD,MAAM;aACd,MAAM;oBACC,MAAM,GAAG,SAAS;;EA0CjC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { SECRETS_MAX_LIST_LIMIT } from '@shipfox/api-secrets-dto';
|
|
2
|
+
import { apiRequest } from '@shipfox/client-api';
|
|
3
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
4
|
+
export function createStoreApi(config) {
|
|
5
|
+
const { resource, listItems, putItem } = config;
|
|
6
|
+
const queryKeys = {
|
|
7
|
+
all: [
|
|
8
|
+
resource
|
|
9
|
+
],
|
|
10
|
+
list: (workspaceId)=>[
|
|
11
|
+
resource,
|
|
12
|
+
'list',
|
|
13
|
+
workspaceId
|
|
14
|
+
]
|
|
15
|
+
};
|
|
16
|
+
// The store is a bounded set (per-workspace cap), so the UI fetches every key
|
|
17
|
+
// in one call rather than paginating: request the max limit and ignore the
|
|
18
|
+
// cursor. See SECRETS_MAX_LIST_LIMIT / the S1a list route.
|
|
19
|
+
async function listAll(params) {
|
|
20
|
+
const search = new URLSearchParams({
|
|
21
|
+
limit: String(SECRETS_MAX_LIST_LIMIT)
|
|
22
|
+
});
|
|
23
|
+
if (params.projectId) search.set('project_id', params.projectId);
|
|
24
|
+
const response = await apiRequest(`/workspaces/${params.workspaceId}/${resource}?${search.toString()}`, {
|
|
25
|
+
signal: params.signal
|
|
26
|
+
});
|
|
27
|
+
return listItems(response);
|
|
28
|
+
}
|
|
29
|
+
async function put(params) {
|
|
30
|
+
const response = await apiRequest(`/workspaces/${params.workspaceId}/${resource}/${encodeURIComponent(params.key)}`, {
|
|
31
|
+
method: 'PUT',
|
|
32
|
+
body: params.body
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
item: putItem(response),
|
|
36
|
+
warnings: response.warnings
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function remove(params) {
|
|
40
|
+
const search = params.projectId ? `?${new URLSearchParams({
|
|
41
|
+
project_id: params.projectId
|
|
42
|
+
}).toString()}` : '';
|
|
43
|
+
await apiRequest(`/workspaces/${params.workspaceId}/${resource}/${encodeURIComponent(params.key)}${search}`, {
|
|
44
|
+
method: 'DELETE'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function useListQuery(workspaceId) {
|
|
48
|
+
return useQuery({
|
|
49
|
+
queryKey: workspaceId ? queryKeys.list(workspaceId) : [
|
|
50
|
+
resource,
|
|
51
|
+
'list'
|
|
52
|
+
],
|
|
53
|
+
enabled: Boolean(workspaceId),
|
|
54
|
+
queryFn: ({ signal })=>listAll({
|
|
55
|
+
workspaceId: workspaceId ?? '',
|
|
56
|
+
signal
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function usePutMutation() {
|
|
61
|
+
const queryClient = useQueryClient();
|
|
62
|
+
return useMutation({
|
|
63
|
+
mutationFn: put,
|
|
64
|
+
// Invalidate the whole resource namespace so the per-item detail cache
|
|
65
|
+
// (full variable values) is refreshed too, not just the list.
|
|
66
|
+
onSuccess: async ()=>{
|
|
67
|
+
await queryClient.invalidateQueries({
|
|
68
|
+
queryKey: queryKeys.all
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function useDeleteMutation() {
|
|
74
|
+
const queryClient = useQueryClient();
|
|
75
|
+
return useMutation({
|
|
76
|
+
mutationFn: remove,
|
|
77
|
+
onSuccess: async ()=>{
|
|
78
|
+
await queryClient.invalidateQueries({
|
|
79
|
+
queryKey: queryKeys.all
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
queryKeys,
|
|
86
|
+
listAll,
|
|
87
|
+
put,
|
|
88
|
+
remove,
|
|
89
|
+
useListQuery,
|
|
90
|
+
usePutMutation,
|
|
91
|
+
useDeleteMutation
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=create-store-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/create-store-api.ts"],"sourcesContent":["import {SECRETS_MAX_LIST_LIMIT, type SecretWriteWarningDto} from '@shipfox/api-secrets-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';\n\n/**\n * Secrets and variables share an identical management surface (list / put /\n * delete under `/workspaces/:id/{resource}`); only the response envelope keys\n * and DTO types differ. This factory builds the typed transport functions,\n * query keys, and React Query hooks for one resource so `secrets.ts` and\n * `variables.ts` stay thin instantiations. The forms and sections diverge\n * (write-only vs readable) and are kept separate on purpose.\n */\nexport interface CreateStoreApiConfig<TItem, TListResponse, TPutResponse> {\n /** URL segment and query-key namespace, e.g. `'secrets'` or `'variables'`. */\n resource: string;\n /** Pulls the item array out of a list response. */\n listItems: (response: TListResponse) => TItem[];\n /** Pulls the single item out of a put response. */\n putItem: (response: TPutResponse) => TItem;\n}\n\nexport interface PutResult<TItem> {\n item: TItem;\n warnings: SecretWriteWarningDto[];\n}\n\nexport function createStoreApi<\n TItem,\n TListResponse,\n TPutBody,\n TPutResponse extends {warnings: SecretWriteWarningDto[]},\n>(config: CreateStoreApiConfig<TItem, TListResponse, TPutResponse>) {\n const {resource, listItems, putItem} = config;\n\n const queryKeys = {\n all: [resource] as const,\n list: (workspaceId: string) => [resource, 'list', workspaceId] as const,\n };\n\n // The store is a bounded set (per-workspace cap), so the UI fetches every key\n // in one call rather than paginating: request the max limit and ignore the\n // cursor. See SECRETS_MAX_LIST_LIMIT / the S1a list route.\n async function listAll(params: {\n workspaceId: string;\n projectId?: string | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<TItem[]> {\n const search = new URLSearchParams({limit: String(SECRETS_MAX_LIST_LIMIT)});\n if (params.projectId) search.set('project_id', params.projectId);\n const response = await apiRequest<TListResponse>(\n `/workspaces/${params.workspaceId}/${resource}?${search.toString()}`,\n {signal: params.signal},\n );\n return listItems(response);\n }\n\n async function put(params: {\n workspaceId: string;\n key: string;\n body: TPutBody;\n }): Promise<PutResult<TItem>> {\n const response = await apiRequest<TPutResponse>(\n `/workspaces/${params.workspaceId}/${resource}/${encodeURIComponent(params.key)}`,\n {method: 'PUT', body: params.body},\n );\n return {item: putItem(response), warnings: response.warnings};\n }\n\n async function remove(params: {\n workspaceId: string;\n key: string;\n projectId?: string | undefined;\n }): Promise<void> {\n const search = params.projectId\n ? `?${new URLSearchParams({project_id: params.projectId}).toString()}`\n : '';\n await apiRequest<void>(\n `/workspaces/${params.workspaceId}/${resource}/${encodeURIComponent(params.key)}${search}`,\n {method: 'DELETE'},\n );\n }\n\n function useListQuery(workspaceId: string | undefined) {\n return useQuery({\n queryKey: workspaceId ? queryKeys.list(workspaceId) : [resource, 'list'],\n enabled: Boolean(workspaceId),\n queryFn: ({signal}) => listAll({workspaceId: workspaceId ?? '', signal}),\n });\n }\n\n function usePutMutation() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: put,\n // Invalidate the whole resource namespace so the per-item detail cache\n // (full variable values) is refreshed too, not just the list.\n onSuccess: async () => {\n await queryClient.invalidateQueries({queryKey: queryKeys.all});\n },\n });\n }\n\n function useDeleteMutation() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: remove,\n onSuccess: async () => {\n await queryClient.invalidateQueries({queryKey: queryKeys.all});\n },\n });\n }\n\n return {queryKeys, listAll, put, remove, useListQuery, usePutMutation, useDeleteMutation};\n}\n"],"names":["SECRETS_MAX_LIST_LIMIT","apiRequest","useMutation","useQuery","useQueryClient","createStoreApi","config","resource","listItems","putItem","queryKeys","all","list","workspaceId","listAll","params","search","URLSearchParams","limit","String","projectId","set","response","toString","signal","put","encodeURIComponent","key","method","body","item","warnings","remove","project_id","useListQuery","queryKey","enabled","Boolean","queryFn","usePutMutation","queryClient","mutationFn","onSuccess","invalidateQueries","useDeleteMutation"],"mappings":"AAAA,SAAQA,sBAAsB,QAAmC,2BAA2B;AAC5F,SAAQC,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,WAAW,EAAEC,QAAQ,EAAEC,cAAc,QAAO,wBAAwB;AAwB5E,OAAO,SAASC,eAKdC,MAAgE;IAChE,MAAM,EAACC,QAAQ,EAAEC,SAAS,EAAEC,OAAO,EAAC,GAAGH;IAEvC,MAAMI,YAAY;QAChBC,KAAK;YAACJ;SAAS;QACfK,MAAM,CAACC,cAAwB;gBAACN;gBAAU;gBAAQM;aAAY;IAChE;IAEA,8EAA8E;IAC9E,2EAA2E;IAC3E,2DAA2D;IAC3D,eAAeC,QAAQC,MAItB;QACC,MAAMC,SAAS,IAAIC,gBAAgB;YAACC,OAAOC,OAAOnB;QAAuB;QACzE,IAAIe,OAAOK,SAAS,EAAEJ,OAAOK,GAAG,CAAC,cAAcN,OAAOK,SAAS;QAC/D,MAAME,WAAW,MAAMrB,WACrB,CAAC,YAAY,EAAEc,OAAOF,WAAW,CAAC,CAAC,EAAEN,SAAS,CAAC,EAAES,OAAOO,QAAQ,IAAI,EACpE;YAACC,QAAQT,OAAOS,MAAM;QAAA;QAExB,OAAOhB,UAAUc;IACnB;IAEA,eAAeG,IAAIV,MAIlB;QACC,MAAMO,WAAW,MAAMrB,WACrB,CAAC,YAAY,EAAEc,OAAOF,WAAW,CAAC,CAAC,EAAEN,SAAS,CAAC,EAAEmB,mBAAmBX,OAAOY,GAAG,GAAG,EACjF;YAACC,QAAQ;YAAOC,MAAMd,OAAOc,IAAI;QAAA;QAEnC,OAAO;YAACC,MAAMrB,QAAQa;YAAWS,UAAUT,SAASS,QAAQ;QAAA;IAC9D;IAEA,eAAeC,OAAOjB,MAIrB;QACC,MAAMC,SAASD,OAAOK,SAAS,GAC3B,CAAC,CAAC,EAAE,IAAIH,gBAAgB;YAACgB,YAAYlB,OAAOK,SAAS;QAAA,GAAGG,QAAQ,IAAI,GACpE;QACJ,MAAMtB,WACJ,CAAC,YAAY,EAAEc,OAAOF,WAAW,CAAC,CAAC,EAAEN,SAAS,CAAC,EAAEmB,mBAAmBX,OAAOY,GAAG,IAAIX,QAAQ,EAC1F;YAACY,QAAQ;QAAQ;IAErB;IAEA,SAASM,aAAarB,WAA+B;QACnD,OAAOV,SAAS;YACdgC,UAAUtB,cAAcH,UAAUE,IAAI,CAACC,eAAe;gBAACN;gBAAU;aAAO;YACxE6B,SAASC,QAAQxB;YACjByB,SAAS,CAAC,EAACd,MAAM,EAAC,GAAKV,QAAQ;oBAACD,aAAaA,eAAe;oBAAIW;gBAAM;QACxE;IACF;IAEA,SAASe;QACP,MAAMC,cAAcpC;QACpB,OAAOF,YAAY;YACjBuC,YAAYhB;YACZ,uEAAuE;YACvE,8DAA8D;YAC9DiB,WAAW;gBACT,MAAMF,YAAYG,iBAAiB,CAAC;oBAACR,UAAUzB,UAAUC,GAAG;gBAAA;YAC9D;QACF;IACF;IAEA,SAASiC;QACP,MAAMJ,cAAcpC;QACpB,OAAOF,YAAY;YACjBuC,YAAYT;YACZU,WAAW;gBACT,MAAMF,YAAYG,iBAAiB,CAAC;oBAACR,UAAUzB,UAAUC,GAAG;gBAAA;YAC9D;QACF;IACF;IAEA,OAAO;QAACD;QAAWI;QAASW;QAAKO;QAAQE;QAAcK;QAAgBK;IAAiB;AAC1F"}
|