@shipfox/client-secrets 21.0.0 → 22.0.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/components/store-section-shell.d.ts +0 -5
- package/dist/components/store-section-shell.d.ts.map +1 -1
- package/dist/components/store-section-shell.js +28 -31
- package/dist/components/store-section-shell.js.map +1 -1
- package/dist/components/workspace-secrets-section.d.ts.map +1 -1
- package/dist/components/workspace-secrets-section.js +6 -6
- package/dist/components/workspace-secrets-section.js.map +1 -1
- package/dist/components/workspace-variables-section.d.ts.map +1 -1
- package/dist/components/workspace-variables-section.js +6 -6
- package/dist/components/workspace-variables-section.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/components/store-section-shell.tsx +20 -25
- package/src/components/workspace-secrets-section.tsx +7 -6
- package/src/components/workspace-variables-section.tsx +7 -6
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 22 files with swc (
|
|
2
|
+
Successfully compiled: 22 files with swc (418.86ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @shipfox/client-secrets
|
|
2
2
|
|
|
3
|
+
## 22.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7693eb3: Render empty and load-error states inside bordered panel bodies and keep loading placeholders aligned with their data regions.
|
|
8
|
+
- 00c1cb8: Panels across these surfaces share one hover, focus, and elevation treatment. Grids of openable things, including the integration gallery, the available providers grid, and the harness picker, render as cells inside a single panel divided by hairlines instead of separate bordered tiles.
|
|
9
|
+
- Updated dependencies [50b3867]
|
|
10
|
+
- Updated dependencies [00c1cb8]
|
|
11
|
+
- Updated dependencies [7693eb3]
|
|
12
|
+
- Updated dependencies [00c1cb8]
|
|
13
|
+
- Updated dependencies [56f4526]
|
|
14
|
+
- @shipfox/client-shell@22.0.0
|
|
15
|
+
- @shipfox/react-ui@2.1.0
|
|
16
|
+
- @shipfox/client-ui@22.0.0
|
|
17
|
+
|
|
3
18
|
## 21.0.0
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
export declare const STORE_SURFACE_CLASS = "overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base";
|
|
2
1
|
/** Placeholder rows shown while the store list loads. */
|
|
3
2
|
export declare function StoreRowsSkeleton({ label }: {
|
|
4
3
|
label: string;
|
|
5
4
|
}): import("react").JSX.Element;
|
|
6
|
-
export declare function StoreSurface({ className, children, }: {
|
|
7
|
-
className?: string;
|
|
8
|
-
children: React.ReactNode;
|
|
9
|
-
}): import("react").JSX.Element;
|
|
10
5
|
//# sourceMappingURL=store-section-shell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-section-shell.d.ts","sourceRoot":"","sources":["../../src/components/store-section-shell.tsx"],"names":[],"mappings":"AAGA,
|
|
1
|
+
{"version":3,"file":"store-section-shell.d.ts","sourceRoot":"","sources":["../../src/components/store-section-shell.tsx"],"names":[],"mappings":"AAGA,yDAAyD;AACzD,wBAAgB,iBAAiB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,+BAsBzD"}
|
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Panel, PanelBody, PanelRow } from '@shipfox/react-ui/panel';
|
|
2
3
|
import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
3
|
-
import { cn } from '@shipfox/react-ui/utils';
|
|
4
|
-
export const STORE_SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
5
4
|
/** Placeholder rows shown while the store list loads. */ export function StoreRowsSkeleton({ label }) {
|
|
6
|
-
return /*#__PURE__*/ _jsx(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/*#__PURE__*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
5
|
+
return /*#__PURE__*/ _jsx(Panel, {
|
|
6
|
+
children: /*#__PURE__*/ _jsx(PanelBody, {
|
|
7
|
+
asChild: true,
|
|
8
|
+
children: /*#__PURE__*/ _jsx("ul", {
|
|
9
|
+
role: "status",
|
|
10
|
+
"aria-label": label,
|
|
11
|
+
children: [
|
|
12
|
+
0,
|
|
13
|
+
1,
|
|
14
|
+
2
|
|
15
|
+
].map((row)=>/*#__PURE__*/ _jsx(PanelRow, {
|
|
16
|
+
asChild: true,
|
|
17
|
+
className: "justify-start gap-cluster hover:bg-background-neutral-base",
|
|
18
|
+
children: /*#__PURE__*/ _jsxs("li", {
|
|
19
|
+
children: [
|
|
20
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
21
|
+
className: "h-16 w-140"
|
|
22
|
+
}),
|
|
23
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
24
|
+
className: "h-16 w-96"
|
|
25
|
+
}),
|
|
26
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
27
|
+
className: "ml-auto h-14 w-80 shrink-0"
|
|
28
|
+
})
|
|
29
|
+
]
|
|
27
30
|
})
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
}, row))
|
|
32
|
+
})
|
|
30
33
|
})
|
|
31
34
|
});
|
|
32
35
|
}
|
|
33
|
-
export function StoreSurface({ className, children }) {
|
|
34
|
-
return /*#__PURE__*/ _jsx("div", {
|
|
35
|
-
className: cn(STORE_SURFACE_CLASS, className),
|
|
36
|
-
children: children
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
36
|
|
|
40
37
|
//# sourceMappingURL=store-section-shell.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/store-section-shell.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../src/components/store-section-shell.tsx"],"sourcesContent":["import {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\n\n/** Placeholder rows shown while the store list loads. */\nexport function StoreRowsSkeleton({label}: {label: string}) {\n return (\n <Panel>\n <PanelBody asChild>\n <ul role=\"status\" aria-label={label}>\n {[0, 1, 2].map((row) => (\n <PanelRow\n asChild\n className=\"justify-start gap-cluster hover:bg-background-neutral-base\"\n key={row}\n >\n <li>\n <Skeleton className=\"h-16 w-140\" />\n <Skeleton className=\"h-16 w-96\" />\n <Skeleton className=\"ml-auto h-14 w-80 shrink-0\" />\n </li>\n </PanelRow>\n ))}\n </ul>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["Panel","PanelBody","PanelRow","Skeleton","StoreRowsSkeleton","label","asChild","ul","role","aria-label","map","row","className","li"],"mappings":";AAAA,SAAQA,KAAK,EAAEC,SAAS,EAAEC,QAAQ,QAAO,0BAA0B;AACnE,SAAQC,QAAQ,QAAO,6BAA6B;AAEpD,uDAAuD,GACvD,OAAO,SAASC,kBAAkB,EAACC,KAAK,EAAkB;IACxD,qBACE,KAACL;kBACC,cAAA,KAACC;YAAUK,OAAO;sBAChB,cAAA,KAACC;gBAAGC,MAAK;gBAASC,cAAYJ;0BAC3B;oBAAC;oBAAG;oBAAG;iBAAE,CAACK,GAAG,CAAC,CAACC,oBACd,KAACT;wBACCI,OAAO;wBACPM,WAAU;kCAGV,cAAA,MAACC;;8CACC,KAACV;oCAASS,WAAU;;8CACpB,KAACT;oCAASS,WAAU;;8CACpB,KAACT;oCAASS,WAAU;;;;uBALjBD;;;;AAanB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-secrets-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,uBAAuB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+
|
|
1
|
+
{"version":3,"file":"workspace-secrets-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-secrets-section.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,uBAAuB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA2I3E"}
|
|
@@ -16,7 +16,7 @@ import { copyKeyName } from './copy-key.js';
|
|
|
16
16
|
import { DeleteEntryDialog } from './delete-entry-dialog.js';
|
|
17
17
|
import { secretsErrorToFormError } from './form-errors.js';
|
|
18
18
|
import { SecretForm } from './secret-form.js';
|
|
19
|
-
import { StoreRowsSkeleton
|
|
19
|
+
import { StoreRowsSkeleton } from './store-section-shell.js';
|
|
20
20
|
const SECRETS_DESCRIPTION = 'Encrypted, write-only values for sensitive data like API keys, tokens, and passwords.';
|
|
21
21
|
const EMPTY_SECRETS_DESCRIPTION = 'Create a secret to store sensitive values like API keys, tokens, and passwords.';
|
|
22
22
|
export function WorkspaceSecretsSection({ workspaceId }) {
|
|
@@ -69,19 +69,19 @@ export function WorkspaceSecretsSection({ workspaceId }) {
|
|
|
69
69
|
secretsQuery.isPending ? /*#__PURE__*/ _jsx(StoreRowsSkeleton, {
|
|
70
70
|
label: "Loading secrets"
|
|
71
71
|
}) : null,
|
|
72
|
-
secretsQuery.isError && secretsQuery.data === undefined ? /*#__PURE__*/ _jsx(
|
|
73
|
-
className: "px-row",
|
|
72
|
+
secretsQuery.isError && secretsQuery.data === undefined ? /*#__PURE__*/ _jsx(Panel, {
|
|
74
73
|
children: /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
75
74
|
query: secretsQuery,
|
|
76
|
-
subject: "secrets"
|
|
75
|
+
subject: "secrets",
|
|
76
|
+
variant: "panel"
|
|
77
77
|
})
|
|
78
78
|
}) : null,
|
|
79
|
-
secretsQuery.data !== undefined && secrets.length === 0 ? /*#__PURE__*/ _jsx(
|
|
80
|
-
className: "px-row",
|
|
79
|
+
secretsQuery.data !== undefined && secrets.length === 0 ? /*#__PURE__*/ _jsx(Panel, {
|
|
81
80
|
children: /*#__PURE__*/ _jsx(EmptyState, {
|
|
82
81
|
icon: "keyLine",
|
|
83
82
|
title: "No secrets yet",
|
|
84
83
|
description: EMPTY_SECRETS_DESCRIPTION,
|
|
84
|
+
variant: "panel",
|
|
85
85
|
action: /*#__PURE__*/ _jsx(Button, {
|
|
86
86
|
size: "sm",
|
|
87
87
|
onClick: ()=>setFormState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workspace-secrets-section.tsx"],"sourcesContent":["import {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 {Panel} from '@shipfox/react-ui/panel';\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 {type SecretMetadata, workspaceStoreScope} from '#core/store.js';\nimport {useDeleteSecretMutation, useSecretsQuery} from '#hooks/api/secrets.js';\nimport {copyKeyName} from './copy-key.js';\nimport {DeleteEntryDialog} from './delete-entry-dialog.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {SecretForm} from './secret-form.js';\nimport {StoreRowsSkeleton, StoreSurface} from './store-section-shell.js';\n\nconst SECRETS_DESCRIPTION =\n 'Encrypted, write-only values for sensitive data like API keys, tokens, and passwords.';\nconst EMPTY_SECRETS_DESCRIPTION =\n 'Create a secret to store sensitive values like API keys, tokens, and passwords.';\n\ntype FormState = {mode: 'create'} | {mode: 'edit'; key: string} | null;\n\nexport function WorkspaceSecretsSection({workspaceId}: {workspaceId: string}) {\n const secretsQuery = useSecretsQuery(workspaceId);\n const deleteSecret = useDeleteSecretMutation();\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 secrets = useMemo(\n () => [...(secretsQuery.data ?? [])].sort((a, b) => a.key.localeCompare(b.key)),\n [secretsQuery.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-group\" aria-label=\"Secrets\">\n <div className=\"flex items-start justify-between gap-group\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Secrets</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {SECRETS_DESCRIPTION}\n </Text>\n </div>\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n </div>\n\n {secretsQuery.isPending ? <StoreRowsSkeleton label=\"Loading secrets\" /> : null}\n\n {secretsQuery.isError && secretsQuery.data === undefined ? (\n <StoreSurface className=\"px-row\">\n <QueryLoadError query={secretsQuery} subject=\"secrets\" />\n </StoreSurface>\n ) : null}\n\n {secretsQuery.data !== undefined && secrets.length === 0 ? (\n <StoreSurface className=\"px-row\">\n <EmptyState\n icon=\"keyLine\"\n title=\"No secrets yet\"\n description={EMPTY_SECRETS_DESCRIPTION}\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n }\n />\n </StoreSurface>\n ) : null}\n\n {secrets.length > 0 ? (\n <Panel>\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 {secrets.map((secret) => (\n <SecretRow\n key={secret.key}\n secret={secret}\n onEdit={() => setFormState({mode: 'edit', key: secret.key})}\n onDelete={() => setDeleteKey(secret.key)}\n />\n ))}\n </TableBody>\n </Table>\n </Panel>\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 secret' : 'Create secret'}\n </ModalTitle>\n </ModalHeader>\n {formState ? (\n <SecretForm\n workspaceId={workspaceId}\n mode={formState.mode}\n existingKey={formState.mode === 'edit' ? formState.key : undefined}\n reservedKeys={secrets.map((secret) => secret.key)}\n onSaved={() => {\n const wasEdit = formState.mode === 'edit';\n setFormState(null);\n toast.success(wasEdit ? 'Secret updated' : 'Secret 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={deleteSecret.isPending}\n errorMessage={deleteError}\n onConfirm={async () => {\n if (deleteKey === null) return;\n setDeleteError(undefined);\n try {\n await deleteSecret.mutateAsync({\n workspaceId,\n key: deleteKey,\n scope: workspaceStoreScope,\n });\n toast.success('Secret deleted');\n closeDelete();\n } catch (error) {\n setDeleteError(secretsErrorToFormError(error).message);\n }\n }}\n />\n </RelativeTimeProvider>\n );\n}\n\nfunction SecretRow({\n secret,\n onEdit,\n onDelete,\n}: {\n secret: SecretMetadata;\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 secret name ${secret.key}`}\n onClick={() => void copyKeyName(secret.key)}\n >\n <Code as=\"span\" variant=\"paragraph\" className=\"truncate\">\n {secret.key}\n </Code>\n </button>\n </TableCell>\n <TableCell>\n <span\n role=\"img\"\n aria-label=\"Value hidden\"\n className=\"font-code text-foreground-neutral-muted\"\n >\n ••••••••\n </span>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={secret.updatedAt} />\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 ${secret.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","Panel","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","workspaceStoreScope","useDeleteSecretMutation","useSecretsQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","SecretForm","StoreRowsSkeleton","StoreSurface","SECRETS_DESCRIPTION","EMPTY_SECRETS_DESCRIPTION","WorkspaceSecretsSection","workspaceId","secretsQuery","deleteSecret","formState","setFormState","deleteKey","setDeleteKey","deleteError","setDeleteError","secrets","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","secret","SecretRow","onEdit","onDelete","open","onOpenChange","existingKey","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","scope","error","message","button","type","as","role","value","updatedAt","asChild","align","onSelect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-secrets-section.tsx"],"sourcesContent":["import {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 {Panel} from '@shipfox/react-ui/panel';\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 {type SecretMetadata, workspaceStoreScope} from '#core/store.js';\nimport {useDeleteSecretMutation, useSecretsQuery} from '#hooks/api/secrets.js';\nimport {copyKeyName} from './copy-key.js';\nimport {DeleteEntryDialog} from './delete-entry-dialog.js';\nimport {secretsErrorToFormError} from './form-errors.js';\nimport {SecretForm} from './secret-form.js';\nimport {StoreRowsSkeleton} from './store-section-shell.js';\n\nconst SECRETS_DESCRIPTION =\n 'Encrypted, write-only values for sensitive data like API keys, tokens, and passwords.';\nconst EMPTY_SECRETS_DESCRIPTION =\n 'Create a secret to store sensitive values like API keys, tokens, and passwords.';\n\ntype FormState = {mode: 'create'} | {mode: 'edit'; key: string} | null;\n\nexport function WorkspaceSecretsSection({workspaceId}: {workspaceId: string}) {\n const secretsQuery = useSecretsQuery(workspaceId);\n const deleteSecret = useDeleteSecretMutation();\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 secrets = useMemo(\n () => [...(secretsQuery.data ?? [])].sort((a, b) => a.key.localeCompare(b.key)),\n [secretsQuery.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-group\" aria-label=\"Secrets\">\n <div className=\"flex items-start justify-between gap-group\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Secrets</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {SECRETS_DESCRIPTION}\n </Text>\n </div>\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n </div>\n\n {secretsQuery.isPending ? <StoreRowsSkeleton label=\"Loading secrets\" /> : null}\n\n {secretsQuery.isError && secretsQuery.data === undefined ? (\n <Panel>\n <QueryLoadError query={secretsQuery} subject=\"secrets\" variant=\"panel\" />\n </Panel>\n ) : null}\n\n {secretsQuery.data !== undefined && secrets.length === 0 ? (\n <Panel>\n <EmptyState\n icon=\"keyLine\"\n title=\"No secrets yet\"\n description={EMPTY_SECRETS_DESCRIPTION}\n variant=\"panel\"\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create secret\n </Button>\n }\n />\n </Panel>\n ) : null}\n\n {secrets.length > 0 ? (\n <Panel>\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 {secrets.map((secret) => (\n <SecretRow\n key={secret.key}\n secret={secret}\n onEdit={() => setFormState({mode: 'edit', key: secret.key})}\n onDelete={() => setDeleteKey(secret.key)}\n />\n ))}\n </TableBody>\n </Table>\n </Panel>\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 secret' : 'Create secret'}\n </ModalTitle>\n </ModalHeader>\n {formState ? (\n <SecretForm\n workspaceId={workspaceId}\n mode={formState.mode}\n existingKey={formState.mode === 'edit' ? formState.key : undefined}\n reservedKeys={secrets.map((secret) => secret.key)}\n onSaved={() => {\n const wasEdit = formState.mode === 'edit';\n setFormState(null);\n toast.success(wasEdit ? 'Secret updated' : 'Secret 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={deleteSecret.isPending}\n errorMessage={deleteError}\n onConfirm={async () => {\n if (deleteKey === null) return;\n setDeleteError(undefined);\n try {\n await deleteSecret.mutateAsync({\n workspaceId,\n key: deleteKey,\n scope: workspaceStoreScope,\n });\n toast.success('Secret deleted');\n closeDelete();\n } catch (error) {\n setDeleteError(secretsErrorToFormError(error).message);\n }\n }}\n />\n </RelativeTimeProvider>\n );\n}\n\nfunction SecretRow({\n secret,\n onEdit,\n onDelete,\n}: {\n secret: SecretMetadata;\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 secret name ${secret.key}`}\n onClick={() => void copyKeyName(secret.key)}\n >\n <Code as=\"span\" variant=\"paragraph\" className=\"truncate\">\n {secret.key}\n </Code>\n </button>\n </TableCell>\n <TableCell>\n <span\n role=\"img\"\n aria-label=\"Value hidden\"\n className=\"font-code text-foreground-neutral-muted\"\n >\n ••••••••\n </span>\n </TableCell>\n <TableCell className=\"text-foreground-neutral-muted\">\n <RelativeTime value={secret.updatedAt} />\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 ${secret.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","Panel","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","workspaceStoreScope","useDeleteSecretMutation","useSecretsQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","SecretForm","StoreRowsSkeleton","SECRETS_DESCRIPTION","EMPTY_SECRETS_DESCRIPTION","WorkspaceSecretsSection","workspaceId","secretsQuery","deleteSecret","formState","setFormState","deleteKey","setDeleteKey","deleteError","setDeleteError","secrets","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","secret","SecretRow","onEdit","onDelete","open","onOpenChange","existingKey","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","scope","error","message","button","type","as","role","value","updatedAt","asChild","align","onSelect"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-variables-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-variables-section.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,yBAAyB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+
|
|
1
|
+
{"version":3,"file":"workspace-variables-section.d.ts","sourceRoot":"","sources":["../../src/components/workspace-variables-section.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,yBAAyB,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA+I7E"}
|
|
@@ -15,7 +15,7 @@ import { useDeleteVariableMutation, useVariablesQuery } from '#hooks/api/variabl
|
|
|
15
15
|
import { copyKeyName } from './copy-key.js';
|
|
16
16
|
import { DeleteEntryDialog } from './delete-entry-dialog.js';
|
|
17
17
|
import { secretsErrorToFormError } from './form-errors.js';
|
|
18
|
-
import { StoreRowsSkeleton
|
|
18
|
+
import { StoreRowsSkeleton } from './store-section-shell.js';
|
|
19
19
|
import { VariableForm } from './variable-form.js';
|
|
20
20
|
const VARIABLES_DESCRIPTION = 'Plaintext configuration values for non-sensitive data like regions, flags, and log levels.';
|
|
21
21
|
const EMPTY_VARIABLES_DESCRIPTION = 'Create a variable to store non-sensitive configuration like regions, flags, and log levels.';
|
|
@@ -69,19 +69,19 @@ export function WorkspaceVariablesSection({ workspaceId }) {
|
|
|
69
69
|
variablesQuery.isPending ? /*#__PURE__*/ _jsx(StoreRowsSkeleton, {
|
|
70
70
|
label: "Loading variables"
|
|
71
71
|
}) : null,
|
|
72
|
-
variablesQuery.isError && variablesQuery.data === undefined ? /*#__PURE__*/ _jsx(
|
|
73
|
-
className: "px-row",
|
|
72
|
+
variablesQuery.isError && variablesQuery.data === undefined ? /*#__PURE__*/ _jsx(Panel, {
|
|
74
73
|
children: /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
75
74
|
query: variablesQuery,
|
|
76
|
-
subject: "variables"
|
|
75
|
+
subject: "variables",
|
|
76
|
+
variant: "panel"
|
|
77
77
|
})
|
|
78
78
|
}) : null,
|
|
79
|
-
variablesQuery.data !== undefined && variables.length === 0 ? /*#__PURE__*/ _jsx(
|
|
80
|
-
className: "px-row",
|
|
79
|
+
variablesQuery.data !== undefined && variables.length === 0 ? /*#__PURE__*/ _jsx(Panel, {
|
|
81
80
|
children: /*#__PURE__*/ _jsx(EmptyState, {
|
|
82
81
|
icon: "bracesLine",
|
|
83
82
|
title: "No variables yet",
|
|
84
83
|
description: EMPTY_VARIABLES_DESCRIPTION,
|
|
84
|
+
variant: "panel",
|
|
85
85
|
action: /*#__PURE__*/ _jsx(Button, {
|
|
86
86
|
size: "sm",
|
|
87
87
|
onClick: ()=>setFormState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workspace-variables-section.tsx"],"sourcesContent":["import {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 {Panel} from '@shipfox/react-ui/panel';\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 {type VariablePreview, workspaceStoreScope} from '#core/store.js';\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: VariablePreview} | 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-group\" aria-label=\"Variables\">\n <div className=\"flex items-start justify-between gap-group\">\n <div className=\"flex flex-col gap-tight\">\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-row\">\n <QueryLoadError query={variablesQuery} subject=\"variables\" />\n </StoreSurface>\n ) : null}\n\n {variablesQuery.data !== undefined && variables.length === 0 ? (\n <StoreSurface className=\"px-row\">\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 <Panel>\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 </Panel>\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.valueTruncated : 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({\n workspaceId,\n key: deleteKey,\n scope: workspaceStoreScope,\n });\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: VariablePreview;\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.updatedAt} />\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","Panel","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","workspaceStoreScope","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","valueTruncated","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","scope","error","message","button","type","as","updatedAt","asChild","align","onSelect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-variables-section.tsx"],"sourcesContent":["import {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 {Panel} from '@shipfox/react-ui/panel';\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 {type VariablePreview, workspaceStoreScope} from '#core/store.js';\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} 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: VariablePreview} | 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-group\" aria-label=\"Variables\">\n <div className=\"flex items-start justify-between gap-group\">\n <div className=\"flex flex-col gap-tight\">\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 <Panel>\n <QueryLoadError query={variablesQuery} subject=\"variables\" variant=\"panel\" />\n </Panel>\n ) : null}\n\n {variablesQuery.data !== undefined && variables.length === 0 ? (\n <Panel>\n <EmptyState\n icon=\"bracesLine\"\n title=\"No variables yet\"\n description={EMPTY_VARIABLES_DESCRIPTION}\n variant=\"panel\"\n action={\n <Button size=\"sm\" onClick={() => setFormState({mode: 'create'})}>\n Create variable\n </Button>\n }\n />\n </Panel>\n ) : null}\n\n {variables.length > 0 ? (\n <Panel>\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 </Panel>\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.valueTruncated : 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({\n workspaceId,\n key: deleteKey,\n scope: workspaceStoreScope,\n });\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: VariablePreview;\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.updatedAt} />\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","Panel","RelativeTime","RelativeTimeProvider","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","useMemo","useState","workspaceStoreScope","useDeleteVariableMutation","useVariablesQuery","copyKeyName","DeleteEntryDialog","secretsErrorToFormError","StoreRowsSkeleton","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","valueTruncated","reservedKeys","onSaved","wasEdit","success","onCancel","entryKey","isLoading","errorMessage","onConfirm","mutateAsync","scope","error","message","button","type","as","updatedAt","asChild","align","onSelect"],"mappings":"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"}
|