@stackframe/stack 2.8.2 → 2.8.3
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/CHANGELOG.md +10 -0
- package/dist/components/api-key-dialogs.js +184 -0
- package/dist/components/api-key-dialogs.js.map +1 -0
- package/dist/components/api-key-table.js +149 -0
- package/dist/components/api-key-table.js.map +1 -0
- package/dist/components-page/account-settings.js +134 -15
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/esm/components/api-key-dialogs.js +157 -0
- package/dist/esm/components/api-key-dialogs.js.map +1 -0
- package/dist/esm/components/api-key-table.js +125 -0
- package/dist/esm/components/api-key-table.js.map +1 -0
- package/dist/esm/components-page/account-settings.js +132 -15
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/lib/stack-app/api-keys/index.js +14 -6
- package/dist/esm/lib/stack-app/api-keys/index.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +26 -23
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +90 -0
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +148 -8
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/esm/lib/stack-app/index.js.map +1 -1
- package/dist/esm/lib/stack-app/internal-api-keys/index.js +14 -0
- package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -0
- package/dist/esm/lib/stack-app/projects/index.js +3 -1
- package/dist/esm/lib/stack-app/projects/index.js.map +1 -1
- package/dist/esm/lib/stack-app/teams/index.js.map +1 -1
- package/dist/esm/lib/stack-app/users/index.js.map +1 -1
- package/dist/index.d.mts +99 -35
- package/dist/index.d.ts +99 -35
- package/dist/lib/stack-app/api-keys/index.js +16 -7
- package/dist/lib/stack-app/api-keys/index.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +25 -22
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +90 -0
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js +148 -8
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/lib/stack-app/index.js.map +1 -1
- package/dist/lib/stack-app/internal-api-keys/index.js +39 -0
- package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -0
- package/dist/lib/stack-app/project-configs/index.js.map +1 -1
- package/dist/lib/stack-app/projects/index.js +3 -1
- package/dist/lib/stack-app/projects/index.js.map +1 -1
- package/dist/lib/stack-app/teams/index.js.map +1 -1
- package/dist/lib/stack-app/users/index.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/components/api-key-table.tsx
|
|
5
|
+
import { ActionCell, ActionDialog, BadgeCell, DataTable, DataTableColumnHeader, DataTableFacetedFilter, DateCell, SearchToolbarItem, TextCell, standardFilterFn } from "@stackframe/stack-ui";
|
|
6
|
+
import { useMemo, useState } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function toolbarRender(table) {
|
|
9
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10
|
+
/* @__PURE__ */ jsx(SearchToolbarItem, { table, placeholder: "Search table" }),
|
|
11
|
+
/* @__PURE__ */ jsx(
|
|
12
|
+
DataTableFacetedFilter,
|
|
13
|
+
{
|
|
14
|
+
column: table.getColumn("status"),
|
|
15
|
+
title: "Status",
|
|
16
|
+
options: ["valid", "expired", "revoked"].map((provider) => ({
|
|
17
|
+
value: provider,
|
|
18
|
+
label: provider
|
|
19
|
+
}))
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
] });
|
|
23
|
+
}
|
|
24
|
+
function RevokeDialog(props) {
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
ActionDialog,
|
|
27
|
+
{
|
|
28
|
+
open: props.open,
|
|
29
|
+
onOpenChange: props.onOpenChange,
|
|
30
|
+
title: "Revoke API Key",
|
|
31
|
+
danger: true,
|
|
32
|
+
cancelButton: true,
|
|
33
|
+
okButton: { label: "Revoke Key", onClick: async () => {
|
|
34
|
+
await props.apiKey.revoke();
|
|
35
|
+
} },
|
|
36
|
+
confirmText: "I understand this will unlink all the apps using this API key",
|
|
37
|
+
children: `Are you sure you want to revoke API key *****${props.apiKey.value.lastFour}?`
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function Actions({ row }) {
|
|
42
|
+
const [isRevokeModalOpen, setIsRevokeModalOpen] = useState(false);
|
|
43
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
44
|
+
/* @__PURE__ */ jsx(RevokeDialog, { apiKey: row.original, open: isRevokeModalOpen, onOpenChange: setIsRevokeModalOpen }),
|
|
45
|
+
/* @__PURE__ */ jsx(
|
|
46
|
+
ActionCell,
|
|
47
|
+
{
|
|
48
|
+
invisible: row.original.status !== "valid",
|
|
49
|
+
items: [{
|
|
50
|
+
item: "Revoke",
|
|
51
|
+
danger: true,
|
|
52
|
+
onClick: () => setIsRevokeModalOpen(true)
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
var columns = [
|
|
59
|
+
{
|
|
60
|
+
accessorKey: "description",
|
|
61
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Description" }),
|
|
62
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(TextCell, { size: 100, children: row.original.description })
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
accessorKey: "status",
|
|
66
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Status" }),
|
|
67
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(BadgeCell, { badges: [row.original.status] }),
|
|
68
|
+
filterFn: standardFilterFn
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "value",
|
|
72
|
+
accessorFn: (row) => row.value.lastFour,
|
|
73
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Client Key" }),
|
|
74
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs(TextCell, { children: [
|
|
75
|
+
"*******",
|
|
76
|
+
row.original.value.lastFour
|
|
77
|
+
] }),
|
|
78
|
+
enableSorting: false
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
accessorKey: "expiresAt",
|
|
82
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Expires At" }),
|
|
83
|
+
cell: ({ row }) => {
|
|
84
|
+
if (row.original.status === "revoked") return /* @__PURE__ */ jsx(TextCell, { children: "-" });
|
|
85
|
+
return row.original.expiresAt ? /* @__PURE__ */ jsx(DateCell, { date: row.original.expiresAt, ignoreAfterYears: 50 }) : /* @__PURE__ */ jsx(TextCell, { children: "Never" });
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
accessorKey: "createdAt",
|
|
90
|
+
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Created At" }),
|
|
91
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(DateCell, { date: row.original.createdAt, ignoreAfterYears: 50 })
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "actions",
|
|
95
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(Actions, { row })
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
function ApiKeyTable(props) {
|
|
99
|
+
const extendedApiKeys = useMemo(() => {
|
|
100
|
+
const keys = props.apiKeys.map((apiKey) => ({
|
|
101
|
+
...apiKey,
|
|
102
|
+
status: { "valid": "valid", "manually-revoked": "revoked", "expired": "expired" }[apiKey.whyInvalid() || "valid"]
|
|
103
|
+
}));
|
|
104
|
+
return keys.sort((a, b) => {
|
|
105
|
+
if (a.status === b.status) {
|
|
106
|
+
return a.createdAt < b.createdAt ? 1 : -1;
|
|
107
|
+
}
|
|
108
|
+
return a.status === "valid" ? -1 : 1;
|
|
109
|
+
});
|
|
110
|
+
}, [props.apiKeys]);
|
|
111
|
+
return /* @__PURE__ */ jsx(
|
|
112
|
+
DataTable,
|
|
113
|
+
{
|
|
114
|
+
data: extendedApiKeys,
|
|
115
|
+
columns,
|
|
116
|
+
toolbarRender,
|
|
117
|
+
defaultColumnFilters: [],
|
|
118
|
+
defaultSorting: []
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
ApiKeyTable
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=api-key-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/api-key-table.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { ActionCell, ActionDialog, BadgeCell, DataTable, DataTableColumnHeader, DataTableFacetedFilter, DateCell, SearchToolbarItem, TextCell, standardFilterFn } from \"@stackframe/stack-ui\";\nimport { ColumnDef, Row, Table } from \"@tanstack/react-table\";\nimport { useMemo, useState } from \"react\";\nimport { ApiKey } from \"../lib/stack-app/api-keys\";\n\ntype ExtendedApiKey = ApiKey & {\n status: 'valid' | 'expired' | 'revoked',\n};\n\nfunction toolbarRender<TData>(table: Table<TData>) {\n return (\n <>\n <SearchToolbarItem table={table} placeholder=\"Search table\" />\n <DataTableFacetedFilter\n column={table.getColumn(\"status\")}\n title=\"Status\"\n options={['valid', 'expired', 'revoked'].map((provider) => ({\n value: provider,\n label: provider,\n }))}\n />\n </>\n );\n}\n\nfunction RevokeDialog(props: {\n apiKey: ExtendedApiKey,\n open: boolean,\n onOpenChange: (open: boolean) => void,\n}) {\n return <ActionDialog\n open={props.open}\n onOpenChange={props.onOpenChange}\n title=\"Revoke API Key\"\n danger\n cancelButton\n okButton={{ label: \"Revoke Key\", onClick: async () => { await props.apiKey.revoke(); } }}\n confirmText=\"I understand this will unlink all the apps using this API key\"\n >\n {`Are you sure you want to revoke API key *****${props.apiKey.value.lastFour}?`}\n </ActionDialog>;\n}\n\nfunction Actions({ row }: { row: Row<ExtendedApiKey> }) {\n const [isRevokeModalOpen, setIsRevokeModalOpen] = useState(false);\n return (\n <>\n <RevokeDialog apiKey={row.original} open={isRevokeModalOpen} onOpenChange={setIsRevokeModalOpen} />\n <ActionCell\n invisible={row.original.status !== 'valid'}\n items={[{\n item: \"Revoke\",\n danger: true,\n onClick: () => setIsRevokeModalOpen(true),\n }]}\n />\n </>\n );\n}\n\nconst columns: ColumnDef<ExtendedApiKey>[] = [\n {\n accessorKey: \"description\",\n header: ({ column }) => <DataTableColumnHeader column={column} columnTitle=\"Description\" />,\n cell: ({ row }) => <TextCell size={100}>{row.original.description}</TextCell>,\n },\n {\n accessorKey: \"status\",\n header: ({ column }) => <DataTableColumnHeader column={column} columnTitle=\"Status\" />,\n cell: ({ row }) => <BadgeCell badges={[row.original.status]} />,\n filterFn: standardFilterFn,\n },\n {\n id: \"value\",\n accessorFn: (row) => row.value.lastFour,\n header: ({ column }) => <DataTableColumnHeader column={column} columnTitle=\"Client Key\" />,\n cell: ({ row }) => <TextCell>*******{row.original.value.lastFour}</TextCell>,\n enableSorting: false,\n },\n {\n accessorKey: \"expiresAt\",\n header: ({ column }) => <DataTableColumnHeader column={column} columnTitle=\"Expires At\" />,\n cell: ({ row }) => {\n if (row.original.status === 'revoked') return <TextCell>-</TextCell>;\n return row.original.expiresAt ? <DateCell date={row.original.expiresAt} ignoreAfterYears={50} /> : <TextCell>Never</TextCell>;\n },\n },\n {\n accessorKey: \"createdAt\",\n header: ({ column }) => <DataTableColumnHeader column={column} columnTitle=\"Created At\" />,\n cell: ({ row }) => <DateCell date={row.original.createdAt} ignoreAfterYears={50} />\n },\n {\n id: \"actions\",\n cell: ({ row }) => <Actions row={row} />,\n },\n];\n\nexport function ApiKeyTable(props: { apiKeys: ApiKey[] }) {\n const extendedApiKeys = useMemo(() => {\n const keys = props.apiKeys.map((apiKey) => ({\n ...apiKey,\n status: ({ 'valid': 'valid', 'manually-revoked': 'revoked', 'expired': 'expired' } as const)[apiKey.whyInvalid() || 'valid'],\n } satisfies ExtendedApiKey));\n // first sort based on status, then by createdAt\n return keys.sort((a, b) => {\n if (a.status === b.status) {\n return a.createdAt < b.createdAt ? 1 : -1;\n }\n return a.status === 'valid' ? -1 : 1;\n });\n }, [props.apiKeys]);\n\n return <DataTable\n data={extendedApiKeys}\n columns={columns}\n toolbarRender={toolbarRender}\n defaultColumnFilters={[]}\n defaultSorting={[]}\n />;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -10,7 +10,7 @@ import { useAsyncCallback } from "@stackframe/stack-shared/dist/hooks/use-async-
|
|
|
10
10
|
import { passwordSchema as schemaFieldsPasswordSchema, strictEmailSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
|
11
11
|
import { generateRandomValues } from "@stackframe/stack-shared/dist/utils/crypto";
|
|
12
12
|
import { fromNow } from "@stackframe/stack-shared/dist/utils/dates";
|
|
13
|
-
import { captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
|
13
|
+
import { StackAssertionError, captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
|
14
14
|
import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
|
15
15
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActionCell, Badge, Button, Input, Label, PasswordInput, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
|
16
16
|
import { Edit, Trash, icons } from "lucide-react";
|
|
@@ -19,6 +19,8 @@ import { Suspense, useEffect, useState } from "react";
|
|
|
19
19
|
import { useForm } from "react-hook-form";
|
|
20
20
|
import * as yup from "yup";
|
|
21
21
|
import { MessageCard, useStackApp, useUser } from "..";
|
|
22
|
+
import { CreateApiKeyDialog, ShowApiKeyDialog } from "../components/api-key-dialogs";
|
|
23
|
+
import { ApiKeyTable } from "../components/api-key-table";
|
|
22
24
|
import { FormWarningText } from "../components/elements/form-warning";
|
|
23
25
|
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
|
24
26
|
import { SidebarLayout } from "../components/elements/sidebar-layout";
|
|
@@ -62,6 +64,13 @@ function AccountSettings(props) {
|
|
|
62
64
|
icon: /* @__PURE__ */ jsx(Icon, { name: "Monitor" }),
|
|
63
65
|
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ActiveSessionsPageSkeleton, {}), children: /* @__PURE__ */ jsx(ActiveSessionsPage, {}) })
|
|
64
66
|
},
|
|
67
|
+
...project.config.allowUserApiKeys ? [{
|
|
68
|
+
title: t("API Keys"),
|
|
69
|
+
type: "item",
|
|
70
|
+
id: "api-keys",
|
|
71
|
+
icon: /* @__PURE__ */ jsx(Icon, { name: "Key" }),
|
|
72
|
+
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ jsx(ApiKeysPage, {}) })
|
|
73
|
+
}] : [],
|
|
65
74
|
{
|
|
66
75
|
title: t("Settings"),
|
|
67
76
|
type: "item",
|
|
@@ -95,14 +104,14 @@ function AccountSettings(props) {
|
|
|
95
104
|
] }),
|
|
96
105
|
type: "item",
|
|
97
106
|
id: `team-${team.id}`,
|
|
98
|
-
content: /* @__PURE__ */ jsx(TeamPage, { team })
|
|
107
|
+
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(TeamPageSkeleton, {}), children: /* @__PURE__ */ jsx(TeamPage, { team }) })
|
|
99
108
|
})),
|
|
100
109
|
...project.config.clientTeamCreationEnabled ? [{
|
|
101
110
|
title: t("Create a team"),
|
|
102
111
|
icon: /* @__PURE__ */ jsx(Icon, { name: "CirclePlus" }),
|
|
103
112
|
type: "item",
|
|
104
113
|
id: "team-creation",
|
|
105
|
-
content: /* @__PURE__ */ jsx(TeamCreation, {})
|
|
114
|
+
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(TeamCreationSkeleton, {}), children: /* @__PURE__ */ jsx(TeamCreation, {}) })
|
|
106
115
|
}] : []
|
|
107
116
|
].filter((p) => p.type === "divider" || p.content),
|
|
108
117
|
title: t("Account Settings")
|
|
@@ -124,6 +133,88 @@ function Section(props) {
|
|
|
124
133
|
function PageLayout(props) {
|
|
125
134
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6", children: props.children });
|
|
126
135
|
}
|
|
136
|
+
function ApiKeysPage() {
|
|
137
|
+
const { t } = useTranslation();
|
|
138
|
+
const user = useUser({ or: "redirect" });
|
|
139
|
+
const apiKeys = user.useApiKeys();
|
|
140
|
+
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);
|
|
141
|
+
const [returnedApiKey, setReturnedApiKey] = useState(null);
|
|
142
|
+
const CreateDialog = CreateApiKeyDialog;
|
|
143
|
+
const ShowDialog = ShowApiKeyDialog;
|
|
144
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
145
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") }),
|
|
146
|
+
/* @__PURE__ */ jsx(ApiKeyTable, { apiKeys }),
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
CreateDialog,
|
|
149
|
+
{
|
|
150
|
+
open: isNewApiKeyDialogOpen,
|
|
151
|
+
onOpenChange: setIsNewApiKeyDialogOpen,
|
|
152
|
+
onKeyCreated: setReturnedApiKey,
|
|
153
|
+
createApiKey: async (data) => {
|
|
154
|
+
const apiKey = await user.createApiKey(data);
|
|
155
|
+
return apiKey;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
/* @__PURE__ */ jsx(
|
|
160
|
+
ShowDialog,
|
|
161
|
+
{
|
|
162
|
+
apiKey: returnedApiKey,
|
|
163
|
+
onClose: () => setReturnedApiKey(null)
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
function TeamApiKeysSection(props) {
|
|
169
|
+
const user = useUser({ or: "redirect" });
|
|
170
|
+
const team = user.useTeam(props.team.id);
|
|
171
|
+
if (!team) {
|
|
172
|
+
throw new StackAssertionError("Team not found");
|
|
173
|
+
}
|
|
174
|
+
const manageApiKeysPermission = user.usePermission(props.team, "$manage_api_keys");
|
|
175
|
+
if (!manageApiKeysPermission) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
return /* @__PURE__ */ jsx(TeamApiKeysSectionInner, { team: props.team });
|
|
179
|
+
}
|
|
180
|
+
function TeamApiKeysSectionInner(props) {
|
|
181
|
+
const { t } = useTranslation();
|
|
182
|
+
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);
|
|
183
|
+
const [returnedApiKey, setReturnedApiKey] = useState(null);
|
|
184
|
+
const apiKeys = props.team.useApiKeys();
|
|
185
|
+
const CreateDialog = CreateApiKeyDialog;
|
|
186
|
+
const ShowDialog = ShowApiKeyDialog;
|
|
187
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
Section,
|
|
190
|
+
{
|
|
191
|
+
title: t("API Keys"),
|
|
192
|
+
description: t("API keys grant programmatic access to your team."),
|
|
193
|
+
children: /* @__PURE__ */ jsx(Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") })
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
/* @__PURE__ */ jsx(ApiKeyTable, { apiKeys }),
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
198
|
+
CreateDialog,
|
|
199
|
+
{
|
|
200
|
+
open: isNewApiKeyDialogOpen,
|
|
201
|
+
onOpenChange: setIsNewApiKeyDialogOpen,
|
|
202
|
+
onKeyCreated: setReturnedApiKey,
|
|
203
|
+
createApiKey: async (data) => {
|
|
204
|
+
const apiKey = await props.team.createApiKey(data);
|
|
205
|
+
return apiKey;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
),
|
|
209
|
+
/* @__PURE__ */ jsx(
|
|
210
|
+
ShowDialog,
|
|
211
|
+
{
|
|
212
|
+
apiKey: returnedApiKey,
|
|
213
|
+
onClose: () => setReturnedApiKey(null)
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
] });
|
|
217
|
+
}
|
|
127
218
|
function ProfilePage() {
|
|
128
219
|
const { t } = useTranslation();
|
|
129
220
|
const user = useUser({ or: "redirect" });
|
|
@@ -801,12 +892,13 @@ function SignOutSection() {
|
|
|
801
892
|
}
|
|
802
893
|
function TeamPage(props) {
|
|
803
894
|
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
804
|
-
/* @__PURE__ */ jsx(TeamUserProfileSection, { team: props.team }),
|
|
805
|
-
/* @__PURE__ */ jsx(
|
|
806
|
-
/* @__PURE__ */ jsx(
|
|
807
|
-
/* @__PURE__ */ jsx(
|
|
808
|
-
/* @__PURE__ */ jsx(
|
|
809
|
-
/* @__PURE__ */ jsx(
|
|
895
|
+
/* @__PURE__ */ jsx(TeamUserProfileSection, { team: props.team }, `user-profile-${props.team.id}`),
|
|
896
|
+
/* @__PURE__ */ jsx(TeamProfileImageSection, { team: props.team }, `profile-image-${props.team.id}`),
|
|
897
|
+
/* @__PURE__ */ jsx(TeamDisplayNameSection, { team: props.team }, `display-name-${props.team.id}`),
|
|
898
|
+
/* @__PURE__ */ jsx(MemberListSection, { team: props.team }, `member-list-${props.team.id}`),
|
|
899
|
+
/* @__PURE__ */ jsx(MemberInvitationSection, { team: props.team }, `member-invitation-${props.team.id}`),
|
|
900
|
+
/* @__PURE__ */ jsx(TeamApiKeysSection, { team: props.team }, `api-keys-${props.team.id}`),
|
|
901
|
+
/* @__PURE__ */ jsx(LeaveTeamSection, { team: props.team }, `leave-team-${props.team.id}`)
|
|
810
902
|
] });
|
|
811
903
|
}
|
|
812
904
|
function LeaveTeamSection(props) {
|
|
@@ -938,11 +1030,14 @@ function MemberInvitationsSectionInvitationsList(props) {
|
|
|
938
1030
|
/* @__PURE__ */ jsx(TableHead, { className: "w-[60px]", children: t("Expires") }),
|
|
939
1031
|
/* @__PURE__ */ jsx(TableHead, { className: "w-[36px] max-w-[36px]" })
|
|
940
1032
|
] }) }),
|
|
941
|
-
/* @__PURE__ */
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1033
|
+
/* @__PURE__ */ jsxs(TableBody, { children: [
|
|
1034
|
+
invitationsToShow.map((invitation, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
1035
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: invitation.recipientEmail }) }),
|
|
1036
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { variant: "secondary", children: invitation.expiresAt.toLocaleString() }) }),
|
|
1037
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", className: "max-w-[36px]", children: removeMemberPermission && /* @__PURE__ */ jsx(Button, { onClick: async () => await invitation.revoke(), size: "icon", variant: "ghost", children: /* @__PURE__ */ jsx(Trash, { className: "w-4 h-4" }) }) })
|
|
1038
|
+
] }, invitation.id)),
|
|
1039
|
+
invitationsToShow.length === 0 && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: 3, children: /* @__PURE__ */ jsx(Typography, { variant: "secondary", children: t("No outstanding invitations") }) }) })
|
|
1040
|
+
] })
|
|
946
1041
|
] }) });
|
|
947
1042
|
}
|
|
948
1043
|
function MemberInvitationSectionInner(props) {
|
|
@@ -1171,8 +1266,30 @@ function EditableText(props) {
|
|
|
1171
1266
|
/* @__PURE__ */ jsx(Button, { onClick: () => setEditing(true), size: "icon", variant: "ghost", children: /* @__PURE__ */ jsx(Edit, { className: "w-4 h-4" }) })
|
|
1172
1267
|
] }) });
|
|
1173
1268
|
}
|
|
1269
|
+
function ApiKeysPageSkeleton() {
|
|
1270
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
1271
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
|
1272
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
|
1273
|
+
] });
|
|
1274
|
+
}
|
|
1275
|
+
function TeamPageSkeleton() {
|
|
1276
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
1277
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
|
1278
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
|
1279
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
|
1280
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
|
1281
|
+
] });
|
|
1282
|
+
}
|
|
1283
|
+
function TeamCreationSkeleton() {
|
|
1284
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
1285
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
|
1286
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" })
|
|
1287
|
+
] });
|
|
1288
|
+
}
|
|
1174
1289
|
export {
|
|
1175
1290
|
AccountSettings,
|
|
1176
|
-
|
|
1291
|
+
ApiKeysPage,
|
|
1292
|
+
EditableText,
|
|
1293
|
+
TeamApiKeysSection
|
|
1177
1294
|
};
|
|
1178
1295
|
//# sourceMappingURL=account-settings.js.map
|