@workos-inc/widgets 1.19.0 → 1.19.1-next.1790792916241
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 +8 -0
- package/dist/cjs/admin-portal-sso-connection.client.cjs +72 -75
- package/dist/cjs/admin-portal-sso-connection.client.cjs.map +1 -1
- package/dist/cjs/lib/admin-portal-audit-log-streaming.cjs +5 -22
- package/dist/cjs/lib/admin-portal-audit-log-streaming.cjs.map +1 -1
- package/dist/cjs/lib/admin-portal-sso-connection.cjs +7 -41
- package/dist/cjs/lib/admin-portal-sso-connection.cjs.map +1 -1
- package/dist/cjs/lib/admin-portal-sso-connection.d.cts +0 -1
- package/dist/cjs/lib/api-keys/api-key-expiration.cjs +5 -3
- package/dist/cjs/lib/api-keys/api-key-expiration.cjs.map +1 -1
- package/dist/cjs/lib/api-keys/api-key-expiration.d.cts +1 -1
- package/dist/cjs/lib/api-keys/create-api-key.cjs +4 -1
- package/dist/cjs/lib/api-keys/create-api-key.cjs.map +1 -1
- package/dist/cjs/lib/api-keys/relative-time.cjs +7 -12
- package/dist/cjs/lib/api-keys/relative-time.cjs.map +1 -1
- package/dist/cjs/lib/directory-sync.cjs +5 -30
- package/dist/cjs/lib/directory-sync.cjs.map +1 -1
- package/dist/cjs/lib/domain-item.cjs +5 -10
- package/dist/cjs/lib/domain-item.cjs.map +1 -1
- package/dist/cjs/lib/user-api-keys/create-user-api-key.cjs +4 -1
- package/dist/cjs/lib/user-api-keys/create-user-api-key.cjs.map +1 -1
- package/dist/cjs/lib/user-security.cjs +9 -17
- package/dist/cjs/lib/user-security.cjs.map +1 -1
- package/dist/cjs/lib/user-sessions.cjs +9 -13
- package/dist/cjs/lib/user-sessions.cjs.map +1 -1
- package/dist/cjs/lib/user-sessions.d.cts +2 -2
- package/dist/cjs/lib/users-management.cjs +14 -24
- package/dist/cjs/lib/users-management.cjs.map +1 -1
- package/dist/cjs/lib/utils.cjs +49 -13
- package/dist/cjs/lib/utils.cjs.map +1 -1
- package/dist/cjs/use-comparative-readable-date.cjs +70 -0
- package/dist/cjs/use-comparative-readable-date.cjs.map +1 -0
- package/dist/cjs/use-comparative-readable-date.d.cts +8 -0
- package/dist/cjs/use-now.cjs +101 -0
- package/dist/cjs/use-now.cjs.map +1 -0
- package/dist/cjs/use-now.d.cts +3 -0
- package/dist/esm/admin-portal-sso-connection.client.js +73 -66
- package/dist/esm/admin-portal-sso-connection.client.js.map +1 -1
- package/dist/esm/lib/admin-portal-audit-log-streaming.js +5 -22
- package/dist/esm/lib/admin-portal-audit-log-streaming.js.map +1 -1
- package/dist/esm/lib/admin-portal-sso-connection.d.ts +0 -1
- package/dist/esm/lib/admin-portal-sso-connection.js +7 -41
- package/dist/esm/lib/admin-portal-sso-connection.js.map +1 -1
- package/dist/esm/lib/api-keys/api-key-expiration.d.ts +1 -1
- package/dist/esm/lib/api-keys/api-key-expiration.js +5 -3
- package/dist/esm/lib/api-keys/api-key-expiration.js.map +1 -1
- package/dist/esm/lib/api-keys/create-api-key.js +4 -1
- package/dist/esm/lib/api-keys/create-api-key.js.map +1 -1
- package/dist/esm/lib/api-keys/relative-time.js +7 -12
- package/dist/esm/lib/api-keys/relative-time.js.map +1 -1
- package/dist/esm/lib/directory-sync.js +5 -30
- package/dist/esm/lib/directory-sync.js.map +1 -1
- package/dist/esm/lib/domain-item.js +5 -10
- package/dist/esm/lib/domain-item.js.map +1 -1
- package/dist/esm/lib/identity-providers.d.ts +2 -2
- package/dist/esm/lib/user-api-keys/create-user-api-key.js +4 -1
- package/dist/esm/lib/user-api-keys/create-user-api-key.js.map +1 -1
- package/dist/esm/lib/user-security.js +9 -18
- package/dist/esm/lib/user-security.js.map +1 -1
- package/dist/esm/lib/user-sessions.d.ts +2 -2
- package/dist/esm/lib/user-sessions.js +9 -14
- package/dist/esm/lib/user-sessions.js.map +1 -1
- package/dist/esm/lib/users-management.js +14 -25
- package/dist/esm/lib/users-management.js.map +1 -1
- package/dist/esm/lib/utils.js +49 -13
- package/dist/esm/lib/utils.js.map +1 -1
- package/dist/esm/use-comparative-readable-date.d.ts +8 -0
- package/dist/esm/use-comparative-readable-date.js +36 -0
- package/dist/esm/use-comparative-readable-date.js.map +1 -0
- package/dist/esm/use-now.d.ts +3 -0
- package/dist/esm/use-now.js +67 -0
- package/dist/esm/use-now.js.map +1 -0
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { Translation } from "./i18n/translation.js";
|
|
23
23
|
import { useTranslation } from "./i18n/use-translation.js";
|
|
24
24
|
import { getErrorMessage } from "./generic-error.js";
|
|
25
|
+
import { useComparativeReadableDate } from "../use-comparative-readable-date.js";
|
|
25
26
|
const AdminPortalSsoConnectionContext = React.createContext(null);
|
|
26
27
|
AdminPortalSsoConnectionContext.displayName = "AdminPortalSsoConnectionContext";
|
|
27
28
|
function useAdminPortalSsoConnectionContext() {
|
|
@@ -34,7 +35,6 @@ function useAdminPortalSsoConnectionContext() {
|
|
|
34
35
|
return context;
|
|
35
36
|
}
|
|
36
37
|
const AdminPortalSsoConnection = ({
|
|
37
|
-
currentDate,
|
|
38
38
|
connectionStatus,
|
|
39
39
|
identityProvider,
|
|
40
40
|
expiryDate,
|
|
@@ -63,8 +63,7 @@ const AdminPortalSsoConnection = ({
|
|
|
63
63
|
{
|
|
64
64
|
connectionStatus,
|
|
65
65
|
identityProvider,
|
|
66
|
-
lastSession
|
|
67
|
-
currentDate
|
|
66
|
+
lastSession
|
|
68
67
|
}
|
|
69
68
|
),
|
|
70
69
|
/* @__PURE__ */ jsxs(Flex, { gap: "5", align: "center", children: [
|
|
@@ -177,28 +176,24 @@ function SsoStatus({
|
|
|
177
176
|
return unreachable(status);
|
|
178
177
|
}
|
|
179
178
|
function SessionInfo({
|
|
180
|
-
currentDate,
|
|
181
179
|
identityProvider,
|
|
182
180
|
lastSession,
|
|
183
181
|
connectionStatus
|
|
184
182
|
}) {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
return getRelativeTimeString(currentDate, lastSession);
|
|
190
|
-
}, [lastSession, currentDate, connectionStatus]);
|
|
183
|
+
const relativeTime = useComparativeReadableDate({
|
|
184
|
+
date: connectionStatus === "NotConfigured" || connectionStatus === "Inactive" ? null : lastSession
|
|
185
|
+
});
|
|
191
186
|
return /* @__PURE__ */ jsxs(Flex, { gap: "4", align: "center", children: [
|
|
192
187
|
/* @__PURE__ */ jsx(IconPanel, { color: "panel", children: /* @__PURE__ */ jsx(ProviderIcon, { provider: identityProvider, size: "2" }) }),
|
|
193
188
|
lastSession ? /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
194
189
|
/* @__PURE__ */ jsx(Text, { size: "2", weight: "bold", children: getIdentityProviderName(identityProvider) }),
|
|
195
|
-
|
|
190
|
+
relativeTime ? /* @__PURE__ */ jsx(Text, { color: "gray", size: "2", children: /* @__PURE__ */ jsx(
|
|
196
191
|
Translation,
|
|
197
192
|
{
|
|
198
193
|
defaultMessage: "Last session {relativeTime}",
|
|
199
194
|
id: "yN72Gc",
|
|
200
195
|
description: "Label showing when the last SSO session occurred",
|
|
201
|
-
values: { relativeTime
|
|
196
|
+
values: { relativeTime }
|
|
202
197
|
}
|
|
203
198
|
) }) : null
|
|
204
199
|
] }) : /* @__PURE__ */ jsx(Text, { size: "2", weight: "bold", children: getIdentityProviderName(identityProvider) })
|
|
@@ -306,35 +301,6 @@ const AdminPortalSsoConnectionError = ({ error, ...domProps }) => {
|
|
|
306
301
|
] })
|
|
307
302
|
] }) }) });
|
|
308
303
|
};
|
|
309
|
-
function getRelativeTimeString(currentDate, lastSession) {
|
|
310
|
-
if (!currentDate || !lastSession) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
314
|
-
const diff = lastSession.getTime() - currentDate.getTime();
|
|
315
|
-
const diffSeconds = Math.round(diff / 1e3);
|
|
316
|
-
const diffMinutes = Math.round(diffSeconds / 60);
|
|
317
|
-
const diffHours = Math.round(diffMinutes / 60);
|
|
318
|
-
const diffDays = Math.round(diffHours / 24);
|
|
319
|
-
const diffMonths = Math.round(diffDays / 30);
|
|
320
|
-
const diffYears = Math.round(diffMonths / 12);
|
|
321
|
-
if (Math.abs(diffSeconds) < 60) {
|
|
322
|
-
return "now";
|
|
323
|
-
}
|
|
324
|
-
if (Math.abs(diffMinutes) < 60) {
|
|
325
|
-
return rtf.format(diffMinutes, "minute");
|
|
326
|
-
}
|
|
327
|
-
if (Math.abs(diffHours) < 24) {
|
|
328
|
-
return rtf.format(diffHours, "hour");
|
|
329
|
-
}
|
|
330
|
-
if (Math.abs(diffDays) < 30) {
|
|
331
|
-
return rtf.format(diffDays, "day");
|
|
332
|
-
}
|
|
333
|
-
if (Math.abs(diffMonths) < 12) {
|
|
334
|
-
return rtf.format(diffMonths, "month");
|
|
335
|
-
}
|
|
336
|
-
return rtf.format(diffYears, "year");
|
|
337
|
-
}
|
|
338
304
|
function getWidgetRootDomProps(state, domProps) {
|
|
339
305
|
return getDomProps({
|
|
340
306
|
...domProps,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/admin-portal-sso-connection.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport { Button, Skeleton } from \"./elements.js\";\nimport { IconPanel } from \"./icon-panel.js\";\nimport {\n ExternalLinkIcon,\n InfoCircledIcon,\n Cross2Icon,\n} from \"@radix-ui/react-icons\";\nimport * as CardList from \"./card-list.js\";\nimport { ProviderIcon } from \"./provider-icon.js\";\nimport {\n getIdentityProviderName,\n type IdentityProvider,\n} from \"./identity-providers.js\";\nimport { Status } from \"./status.js\";\nimport {\n getDomProps,\n unreachable,\n type WidgetRootState,\n type WidgetRootDomProps,\n} from \"./utils.js\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useTranslation } from \"./i18n/use-translation.js\";\nimport { getErrorMessage } from \"./generic-error.js\";\n\ninterface NotConfiguredProps {\n connectionStatus: \"NotConfigured\";\n identityProvider?: never;\n expiryDate?: never;\n}\n\ninterface InactiveProps {\n connectionStatus: \"Inactive\";\n identityProvider: IdentityProvider;\n expiryDate?: never;\n}\n\ninterface ActiveProps {\n connectionStatus: \"Active\";\n identityProvider: IdentityProvider;\n expiryDate?: never;\n}\n\ninterface RequiresActionProps {\n connectionStatus: \"Expired\" | \"Expiring\";\n identityProvider: IdentityProvider;\n expiryDate: Date | null;\n}\n\nexport type AdminPortalSsoConnectionStatusProps =\n | NotConfiguredProps\n | InactiveProps\n | ActiveProps\n | RequiresActionProps;\n\ntype AdminPortalSsoConnectionProps = WidgetRootDomProps &\n AdminPortalSsoConnectionStatusProps & {\n currentDate: Date | null;\n lastSession: Date | null;\n adminPortalOpenButton: React.ReactNode;\n };\n\ninterface AdminPortalSsoConnectionContextValue {\n connectionStatus: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}\n\nconst AdminPortalSsoConnectionContext =\n React.createContext<AdminPortalSsoConnectionContextValue | null>(null);\nAdminPortalSsoConnectionContext.displayName = \"AdminPortalSsoConnectionContext\";\n\nfunction useAdminPortalSsoConnectionContext() {\n const context = React.useContext(AdminPortalSsoConnectionContext);\n if (!context) {\n throw new Error(\n \"useAdminPortalSsoConnectionContext must be used within a AdminPortalSsoConnectionContext provider\",\n );\n }\n return context;\n}\n\nconst AdminPortalSsoConnection: React.FC<AdminPortalSsoConnectionProps> = ({\n currentDate,\n connectionStatus,\n identityProvider,\n expiryDate,\n lastSession,\n adminPortalOpenButton,\n ...domProps\n}) => {\n return (\n <CardList.Root size=\"2\" {...getWidgetRootDomProps(\"resolved\", domProps)}>\n <AdminPortalSsoConnectionContext.Provider value={{ connectionStatus }}>\n <CardList.Item>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n {(() => {\n if (connectionStatus === \"NotConfigured\") {\n return (\n <>\n <Text size=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"You haven't set up Single Sign-On yet.\"\n id=\"KDs1Ib\"\n description=\"Empty state message when SSO is not configured\"\n />\n </Text>\n {adminPortalOpenButton}\n </>\n );\n }\n\n return (\n <>\n <SessionInfo\n connectionStatus={connectionStatus}\n identityProvider={identityProvider}\n lastSession={lastSession}\n currentDate={currentDate}\n />\n <Flex gap=\"5\" align=\"center\">\n <SsoStatus status={connectionStatus} />\n {adminPortalOpenButton}\n </Flex>\n </>\n );\n })()}\n </Flex>\n </CardList.Item>\n {connectionStatus === \"Expired\" && (\n <CardList.Item>\n <Flex align=\"start\" gap=\"2\">\n <Box asChild mt=\"2px\" flexShrink=\"0\">\n <InfoCircledIcon color=\"gray\" />\n </Box>\n {(() => {\n if (connectionStatus === \"Expired\") {\n return (\n <Text size=\"2\" color=\"gray\">\n {(() => {\n if (!expiryDate) {\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate has expired. Users won't be able to sign-in to the application until the certificate is renewed.\"\n id=\"5tX19v\"\n description=\"Error message when SAML certificate has expired (no date)\"\n />\n );\n }\n\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate expired on {expiryDate}. Users won't be able to sign-in to the application until the certificate is renewed.\"\n id=\"vmTW/h\"\n description=\"Error message when SAML certificate has expired with date\"\n values={{\n expiryDate: (\n <Text weight=\"bold\" as=\"span\">\n {expiryDate?.toLocaleString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n })}\n </Text>\n ),\n }}\n />\n );\n })()}\n </Text>\n );\n }\n\n if (connectionStatus === \"Expiring\") {\n return (\n <Text size=\"2\" color=\"gray\">\n {(() => {\n if (!expiryDate) {\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate is expiring soon. When expired, users won't be able to sign-in.\"\n id=\"3dgA5f\"\n description=\"Warning message when SAML certificate is expiring soon (no date)\"\n />\n );\n }\n\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate will expire on {expiryDate}. When expired, users won't be able to sign-in.\"\n id=\"xT9Exy\"\n description=\"Warning message when SAML certificate is expiring with date\"\n values={{\n expiryDate: (\n <Text weight=\"bold\" as=\"span\">\n {expiryDate?.toLocaleString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n })}\n </Text>\n ),\n }}\n />\n );\n })()}\n </Text>\n );\n }\n })()}\n </Flex>\n </CardList.Item>\n )}\n </AdminPortalSsoConnectionContext.Provider>\n </CardList.Root>\n );\n};\n\nfunction SsoStatus({\n status,\n}: {\n status: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}) {\n if (status === \"NotConfigured\") {\n return null;\n }\n\n if (status === \"Inactive\") {\n return (\n <Status state=\"waiting\">\n <Translation\n defaultMessage=\"Setup in progress\"\n id=\"MQR5mR\"\n description=\"Status label when SSO setup is incomplete\"\n />\n </Status>\n );\n }\n\n if (status === \"Expired\") {\n return (\n <Status state=\"error\">\n <Translation\n defaultMessage=\"Requires action\"\n id=\"Yo2vHC\"\n description=\"Status label when SSO requires attention\"\n />\n </Status>\n );\n }\n\n if (status === \"Active\" || status === \"Expiring\") {\n return (\n <Status state=\"success\">\n <Translation\n defaultMessage=\"Connected\"\n id=\"8S+Cyw\"\n description=\"Status label when SSO is active\"\n />\n </Status>\n );\n }\n\n return unreachable(status);\n}\n\nfunction SessionInfo({\n currentDate,\n identityProvider,\n lastSession,\n connectionStatus,\n}: {\n identityProvider: IdentityProvider;\n lastSession: Date | null;\n currentDate: Date | null;\n connectionStatus: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}) {\n const relativeTimeString = React.useMemo(() => {\n if (\n !lastSession ||\n !currentDate ||\n connectionStatus === \"NotConfigured\" ||\n connectionStatus === \"Inactive\"\n ) {\n return null;\n }\n\n return getRelativeTimeString(currentDate, lastSession);\n }, [lastSession, currentDate, connectionStatus]);\n\n return (\n <Flex gap=\"4\" align=\"center\">\n <IconPanel color=\"panel\">\n <ProviderIcon provider={identityProvider} size=\"2\" />\n </IconPanel>\n {lastSession ? (\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {getIdentityProviderName(identityProvider)}\n </Text>\n {relativeTimeString ? (\n <Text color=\"gray\" size=\"2\">\n <Translation\n defaultMessage=\"Last session {relativeTime}\"\n id=\"yN72Gc\"\n description=\"Label showing when the last SSO session occurred\"\n values={{ relativeTime: relativeTimeString }}\n />\n </Text>\n ) : null}\n </Flex>\n ) : (\n <Text size=\"2\" weight=\"bold\">\n {getIdentityProviderName(identityProvider)}\n </Text>\n )}\n </Flex>\n );\n}\n\nfunction AdminPortalOpenButton({\n isPending,\n initConfig,\n}: {\n isPending: boolean;\n initConfig: () => void;\n}) {\n const { connectionStatus } = useAdminPortalSsoConnectionContext();\n const label = (() => {\n switch (connectionStatus) {\n case \"NotConfigured\":\n return (\n <Translation\n defaultMessage=\"Set up SSO\"\n id=\"MtkNQO\"\n description=\"Button label to start SSO setup\"\n />\n );\n case \"Inactive\":\n return (\n <Translation\n defaultMessage=\"Continue setup\"\n id=\"HFxrbY\"\n description=\"Button label to continue incomplete SSO setup\"\n />\n );\n case \"Active\":\n case \"Expiring\":\n case \"Expired\":\n return (\n <Translation\n defaultMessage=\"Manage\"\n id=\"T1VKzP\"\n description=\"Button label to manage SSO settings\"\n />\n );\n default:\n return unreachable(connectionStatus);\n }\n })();\n\n return (\n <Button\n variant=\"secondary\"\n loading={isPending}\n disabled={isPending}\n onClick={initConfig}\n >\n {label} <ExternalLinkIcon aria-hidden />\n </Button>\n );\n}\n\ninterface AdminPortalSsoConnectionLoadingProps extends WidgetRootDomProps {}\n\nconst AdminPortalSsoConnectionLoading: React.FC<\n AdminPortalSsoConnectionLoadingProps\n> = (props) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"loading\", props)}>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n <Flex gap=\"4\" align=\"center\">\n <Skeleton>\n <IconPanel color=\"panel\">\n <ProviderIcon provider=\"okta\" size=\"2\" />\n </IconPanel>\n </Skeleton>\n <Flex direction=\"column\" gap=\"1\" my=\"-4px\">\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"1\">Okta</Text>\n </Skeleton>\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"1\">Last session 10 minutes ago</Text>\n </Skeleton>\n </Flex>\n </Flex>\n <Flex gap=\"5\" align=\"center\">\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Status state=\"error\">Requires action</Status>\n </Skeleton>\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Button variant=\"secondary\">\n Manage <ExternalLinkIcon aria-hidden />\n </Button>\n </Skeleton>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\ninterface AdminPortalSsoConnectionErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst AdminPortalSsoConnectionError: React.FC<\n AdminPortalSsoConnectionErrorProps\n> = ({ error, ...domProps }) => {\n const translate = useTranslation();\n React.useEffect(() => {\n console.error(error);\n }, [error]);\n\n const { heading, message } = getErrorMessage(error, translate);\n\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"error\", domProps)}>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n <Flex gap=\"4\" align=\"center\">\n <Flex\n align=\"center\"\n justify=\"center\"\n width=\"24px\"\n height=\"24px\"\n style={{\n borderRadius: \"9999px\",\n backgroundColor: \"var(--red-a4)\",\n color: \"var(--red-a11)\",\n flexShrink: 0,\n }}\n >\n <Cross2Icon width=\"18px\" height=\"18px\" />\n </Flex>\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {heading}\n </Text>\n <Text size=\"2\" color=\"gray\">\n {message}\n </Text>\n </Flex>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\nfunction getRelativeTimeString(\n currentDate: Date | null,\n lastSession: Date | null,\n) {\n if (!currentDate || !lastSession) {\n return null;\n }\n\n const rtf = new Intl.RelativeTimeFormat(\"en\", { numeric: \"auto\" });\n const diff = lastSession.getTime() - currentDate.getTime();\n\n const diffSeconds = Math.round(diff / 1000);\n const diffMinutes = Math.round(diffSeconds / 60);\n const diffHours = Math.round(diffMinutes / 60);\n const diffDays = Math.round(diffHours / 24);\n const diffMonths = Math.round(diffDays / 30);\n const diffYears = Math.round(diffMonths / 12);\n\n if (Math.abs(diffSeconds) < 60) {\n return \"now\";\n }\n\n if (Math.abs(diffMinutes) < 60) {\n return rtf.format(diffMinutes, \"minute\");\n }\n\n if (Math.abs(diffHours) < 24) {\n return rtf.format(diffHours, \"hour\");\n }\n\n if (Math.abs(diffDays) < 30) {\n return rtf.format(diffDays, \"day\");\n }\n\n if (Math.abs(diffMonths) < 12) {\n return rtf.format(diffMonths, \"month\");\n }\n\n return rtf.format(diffYears, \"year\");\n}\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"admin-portal-sso-connection\",\n widgetState: state,\n });\n}\n\nexport type {\n AdminPortalSsoConnectionProps,\n AdminPortalSsoConnectionLoadingProps,\n AdminPortalSsoConnectionErrorProps,\n};\nexport {\n AdminPortalSsoConnection,\n AdminPortalSsoConnectionLoading,\n AdminPortalSsoConnectionError,\n /** @internal */\n AdminPortalOpenButton,\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/admin-portal-sso-connection.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport { Button, Skeleton } from \"./elements.js\";\nimport { IconPanel } from \"./icon-panel.js\";\nimport {\n ExternalLinkIcon,\n InfoCircledIcon,\n Cross2Icon,\n} from \"@radix-ui/react-icons\";\nimport * as CardList from \"./card-list.js\";\nimport { ProviderIcon } from \"./provider-icon.js\";\nimport {\n getIdentityProviderName,\n type IdentityProvider,\n} from \"./identity-providers.js\";\nimport { Status } from \"./status.js\";\nimport {\n getDomProps,\n unreachable,\n type WidgetRootState,\n type WidgetRootDomProps,\n} from \"./utils.js\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useTranslation } from \"./i18n/use-translation.js\";\nimport { getErrorMessage } from \"./generic-error.js\";\nimport { useComparativeReadableDate } from \"../use-comparative-readable-date.js\";\n\ninterface NotConfiguredProps {\n connectionStatus: \"NotConfigured\";\n identityProvider?: never;\n expiryDate?: never;\n}\n\ninterface InactiveProps {\n connectionStatus: \"Inactive\";\n identityProvider: IdentityProvider;\n expiryDate?: never;\n}\n\ninterface ActiveProps {\n connectionStatus: \"Active\";\n identityProvider: IdentityProvider;\n expiryDate?: never;\n}\n\ninterface RequiresActionProps {\n connectionStatus: \"Expired\" | \"Expiring\";\n identityProvider: IdentityProvider;\n expiryDate: Date | null;\n}\n\nexport type AdminPortalSsoConnectionStatusProps =\n | NotConfiguredProps\n | InactiveProps\n | ActiveProps\n | RequiresActionProps;\n\ntype AdminPortalSsoConnectionProps = WidgetRootDomProps &\n AdminPortalSsoConnectionStatusProps & {\n lastSession: Date | null;\n adminPortalOpenButton: React.ReactNode;\n };\n\ninterface AdminPortalSsoConnectionContextValue {\n connectionStatus: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}\n\nconst AdminPortalSsoConnectionContext =\n React.createContext<AdminPortalSsoConnectionContextValue | null>(null);\nAdminPortalSsoConnectionContext.displayName = \"AdminPortalSsoConnectionContext\";\n\nfunction useAdminPortalSsoConnectionContext() {\n const context = React.useContext(AdminPortalSsoConnectionContext);\n if (!context) {\n throw new Error(\n \"useAdminPortalSsoConnectionContext must be used within a AdminPortalSsoConnectionContext provider\",\n );\n }\n return context;\n}\n\nconst AdminPortalSsoConnection: React.FC<AdminPortalSsoConnectionProps> = ({\n connectionStatus,\n identityProvider,\n expiryDate,\n lastSession,\n adminPortalOpenButton,\n ...domProps\n}) => {\n return (\n <CardList.Root size=\"2\" {...getWidgetRootDomProps(\"resolved\", domProps)}>\n <AdminPortalSsoConnectionContext.Provider value={{ connectionStatus }}>\n <CardList.Item>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n {(() => {\n if (connectionStatus === \"NotConfigured\") {\n return (\n <>\n <Text size=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"You haven't set up Single Sign-On yet.\"\n id=\"KDs1Ib\"\n description=\"Empty state message when SSO is not configured\"\n />\n </Text>\n {adminPortalOpenButton}\n </>\n );\n }\n\n return (\n <>\n <SessionInfo\n connectionStatus={connectionStatus}\n identityProvider={identityProvider}\n lastSession={lastSession}\n />\n <Flex gap=\"5\" align=\"center\">\n <SsoStatus status={connectionStatus} />\n {adminPortalOpenButton}\n </Flex>\n </>\n );\n })()}\n </Flex>\n </CardList.Item>\n {connectionStatus === \"Expired\" && (\n <CardList.Item>\n <Flex align=\"start\" gap=\"2\">\n <Box asChild mt=\"2px\" flexShrink=\"0\">\n <InfoCircledIcon color=\"gray\" />\n </Box>\n {(() => {\n if (connectionStatus === \"Expired\") {\n return (\n <Text size=\"2\" color=\"gray\">\n {(() => {\n if (!expiryDate) {\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate has expired. Users won't be able to sign-in to the application until the certificate is renewed.\"\n id=\"5tX19v\"\n description=\"Error message when SAML certificate has expired (no date)\"\n />\n );\n }\n\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate expired on {expiryDate}. Users won't be able to sign-in to the application until the certificate is renewed.\"\n id=\"vmTW/h\"\n description=\"Error message when SAML certificate has expired with date\"\n values={{\n expiryDate: (\n <Text weight=\"bold\" as=\"span\">\n {expiryDate?.toLocaleString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n })}\n </Text>\n ),\n }}\n />\n );\n })()}\n </Text>\n );\n }\n\n if (connectionStatus === \"Expiring\") {\n return (\n <Text size=\"2\" color=\"gray\">\n {(() => {\n if (!expiryDate) {\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate is expiring soon. When expired, users won't be able to sign-in.\"\n id=\"3dgA5f\"\n description=\"Warning message when SAML certificate is expiring soon (no date)\"\n />\n );\n }\n\n return (\n <Translation\n defaultMessage=\"The SAML response signing certificate will expire on {expiryDate}. When expired, users won't be able to sign-in.\"\n id=\"xT9Exy\"\n description=\"Warning message when SAML certificate is expiring with date\"\n values={{\n expiryDate: (\n <Text weight=\"bold\" as=\"span\">\n {expiryDate?.toLocaleString(\"en-US\", {\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n })}\n </Text>\n ),\n }}\n />\n );\n })()}\n </Text>\n );\n }\n })()}\n </Flex>\n </CardList.Item>\n )}\n </AdminPortalSsoConnectionContext.Provider>\n </CardList.Root>\n );\n};\n\nfunction SsoStatus({\n status,\n}: {\n status: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}) {\n if (status === \"NotConfigured\") {\n return null;\n }\n\n if (status === \"Inactive\") {\n return (\n <Status state=\"waiting\">\n <Translation\n defaultMessage=\"Setup in progress\"\n id=\"MQR5mR\"\n description=\"Status label when SSO setup is incomplete\"\n />\n </Status>\n );\n }\n\n if (status === \"Expired\") {\n return (\n <Status state=\"error\">\n <Translation\n defaultMessage=\"Requires action\"\n id=\"Yo2vHC\"\n description=\"Status label when SSO requires attention\"\n />\n </Status>\n );\n }\n\n if (status === \"Active\" || status === \"Expiring\") {\n return (\n <Status state=\"success\">\n <Translation\n defaultMessage=\"Connected\"\n id=\"8S+Cyw\"\n description=\"Status label when SSO is active\"\n />\n </Status>\n );\n }\n\n return unreachable(status);\n}\n\nfunction SessionInfo({\n identityProvider,\n lastSession,\n connectionStatus,\n}: {\n identityProvider: IdentityProvider;\n lastSession: Date | null;\n connectionStatus: AdminPortalSsoConnectionStatusProps[\"connectionStatus\"];\n}) {\n const relativeTime = useComparativeReadableDate({\n date:\n connectionStatus === \"NotConfigured\" || connectionStatus === \"Inactive\"\n ? null\n : lastSession,\n });\n\n return (\n <Flex gap=\"4\" align=\"center\">\n <IconPanel color=\"panel\">\n <ProviderIcon provider={identityProvider} size=\"2\" />\n </IconPanel>\n {lastSession ? (\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {getIdentityProviderName(identityProvider)}\n </Text>\n {relativeTime ? (\n <Text color=\"gray\" size=\"2\">\n <Translation\n defaultMessage=\"Last session {relativeTime}\"\n id=\"yN72Gc\"\n description=\"Label showing when the last SSO session occurred\"\n values={{ relativeTime }}\n />\n </Text>\n ) : null}\n </Flex>\n ) : (\n <Text size=\"2\" weight=\"bold\">\n {getIdentityProviderName(identityProvider)}\n </Text>\n )}\n </Flex>\n );\n}\n\nfunction AdminPortalOpenButton({\n isPending,\n initConfig,\n}: {\n isPending: boolean;\n initConfig: () => void;\n}) {\n const { connectionStatus } = useAdminPortalSsoConnectionContext();\n const label = (() => {\n switch (connectionStatus) {\n case \"NotConfigured\":\n return (\n <Translation\n defaultMessage=\"Set up SSO\"\n id=\"MtkNQO\"\n description=\"Button label to start SSO setup\"\n />\n );\n case \"Inactive\":\n return (\n <Translation\n defaultMessage=\"Continue setup\"\n id=\"HFxrbY\"\n description=\"Button label to continue incomplete SSO setup\"\n />\n );\n case \"Active\":\n case \"Expiring\":\n case \"Expired\":\n return (\n <Translation\n defaultMessage=\"Manage\"\n id=\"T1VKzP\"\n description=\"Button label to manage SSO settings\"\n />\n );\n default:\n return unreachable(connectionStatus);\n }\n })();\n\n return (\n <Button\n variant=\"secondary\"\n loading={isPending}\n disabled={isPending}\n onClick={initConfig}\n >\n {label} <ExternalLinkIcon aria-hidden />\n </Button>\n );\n}\n\ninterface AdminPortalSsoConnectionLoadingProps extends WidgetRootDomProps {}\n\nconst AdminPortalSsoConnectionLoading: React.FC<\n AdminPortalSsoConnectionLoadingProps\n> = (props) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"loading\", props)}>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n <Flex gap=\"4\" align=\"center\">\n <Skeleton>\n <IconPanel color=\"panel\">\n <ProviderIcon provider=\"okta\" size=\"2\" />\n </IconPanel>\n </Skeleton>\n <Flex direction=\"column\" gap=\"1\" my=\"-4px\">\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"1\">Okta</Text>\n </Skeleton>\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"1\">Last session 10 minutes ago</Text>\n </Skeleton>\n </Flex>\n </Flex>\n <Flex gap=\"5\" align=\"center\">\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Status state=\"error\">Requires action</Status>\n </Skeleton>\n <Skeleton>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Button variant=\"secondary\">\n Manage <ExternalLinkIcon aria-hidden />\n </Button>\n </Skeleton>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\ninterface AdminPortalSsoConnectionErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst AdminPortalSsoConnectionError: React.FC<\n AdminPortalSsoConnectionErrorProps\n> = ({ error, ...domProps }) => {\n const translate = useTranslation();\n React.useEffect(() => {\n console.error(error);\n }, [error]);\n\n const { heading, message } = getErrorMessage(error, translate);\n\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"error\", domProps)}>\n <Flex direction=\"row\" justify=\"between\" align=\"center\" gap=\"2\">\n <Flex gap=\"4\" align=\"center\">\n <Flex\n align=\"center\"\n justify=\"center\"\n width=\"24px\"\n height=\"24px\"\n style={{\n borderRadius: \"9999px\",\n backgroundColor: \"var(--red-a4)\",\n color: \"var(--red-a11)\",\n flexShrink: 0,\n }}\n >\n <Cross2Icon width=\"18px\" height=\"18px\" />\n </Flex>\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {heading}\n </Text>\n <Text size=\"2\" color=\"gray\">\n {message}\n </Text>\n </Flex>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"admin-portal-sso-connection\",\n widgetState: state,\n });\n}\n\nexport type {\n AdminPortalSsoConnectionProps,\n AdminPortalSsoConnectionLoadingProps,\n AdminPortalSsoConnectionErrorProps,\n};\nexport {\n AdminPortalSsoConnection,\n AdminPortalSsoConnectionLoading,\n AdminPortalSsoConnectionError,\n /** @internal */\n AdminPortalOpenButton,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
type ApiKeyExpirationOption = "never" | "1_hour" | "24_hours" | "3_days" | "7_days" | "current";
|
|
4
4
|
type ApiKeyExpirationPreset = Exclude<ApiKeyExpirationOption, "current">;
|
|
5
|
-
declare function getApiKeyExpiresAt(value: ApiKeyExpirationOption): string | null | undefined;
|
|
5
|
+
declare function getApiKeyExpiresAt(now: Date, value: ApiKeyExpirationOption): string | null | undefined;
|
|
6
6
|
declare function getApiKeyExpirationSelectValue(expiresAt?: string | null): ApiKeyExpirationOption;
|
|
7
7
|
declare function ApiKeyExpirationSelectField({ defaultValue, value, onValueChange, currentExpiresAt, }: {
|
|
8
8
|
defaultValue?: ApiKeyExpirationOption;
|
|
@@ -13,8 +13,7 @@ const EXPIRATION_OPTIONS = [
|
|
|
13
13
|
"3_days",
|
|
14
14
|
"7_days"
|
|
15
15
|
];
|
|
16
|
-
function getApiKeyExpiresAt(value) {
|
|
17
|
-
const now = /* @__PURE__ */ new Date();
|
|
16
|
+
function getApiKeyExpiresAt(now, value) {
|
|
18
17
|
switch (value) {
|
|
19
18
|
case "never":
|
|
20
19
|
return null;
|
|
@@ -215,7 +214,10 @@ function EditApiKeyExpirationDialogContent({
|
|
|
215
214
|
disabled: !canSave,
|
|
216
215
|
loading: isPending || isSuccess,
|
|
217
216
|
onClick: () => {
|
|
218
|
-
const nextExpiresAt = getApiKeyExpiresAt(
|
|
217
|
+
const nextExpiresAt = getApiKeyExpiresAt(
|
|
218
|
+
/* @__PURE__ */ new Date(),
|
|
219
|
+
selectedExpiration
|
|
220
|
+
);
|
|
219
221
|
if (nextExpiresAt !== void 0 && selectedExpiration !== "current") {
|
|
220
222
|
onSave(nextExpiresAt);
|
|
221
223
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/api-keys/api-key-expiration.tsx"],"sourcesContent":["import { Callout, Flex } from \"@radix-ui/themes\";\nimport { ExclamationTriangleIcon } from \"@radix-ui/react-icons\";\nimport * as React from \"react\";\nimport { Button, Dialog, Label, Select } from \"../elements.js\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useLocale } from \"../i18n/use-locale.js\";\nimport { useIsHydrated } from \"../use-is-hydrated.js\";\n\nexport type ApiKeyExpirationOption =\n | \"never\"\n | \"1_hour\"\n | \"24_hours\"\n | \"3_days\"\n | \"7_days\"\n | \"current\";\n\nexport type ApiKeyExpirationPreset = Exclude<ApiKeyExpirationOption, \"current\">;\n\nconst EXPIRATION_OPTIONS = [\n \"never\",\n \"1_hour\",\n \"24_hours\",\n \"3_days\",\n \"7_days\",\n] satisfies ApiKeyExpirationPreset[];\n\nexport function getApiKeyExpiresAt(\n value: ApiKeyExpirationOption,\n): string | null | undefined {\n const now = new Date();\n\n switch (value) {\n case \"never\":\n return null;\n case \"1_hour\":\n return new Date(now.getTime() + 60 * 60 * 1000).toISOString();\n case \"24_hours\":\n return new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString();\n case \"3_days\":\n return new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000).toISOString();\n case \"7_days\":\n return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString();\n case \"current\":\n return undefined;\n }\n}\n\nexport function getApiKeyExpirationSelectValue(\n expiresAt?: string | null,\n): ApiKeyExpirationOption {\n if (!expiresAt) {\n return \"never\";\n }\n\n return \"current\";\n}\n\nexport function ApiKeyExpirationSelectField({\n defaultValue = \"never\",\n value,\n onValueChange,\n currentExpiresAt,\n}: {\n defaultValue?: ApiKeyExpirationOption;\n value?: ApiKeyExpirationOption;\n onValueChange?: (value: ApiKeyExpirationOption) => void;\n currentExpiresAt?: string | null;\n}) {\n return (\n <Flex direction=\"column\" align=\"stretch\" gap=\"2\">\n <Label>\n <Translation\n defaultMessage=\"Expiration\"\n id=\"pwfOwu\"\n description=\"Label for API key expiration field\"\n />\n </Label>\n <Select.Root\n name=\"expiration\"\n defaultValue={defaultValue}\n value={value}\n onValueChange={(nextValue) =>\n onValueChange?.(nextValue as ApiKeyExpirationOption)\n }\n >\n <Select.Trigger />\n <Select.Content>\n {currentExpiresAt ? (\n <Select.Item value=\"current\">\n <Translation\n defaultMessage=\"Current Expiration - {expiresAt}\"\n id=\"9HtsNU\"\n description=\"Option label for the current API key expiration\"\n values={{\n expiresAt: (\n <ApiKeyExpiresOn expiresAt={currentExpiresAt} includeTime />\n ),\n }}\n />\n </Select.Item>\n ) : null}\n {EXPIRATION_OPTIONS.map((option) => (\n <Select.Item key={option} value={option}>\n <ExpirationOptionLabel option={option} />\n </Select.Item>\n ))}\n </Select.Content>\n </Select.Root>\n </Flex>\n );\n}\n\nexport function ApiKeyExpiresOn({\n expiresAt,\n includeTime = false,\n}: {\n expiresAt?: string | null;\n includeTime?: boolean;\n}) {\n const isHydrated = useIsHydrated();\n const locale = useLocale();\n\n if (!expiresAt) {\n return <NeverApiKeyExpirationLabel />;\n }\n\n const expiresOn = new Date(expiresAt);\n const options = {\n timeZone: isHydrated ? undefined : \"UTC\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n ...(includeTime ? { hour: \"numeric\", minute: \"2-digit\" } : {}),\n } satisfies Intl.DateTimeFormatOptions;\n\n return (\n <>\n {includeTime\n ? expiresOn.toLocaleString(locale, options)\n : expiresOn.toLocaleDateString(locale, options)}\n </>\n );\n}\n\nexport function ApiKeyTimestamp({ timestamp }: { timestamp: string }) {\n const isHydrated = useIsHydrated();\n const locale = useLocale();\n const date = new Date(timestamp);\n\n return (\n <>\n {date.toLocaleString(locale, {\n timeZone: isHydrated ? undefined : \"UTC\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n })}\n </>\n );\n}\n\nexport function EditApiKeyExpirationDialog({\n apiKeyName,\n expiresAt,\n error,\n isPending,\n isSuccess,\n open,\n onOpenChange,\n onReset,\n onSave,\n}: {\n apiKeyName: string;\n expiresAt?: string | null;\n error: unknown;\n isPending: boolean;\n isSuccess: boolean;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onReset: () => void;\n onSave: (expiresAt: string | null) => void;\n}) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n {open ? (\n <EditApiKeyExpirationDialogContent\n apiKeyName={apiKeyName}\n expiresAt={expiresAt}\n error={error}\n isPending={isPending}\n isSuccess={isSuccess}\n onReset={onReset}\n onSave={onSave}\n />\n ) : null}\n </Dialog.Root>\n );\n}\n\nfunction EditApiKeyExpirationDialogContent({\n apiKeyName,\n expiresAt,\n error,\n isPending,\n isSuccess,\n onReset,\n onSave,\n}: {\n apiKeyName: string;\n expiresAt?: string | null;\n error: unknown;\n isPending: boolean;\n isSuccess: boolean;\n onReset: () => void;\n onSave: (expiresAt: string | null) => void;\n}) {\n const selectedInitialValue = getApiKeyExpirationSelectValue(expiresAt);\n const [selectedExpiration, setSelectedExpiration] =\n React.useState<ApiKeyExpirationOption>(selectedInitialValue);\n\n const isCurrentExpiration = selectedExpiration === \"current\";\n const isUnchanged = selectedExpiration === selectedInitialValue;\n const canSave =\n !isCurrentExpiration && !isUnchanged && !isPending && !isSuccess;\n\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title>\n <Translation\n defaultMessage=\"Edit secret key expiration\"\n id=\"pIxXuW\"\n description=\"Dialog title for editing API key expiration\"\n />\n </Dialog.Title>\n <Dialog.Description>\n {isCurrentExpiration ? (\n <Translation\n defaultMessage=\"Confirm API key expiration. Once this key expires, you can no longer use it.\"\n id=\"48Q0bX\"\n description=\"Description for confirming the current API key expiration\"\n />\n ) : (\n <Translation\n defaultMessage=\"Set API Key expiration for {name}. Once this key expires, you can no longer use it.\"\n id=\"aLj8zz\"\n description=\"Description for setting API key expiration\"\n values={{ name: apiKeyName }}\n />\n )}\n </Dialog.Description>\n <Flex direction=\"column\" align=\"stretch\" gap=\"4\" mt=\"5\">\n {error ? (\n <Callout.Root color=\"red\" role=\"alert\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"An error occurred while updating the API key expiration.\"\n id=\"ge66Rv\"\n description=\"Error message when API key expiration update fails\"\n />\n </Callout.Text>\n </Callout.Root>\n ) : null}\n <ApiKeyExpirationSelectField\n currentExpiresAt={expiresAt}\n value={selectedExpiration}\n onValueChange={(nextExpiration) => {\n if (!isPending) {\n onReset();\n }\n setSelectedExpiration(nextExpiration);\n }}\n />\n </Flex>\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <Button variant=\"secondary\" disabled={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Cancel\"\n id=\"I9XnRG\"\n description=\"Button to cancel API key expiration editing\"\n />\n </Button>\n </Dialog.Close>\n <Button\n disabled={!canSave}\n loading={isPending || isSuccess}\n onClick={() => {\n const nextExpiresAt = getApiKeyExpiresAt(selectedExpiration);\n if (\n nextExpiresAt !== undefined &&\n selectedExpiration !== \"current\"\n ) {\n onSave(nextExpiresAt);\n }\n }}\n >\n <Translation\n defaultMessage=\"Save changes\"\n id=\"ThFw5g\"\n description=\"Button to save API key expiration changes\"\n />\n </Button>\n </Flex>\n </Dialog.Content>\n );\n}\n\nfunction ExpirationOptionLabel({\n option,\n}: {\n option: Exclude<ApiKeyExpirationOption, \"current\">;\n}) {\n switch (option) {\n case \"never\":\n return <NeverApiKeyExpirationLabel />;\n case \"1_hour\":\n return (\n <Translation\n defaultMessage=\"in 1 hour\"\n id=\"+GR+6F\"\n description=\"Option to expire an API key in one hour\"\n />\n );\n case \"24_hours\":\n return (\n <Translation\n defaultMessage=\"in 24 hours\"\n id=\"UPQwj+\"\n description=\"Option to expire an API key in twenty-four hours\"\n />\n );\n case \"3_days\":\n return (\n <Translation\n defaultMessage=\"in 3 days\"\n id=\"elvZ6g\"\n description=\"Option to expire an API key in three days\"\n />\n );\n case \"7_days\":\n return (\n <Translation\n defaultMessage=\"in 7 days\"\n id=\"Dzu4Fy\"\n description=\"Option to expire an API key in seven days\"\n />\n );\n }\n}\n\nfunction NeverApiKeyExpirationLabel() {\n return (\n <Translation\n defaultMessage=\"Never\"\n id=\"T7DoYo\"\n description=\"Text indicating API key expiration is not set\"\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/api-keys/api-key-expiration.tsx"],"sourcesContent":["import { Callout, Flex } from \"@radix-ui/themes\";\nimport { ExclamationTriangleIcon } from \"@radix-ui/react-icons\";\nimport * as React from \"react\";\nimport { Button, Dialog, Label, Select } from \"../elements.js\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useLocale } from \"../i18n/use-locale.js\";\nimport { useIsHydrated } from \"../use-is-hydrated.js\";\n\nexport type ApiKeyExpirationOption =\n | \"never\"\n | \"1_hour\"\n | \"24_hours\"\n | \"3_days\"\n | \"7_days\"\n | \"current\";\n\nexport type ApiKeyExpirationPreset = Exclude<ApiKeyExpirationOption, \"current\">;\n\nconst EXPIRATION_OPTIONS = [\n \"never\",\n \"1_hour\",\n \"24_hours\",\n \"3_days\",\n \"7_days\",\n] satisfies ApiKeyExpirationPreset[];\n\nexport function getApiKeyExpiresAt(\n now: Date,\n value: ApiKeyExpirationOption,\n): string | null | undefined {\n switch (value) {\n case \"never\":\n return null;\n case \"1_hour\":\n return new Date(now.getTime() + 60 * 60 * 1000).toISOString();\n case \"24_hours\":\n return new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString();\n case \"3_days\":\n return new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000).toISOString();\n case \"7_days\":\n return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString();\n case \"current\":\n return undefined;\n }\n}\n\nexport function getApiKeyExpirationSelectValue(\n expiresAt?: string | null,\n): ApiKeyExpirationOption {\n if (!expiresAt) {\n return \"never\";\n }\n\n return \"current\";\n}\n\nexport function ApiKeyExpirationSelectField({\n defaultValue = \"never\",\n value,\n onValueChange,\n currentExpiresAt,\n}: {\n defaultValue?: ApiKeyExpirationOption;\n value?: ApiKeyExpirationOption;\n onValueChange?: (value: ApiKeyExpirationOption) => void;\n currentExpiresAt?: string | null;\n}) {\n return (\n <Flex direction=\"column\" align=\"stretch\" gap=\"2\">\n <Label>\n <Translation\n defaultMessage=\"Expiration\"\n id=\"pwfOwu\"\n description=\"Label for API key expiration field\"\n />\n </Label>\n <Select.Root\n name=\"expiration\"\n defaultValue={defaultValue}\n value={value}\n onValueChange={(nextValue) =>\n onValueChange?.(nextValue as ApiKeyExpirationOption)\n }\n >\n <Select.Trigger />\n <Select.Content>\n {currentExpiresAt ? (\n <Select.Item value=\"current\">\n <Translation\n defaultMessage=\"Current Expiration - {expiresAt}\"\n id=\"9HtsNU\"\n description=\"Option label for the current API key expiration\"\n values={{\n expiresAt: (\n <ApiKeyExpiresOn expiresAt={currentExpiresAt} includeTime />\n ),\n }}\n />\n </Select.Item>\n ) : null}\n {EXPIRATION_OPTIONS.map((option) => (\n <Select.Item key={option} value={option}>\n <ExpirationOptionLabel option={option} />\n </Select.Item>\n ))}\n </Select.Content>\n </Select.Root>\n </Flex>\n );\n}\n\nexport function ApiKeyExpiresOn({\n expiresAt,\n includeTime = false,\n}: {\n expiresAt?: string | null;\n includeTime?: boolean;\n}) {\n const isHydrated = useIsHydrated();\n const locale = useLocale();\n\n if (!expiresAt) {\n return <NeverApiKeyExpirationLabel />;\n }\n\n const expiresOn = new Date(expiresAt);\n const options = {\n timeZone: isHydrated ? undefined : \"UTC\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n ...(includeTime ? { hour: \"numeric\", minute: \"2-digit\" } : {}),\n } satisfies Intl.DateTimeFormatOptions;\n\n return (\n <>\n {includeTime\n ? expiresOn.toLocaleString(locale, options)\n : expiresOn.toLocaleDateString(locale, options)}\n </>\n );\n}\n\nexport function ApiKeyTimestamp({ timestamp }: { timestamp: string }) {\n const isHydrated = useIsHydrated();\n const locale = useLocale();\n const date = new Date(timestamp);\n\n return (\n <>\n {date.toLocaleString(locale, {\n timeZone: isHydrated ? undefined : \"UTC\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n })}\n </>\n );\n}\n\nexport function EditApiKeyExpirationDialog({\n apiKeyName,\n expiresAt,\n error,\n isPending,\n isSuccess,\n open,\n onOpenChange,\n onReset,\n onSave,\n}: {\n apiKeyName: string;\n expiresAt?: string | null;\n error: unknown;\n isPending: boolean;\n isSuccess: boolean;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onReset: () => void;\n onSave: (expiresAt: string | null) => void;\n}) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n {open ? (\n <EditApiKeyExpirationDialogContent\n apiKeyName={apiKeyName}\n expiresAt={expiresAt}\n error={error}\n isPending={isPending}\n isSuccess={isSuccess}\n onReset={onReset}\n onSave={onSave}\n />\n ) : null}\n </Dialog.Root>\n );\n}\n\nfunction EditApiKeyExpirationDialogContent({\n apiKeyName,\n expiresAt,\n error,\n isPending,\n isSuccess,\n onReset,\n onSave,\n}: {\n apiKeyName: string;\n expiresAt?: string | null;\n error: unknown;\n isPending: boolean;\n isSuccess: boolean;\n onReset: () => void;\n onSave: (expiresAt: string | null) => void;\n}) {\n const selectedInitialValue = getApiKeyExpirationSelectValue(expiresAt);\n const [selectedExpiration, setSelectedExpiration] =\n React.useState<ApiKeyExpirationOption>(selectedInitialValue);\n\n const isCurrentExpiration = selectedExpiration === \"current\";\n const isUnchanged = selectedExpiration === selectedInitialValue;\n const canSave =\n !isCurrentExpiration && !isUnchanged && !isPending && !isSuccess;\n\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title>\n <Translation\n defaultMessage=\"Edit secret key expiration\"\n id=\"pIxXuW\"\n description=\"Dialog title for editing API key expiration\"\n />\n </Dialog.Title>\n <Dialog.Description>\n {isCurrentExpiration ? (\n <Translation\n defaultMessage=\"Confirm API key expiration. Once this key expires, you can no longer use it.\"\n id=\"48Q0bX\"\n description=\"Description for confirming the current API key expiration\"\n />\n ) : (\n <Translation\n defaultMessage=\"Set API Key expiration for {name}. Once this key expires, you can no longer use it.\"\n id=\"aLj8zz\"\n description=\"Description for setting API key expiration\"\n values={{ name: apiKeyName }}\n />\n )}\n </Dialog.Description>\n <Flex direction=\"column\" align=\"stretch\" gap=\"4\" mt=\"5\">\n {error ? (\n <Callout.Root color=\"red\" role=\"alert\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"An error occurred while updating the API key expiration.\"\n id=\"ge66Rv\"\n description=\"Error message when API key expiration update fails\"\n />\n </Callout.Text>\n </Callout.Root>\n ) : null}\n <ApiKeyExpirationSelectField\n currentExpiresAt={expiresAt}\n value={selectedExpiration}\n onValueChange={(nextExpiration) => {\n if (!isPending) {\n onReset();\n }\n setSelectedExpiration(nextExpiration);\n }}\n />\n </Flex>\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <Button variant=\"secondary\" disabled={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Cancel\"\n id=\"I9XnRG\"\n description=\"Button to cancel API key expiration editing\"\n />\n </Button>\n </Dialog.Close>\n <Button\n disabled={!canSave}\n loading={isPending || isSuccess}\n onClick={() => {\n const nextExpiresAt = getApiKeyExpiresAt(\n new Date(),\n selectedExpiration,\n );\n if (\n nextExpiresAt !== undefined &&\n selectedExpiration !== \"current\"\n ) {\n onSave(nextExpiresAt);\n }\n }}\n >\n <Translation\n defaultMessage=\"Save changes\"\n id=\"ThFw5g\"\n description=\"Button to save API key expiration changes\"\n />\n </Button>\n </Flex>\n </Dialog.Content>\n );\n}\n\nfunction ExpirationOptionLabel({\n option,\n}: {\n option: Exclude<ApiKeyExpirationOption, \"current\">;\n}) {\n switch (option) {\n case \"never\":\n return <NeverApiKeyExpirationLabel />;\n case \"1_hour\":\n return (\n <Translation\n defaultMessage=\"in 1 hour\"\n id=\"+GR+6F\"\n description=\"Option to expire an API key in one hour\"\n />\n );\n case \"24_hours\":\n return (\n <Translation\n defaultMessage=\"in 24 hours\"\n id=\"UPQwj+\"\n description=\"Option to expire an API key in twenty-four hours\"\n />\n );\n case \"3_days\":\n return (\n <Translation\n defaultMessage=\"in 3 days\"\n id=\"elvZ6g\"\n description=\"Option to expire an API key in three days\"\n />\n );\n case \"7_days\":\n return (\n <Translation\n defaultMessage=\"in 7 days\"\n id=\"Dzu4Fy\"\n description=\"Option to expire an API key in seven days\"\n />\n );\n }\n}\n\nfunction NeverApiKeyExpirationLabel() {\n return (\n <Translation\n defaultMessage=\"Never\"\n id=\"T7DoYo\"\n description=\"Text indicating API key expiration is not set\"\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -86,8 +86,10 @@ function CreateApiKeyForm() {
|
|
|
86
86
|
Form.Root,
|
|
87
87
|
{
|
|
88
88
|
onSubmit: (e) => {
|
|
89
|
-
if (isPending) return;
|
|
90
89
|
e.preventDefault();
|
|
90
|
+
if (isPending) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
91
93
|
const data = new FormData(e.currentTarget);
|
|
92
94
|
const isValid = validate(data);
|
|
93
95
|
if (!isValid) {
|
|
@@ -99,6 +101,7 @@ function CreateApiKeyForm() {
|
|
|
99
101
|
name: data.get("name")?.toString() ?? "",
|
|
100
102
|
permissions: data.getAll("permission").map(String),
|
|
101
103
|
expiresAt: getApiKeyExpiresAt(
|
|
104
|
+
/* @__PURE__ */ new Date(),
|
|
102
105
|
data.get("expiration")?.toString() ?? "never"
|
|
103
106
|
) ?? null
|
|
104
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/api-keys/create-api-key.tsx"],"sourcesContent":["import {\n Box,\n Checkbox,\n Flex,\n ScrollArea,\n Text,\n Callout,\n} from \"@radix-ui/themes\";\nimport {\n Dialog,\n Label,\n Button,\n TextField,\n TextFieldSlot,\n} from \"../elements.js\";\nimport * as Form from \"../form.js\";\nimport {\n ExclamationTriangleIcon,\n InfoCircledIcon,\n MagnifyingGlassIcon,\n} from \"@radix-ui/react-icons\";\nimport { CopyButton, CopyIconButton } from \"../copy-button.js\";\nimport * as React from \"react\";\nimport type { ListOrganizationApiKeyPermission } from \"../api/types.js\";\nimport {\n useCreateApiKey,\n useListOrganizationApiKeyPermissions,\n} from \"../api/api-key.js\";\nimport { useApiKeysContext } from \"./api-keys-context.js\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useTranslation } from \"../i18n/use-translation.js\";\nimport {\n ApiKeyExpirationSelectField,\n getApiKeyExpiresAt,\n type ApiKeyExpirationOption,\n} from \"./api-key-expiration.js\";\n\nexport function CreateApiKeyDialog() {\n const {\n state: { createDialogOpen, createdApiKey },\n dispatch,\n } = useApiKeysContext();\n\n return (\n <Dialog.Root\n open={createDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n dispatch({ type: \"CLOSE_CREATE_DIALOG\" });\n }\n }}\n >\n {createdApiKey ? (\n <SaveApiKeyContent apiKey={createdApiKey.value} />\n ) : (\n <CreateApiKeyForm\n // always remount the form when the dialog is opened to reset the validation errors\n key={`${createDialogOpen}`}\n />\n )}\n </Dialog.Root>\n );\n}\n\nfunction CreateApiKeyForm() {\n const { dispatch } = useApiKeysContext();\n const translate = useTranslation();\n const [errors, setErrors] = React.useState<{\n name: boolean;\n }>({ name: false });\n\n const validate = (formData: FormData) => {\n const name = formData.get(\"name\")?.toString() ?? \"\";\n const errors = { name: !name.trim().length };\n setErrors(errors);\n const isValid = Object.values(errors).every((error) => error === false);\n return isValid;\n };\n\n const {\n mutate: createOrganizationApiKey,\n error,\n isPending,\n isSuccess,\n } = useCreateApiKey();\n const permissions = useListOrganizationApiKeyPermissions({ limit: 100 });\n\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title size=\"4\" weight={\"bold\"}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"eUFRNS\"\n description=\"Dialog title for creating a new API key\"\n />\n </Dialog.Title>\n <Form.Root\n onSubmit={(e) => {\n if (isPending) return;\n e.preventDefault();\n const data = new FormData(e.currentTarget);\n const isValid = validate(data);\n if (!isValid) {\n return;\n }\n\n createOrganizationApiKey(\n {\n data: {\n name: data.get(\"name\")?.toString() ?? \"\",\n permissions: data.getAll(\"permission\").map(String),\n expiresAt:\n getApiKeyExpiresAt(\n (data.get(\"expiration\")?.toString() ??\n \"never\") as ApiKeyExpirationOption,\n ) ?? null,\n },\n },\n {\n onSuccess: (data) => {\n dispatch({ type: \"RESET_PAGINATION\" });\n dispatch({ type: \"SET_CREATED_API_KEY\", apiKey: data });\n },\n },\n );\n }}\n >\n <Flex direction=\"column\" align=\"stretch\" gap=\"4\">\n {error && (\n <Callout.Root color=\"red\" role=\"alert\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"An error occurred while creating the API key.\"\n id=\"0LyBVd\"\n description=\"Error message when API key creation fails\"\n />\n </Callout.Text>\n </Callout.Root>\n )}\n <Form.Field name=\"name\" asChild>\n <Flex direction=\"column\" align=\"stretch\" gap=\"2\">\n <Form.Label asChild>\n <Label>\n <Translation\n defaultMessage=\"Name\"\n id=\"v0VmRy\"\n description=\"Label for API key name input field\"\n />\n </Label>\n </Form.Label>\n <Form.Control asChild>\n <TextField\n data-1p-ignore\n autoComplete=\"off\"\n placeholder={translate({\n defaultMessage: \"A descriptive name for the API key\",\n id: \"moyvr4\",\n description: \"Placeholder for API key name input\",\n })}\n />\n </Form.Control>\n {errors.name && (\n <Form.Message asChild>\n <Text size=\"2\" color=\"red\">\n <Translation\n defaultMessage=\"The name is required\"\n id=\"oF9d2V\"\n description=\"Validation error when API key name is empty\"\n />\n </Text>\n </Form.Message>\n )}\n </Flex>\n </Form.Field>\n <ApiKeyExpirationSelectField />\n {permissions.isError ? (\n <Callout.Root color=\"amber\" role=\"status\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"Permissions are unavailable right now. You can still create an API key without selecting permissions.\"\n id=\"Pw7VUu\"\n description=\"Warning message when API key permissions fail to load\"\n />\n </Callout.Text>\n </Callout.Root>\n ) : null}\n {permissions.isSuccess && permissions.data.data.length > 0 && (\n <PermissionsField permissions={permissions.data.data} />\n )}\n </Flex>\n\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <Button variant=\"secondary\" disabled={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Cancel\"\n id=\"AyVAAW\"\n description=\"Button to cancel API key creation\"\n />\n </Button>\n </Dialog.Close>\n <Button type=\"submit\" loading={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"d4BNWL\"\n description=\"Button to submit and create the API key\"\n />\n </Button>\n </Flex>\n </Form.Root>\n </Dialog.Content>\n );\n}\n\nfunction SaveApiKeyContent({ apiKey }: { apiKey: string }) {\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title size=\"4\" weight={\"bold\"}>\n <Translation\n defaultMessage=\"Save your key\"\n id=\"wFFCij\"\n description=\"Dialog title prompting user to save their API key\"\n />\n </Dialog.Title>\n <Dialog.Description>\n <Translation\n defaultMessage=\"Please save this API key in a secure location. If you lose it, you'll need to generate a new one.\"\n id=\"sy+2x7\"\n description=\"Warning message about saving the API key\"\n />\n </Dialog.Description>\n <Callout.Root mt=\"5\" mb=\"4\">\n <Callout.Icon>\n <InfoCircledIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"You won't be able to access the key again. Please copy it now.\"\n id=\"EFAK/K\"\n description=\"Callout warning that the key won't be accessible again\"\n />\n </Callout.Text>\n </Callout.Root>\n <TextField value={apiKey} readOnly>\n <TextFieldSlot side=\"right\">\n <CopyIconButton value={apiKey} />\n </TextFieldSlot>\n </TextField>\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <CopyButton value={apiKey}>\n <Translation\n defaultMessage=\"Copy and close\"\n id=\"ViYtvQ\"\n description=\"Button to copy API key and close dialog\"\n />\n </CopyButton>\n </Dialog.Close>\n </Flex>\n </Dialog.Content>\n );\n}\n\nfunction Permission({\n checked,\n permission,\n onChange,\n}: {\n checked: boolean;\n permission: ListOrganizationApiKeyPermission;\n onChange: (isChecked: boolean) => void;\n}) {\n return (\n <label\n htmlFor={permission.slug}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"var(--space-2) var(--space-4)\",\n gap: \"var(--space-3)\",\n borderBottom: \"1px solid var(--gray-6)\",\n cursor: \"pointer\",\n }}\n >\n <Checkbox\n checked={checked}\n id={permission.slug}\n name=\"permission\"\n value={permission.slug}\n onCheckedChange={(e) => onChange(e === \"indeterminate\" ? false : e)}\n />\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {permission.name}\n </Text>\n <Text size=\"2\" color=\"gray\">\n {permission.description}\n </Text>\n </Flex>\n </label>\n );\n}\n\nfunction PermissionsField({\n permissions = [],\n}: {\n permissions: ListOrganizationApiKeyPermission[];\n}) {\n const [selectedPermissions, setSelectedPermissions] = React.useState<\n ListOrganizationApiKeyPermission[]\n >([]);\n const [filter, setFilter] = React.useState(\"\");\n const fieldRef = React.useRef<HTMLDivElement | null>(null);\n const translate = useTranslation();\n const filteredPermissions = React.useMemo(() => {\n return permissions.filter((p) => {\n return (\n !filter.trim().length ||\n p.description?.toLowerCase().match(filter.toLowerCase()) ||\n p.name.toLowerCase().match(filter.toLowerCase()) ||\n p.slug.toLowerCase().match(filter.toLowerCase())\n );\n });\n }, [permissions, filter]);\n\n React.useEffect(() => {\n const onResize = () => {\n if (fieldRef.current && filteredPermissions.length !== 0) {\n fieldRef.current.style.minHeight = \"unset\";\n }\n };\n window.addEventListener(\"resize\", onResize);\n return () => {\n window.removeEventListener(\"resize\", onResize);\n };\n }, [filteredPermissions.length]);\n\n return (\n <Form.Field name=\"permissions\" asChild>\n <Flex direction=\"column\" align=\"stretch\" gap=\"1\">\n <Form.Label asChild>\n <Label>\n <Translation\n defaultMessage=\"Permissions\"\n id=\"6YN1wO\"\n description=\"Label for API key permissions field\"\n />\n </Label>\n </Form.Label>\n <Text size=\"2\" mb=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"Only enable the minimum permissions required for your use case.\"\n id=\"q2VEtG\"\n description=\"Help text for API key permissions selection\"\n />\n </Text>\n <Flex\n ref={fieldRef}\n style={{\n borderRadius: \"var(--radius-3)\",\n borderColor: \"var(--gray-6)\",\n borderWidth: \"1px\",\n borderStyle: \"solid\",\n }}\n direction=\"column\"\n align=\"stretch\"\n >\n <TextField\n name=\"search\"\n value={filter}\n size=\"2\"\n onChange={(e) => {\n if (fieldRef.current) {\n fieldRef.current.style.minHeight =\n fieldRef.current.clientHeight + 2 + \"px\";\n }\n setFilter(e.target.value);\n }}\n style={{\n height: \"40px\",\n borderRadius: \"var(--radius-3) var(--radius-3) 0 0\",\n boxShadow: \"none\",\n }}\n placeholder={translate({\n defaultMessage: \"Search\",\n id: \"iyNcly\",\n description: \"Placeholder for permissions search input\",\n })}\n >\n <TextFieldSlot side=\"left\" px=\"3\">\n <MagnifyingGlassIcon aria-hidden=\"true\" height=\"16\" width=\"16\" />\n </TextFieldSlot>\n </TextField>\n {filteredPermissions.length > 0 ? (\n <>\n <ScrollArea\n scrollbars=\"vertical\"\n style={{\n flex: 1,\n maxHeight: \"calc(50vh - 150px)\",\n border: \"1px solid var(--gray-6)\",\n borderWidth: \"1px 0 0\",\n }}\n >\n {filteredPermissions.map((permission) => (\n <Permission\n checked={selectedPermissions.some(\n (sp) => sp.slug === permission.slug,\n )}\n key={permission.slug}\n permission={permission}\n onChange={(isChecked) =>\n setSelectedPermissions((prev) =>\n isChecked\n ? [...prev, permission]\n : prev.filter((sp) => sp.slug !== permission.slug),\n )\n }\n />\n ))}\n </ScrollArea>\n <Box\n px=\"4\"\n py=\"3\"\n mt={\"-1px\"}\n flexGrow=\"0\"\n style={{\n borderTop: \"0 solid var(--gray-6)\",\n borderWidth: \"1px 0 0\",\n }}\n >\n <SelectedPermissions\n total={permissions.length}\n selected={selectedPermissions.length}\n onToggle={() =>\n setSelectedPermissions((prev) =>\n prev.length === permissions.length ? [] : permissions,\n )\n }\n />\n </Box>\n </>\n ) : (\n <Flex\n gap=\"4\"\n pt=\"6\"\n pb=\"6\"\n style={{ borderTop: \"1px solid var(--gray-6)\" }}\n flexGrow=\"1\"\n justify=\"center\"\n align=\"center\"\n direction=\"column\"\n >\n <MagnifyingGlassIcon\n style={{\n color: \"var(--gray-9)\",\n width: \"var(--space-5)\",\n height: \"var(--space-5)\",\n }}\n />\n <Text size=\"2\" align=\"center\" mb=\"2\" wrap=\"balance\" weight=\"bold\">\n <Translation\n defaultMessage=\"No permissions match your search\"\n id=\"xjqT9f\"\n description=\"Empty state message when no permissions match search\"\n />\n </Text>\n <Button\n variant=\"secondary\"\n size=\"1\"\n onClick={() => setFilter(\"\")}\n >\n <Translation\n defaultMessage=\"Clear search\"\n id=\"YrMs63\"\n description=\"Button to clear permissions search\"\n />\n </Button>\n </Flex>\n )}\n </Flex>\n </Flex>\n </Form.Field>\n );\n}\n\nfunction SelectedPermissions({\n total,\n selected,\n onToggle,\n}: {\n total: number;\n selected: number;\n onToggle: () => void;\n}) {\n return (\n <Flex align=\"center\" gap=\"2\">\n <Text size=\"1\" style={{ lineHeight: 1, color: \"var(--gray-11)\" }}>\n {selected === 0 ? (\n <Translation\n defaultMessage=\"No permissions selected\"\n id=\"FqU9hv\"\n description=\"Message when no permissions are selected\"\n />\n ) : selected === 1 ? (\n <Translation\n defaultMessage=\"1 permission selected\"\n id=\"VouLgT\"\n description=\"Message showing count of selected permissions\"\n />\n ) : (\n <Translation\n defaultMessage=\"{count} permissions selected\"\n id=\"ZL90DI\"\n description=\"Message showing count of selected permissions\"\n values={{ count: selected }}\n />\n )}\n </Text>\n <Text\n size=\"1\"\n onClick={onToggle}\n style={{ cursor: \"pointer\", color: \"var(--accent-10)\" }}\n >\n {total === selected ? (\n <Translation\n defaultMessage=\"Deselect all\"\n id=\"63f7SY\"\n description=\"Button to deselect all permissions\"\n />\n ) : (\n <Translation\n defaultMessage=\"Select all\"\n id=\"onTqlA\"\n description=\"Button to select all permissions\"\n />\n )}\n </Text>\n </Flex>\n );\n}\n\nexport function CreateApiKeyButton() {\n const { dispatch } = useApiKeysContext();\n return (\n <Button onClick={() => dispatch({ type: \"OPEN_CREATE_DIALOG\" })}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"vq5724\"\n description=\"Button to open create API key dialog\"\n />\n </Button>\n );\n}\n"],"mappings":"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","names":["errors","error","data"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/api-keys/create-api-key.tsx"],"sourcesContent":["import {\n Box,\n Checkbox,\n Flex,\n ScrollArea,\n Text,\n Callout,\n} from \"@radix-ui/themes\";\nimport {\n Dialog,\n Label,\n Button,\n TextField,\n TextFieldSlot,\n} from \"../elements.js\";\nimport * as Form from \"../form.js\";\nimport {\n ExclamationTriangleIcon,\n InfoCircledIcon,\n MagnifyingGlassIcon,\n} from \"@radix-ui/react-icons\";\nimport { CopyButton, CopyIconButton } from \"../copy-button.js\";\nimport * as React from \"react\";\nimport type { ListOrganizationApiKeyPermission } from \"../api/types.js\";\nimport {\n useCreateApiKey,\n useListOrganizationApiKeyPermissions,\n} from \"../api/api-key.js\";\nimport { useApiKeysContext } from \"./api-keys-context.js\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useTranslation } from \"../i18n/use-translation.js\";\nimport {\n ApiKeyExpirationSelectField,\n getApiKeyExpiresAt,\n type ApiKeyExpirationOption,\n} from \"./api-key-expiration.js\";\n\nexport function CreateApiKeyDialog() {\n const {\n state: { createDialogOpen, createdApiKey },\n dispatch,\n } = useApiKeysContext();\n\n return (\n <Dialog.Root\n open={createDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n dispatch({ type: \"CLOSE_CREATE_DIALOG\" });\n }\n }}\n >\n {createdApiKey ? (\n <SaveApiKeyContent apiKey={createdApiKey.value} />\n ) : (\n <CreateApiKeyForm\n // always remount the form when the dialog is opened to reset the validation errors\n key={`${createDialogOpen}`}\n />\n )}\n </Dialog.Root>\n );\n}\n\nfunction CreateApiKeyForm() {\n const { dispatch } = useApiKeysContext();\n const translate = useTranslation();\n const [errors, setErrors] = React.useState<{\n name: boolean;\n }>({ name: false });\n\n const validate = (formData: FormData) => {\n const name = formData.get(\"name\")?.toString() ?? \"\";\n const errors = { name: !name.trim().length };\n setErrors(errors);\n const isValid = Object.values(errors).every((error) => error === false);\n return isValid;\n };\n\n const {\n mutate: createOrganizationApiKey,\n error,\n isPending,\n isSuccess,\n } = useCreateApiKey();\n const permissions = useListOrganizationApiKeyPermissions({ limit: 100 });\n\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title size=\"4\" weight={\"bold\"}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"eUFRNS\"\n description=\"Dialog title for creating a new API key\"\n />\n </Dialog.Title>\n <Form.Root\n onSubmit={(e) => {\n e.preventDefault();\n if (isPending) {\n return;\n }\n const data = new FormData(e.currentTarget);\n const isValid = validate(data);\n if (!isValid) {\n return;\n }\n\n createOrganizationApiKey(\n {\n data: {\n name: data.get(\"name\")?.toString() ?? \"\",\n permissions: data.getAll(\"permission\").map(String),\n expiresAt:\n getApiKeyExpiresAt(\n new Date(),\n (data.get(\"expiration\")?.toString() ??\n \"never\") as ApiKeyExpirationOption,\n ) ?? null,\n },\n },\n {\n onSuccess: (data) => {\n dispatch({ type: \"RESET_PAGINATION\" });\n dispatch({ type: \"SET_CREATED_API_KEY\", apiKey: data });\n },\n },\n );\n }}\n >\n <Flex direction=\"column\" align=\"stretch\" gap=\"4\">\n {error && (\n <Callout.Root color=\"red\" role=\"alert\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"An error occurred while creating the API key.\"\n id=\"0LyBVd\"\n description=\"Error message when API key creation fails\"\n />\n </Callout.Text>\n </Callout.Root>\n )}\n <Form.Field name=\"name\" asChild>\n <Flex direction=\"column\" align=\"stretch\" gap=\"2\">\n <Form.Label asChild>\n <Label>\n <Translation\n defaultMessage=\"Name\"\n id=\"v0VmRy\"\n description=\"Label for API key name input field\"\n />\n </Label>\n </Form.Label>\n <Form.Control asChild>\n <TextField\n data-1p-ignore\n autoComplete=\"off\"\n placeholder={translate({\n defaultMessage: \"A descriptive name for the API key\",\n id: \"moyvr4\",\n description: \"Placeholder for API key name input\",\n })}\n />\n </Form.Control>\n {errors.name && (\n <Form.Message asChild>\n <Text size=\"2\" color=\"red\">\n <Translation\n defaultMessage=\"The name is required\"\n id=\"oF9d2V\"\n description=\"Validation error when API key name is empty\"\n />\n </Text>\n </Form.Message>\n )}\n </Flex>\n </Form.Field>\n <ApiKeyExpirationSelectField />\n {permissions.isError ? (\n <Callout.Root color=\"amber\" role=\"status\">\n <Callout.Icon>\n <ExclamationTriangleIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"Permissions are unavailable right now. You can still create an API key without selecting permissions.\"\n id=\"Pw7VUu\"\n description=\"Warning message when API key permissions fail to load\"\n />\n </Callout.Text>\n </Callout.Root>\n ) : null}\n {permissions.isSuccess && permissions.data.data.length > 0 && (\n <PermissionsField permissions={permissions.data.data} />\n )}\n </Flex>\n\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <Button variant=\"secondary\" disabled={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Cancel\"\n id=\"AyVAAW\"\n description=\"Button to cancel API key creation\"\n />\n </Button>\n </Dialog.Close>\n <Button type=\"submit\" loading={isPending || isSuccess}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"d4BNWL\"\n description=\"Button to submit and create the API key\"\n />\n </Button>\n </Flex>\n </Form.Root>\n </Dialog.Content>\n );\n}\n\nfunction SaveApiKeyContent({ apiKey }: { apiKey: string }) {\n return (\n <Dialog.Content maxWidth=\"529px\">\n <Dialog.Title size=\"4\" weight={\"bold\"}>\n <Translation\n defaultMessage=\"Save your key\"\n id=\"wFFCij\"\n description=\"Dialog title prompting user to save their API key\"\n />\n </Dialog.Title>\n <Dialog.Description>\n <Translation\n defaultMessage=\"Please save this API key in a secure location. If you lose it, you'll need to generate a new one.\"\n id=\"sy+2x7\"\n description=\"Warning message about saving the API key\"\n />\n </Dialog.Description>\n <Callout.Root mt=\"5\" mb=\"4\">\n <Callout.Icon>\n <InfoCircledIcon />\n </Callout.Icon>\n <Callout.Text>\n <Translation\n defaultMessage=\"You won't be able to access the key again. Please copy it now.\"\n id=\"EFAK/K\"\n description=\"Callout warning that the key won't be accessible again\"\n />\n </Callout.Text>\n </Callout.Root>\n <TextField value={apiKey} readOnly>\n <TextFieldSlot side=\"right\">\n <CopyIconButton value={apiKey} />\n </TextFieldSlot>\n </TextField>\n <Flex align=\"center\" gap=\"3\" justify=\"end\" mt=\"5\">\n <Dialog.Close>\n <CopyButton value={apiKey}>\n <Translation\n defaultMessage=\"Copy and close\"\n id=\"ViYtvQ\"\n description=\"Button to copy API key and close dialog\"\n />\n </CopyButton>\n </Dialog.Close>\n </Flex>\n </Dialog.Content>\n );\n}\n\nfunction Permission({\n checked,\n permission,\n onChange,\n}: {\n checked: boolean;\n permission: ListOrganizationApiKeyPermission;\n onChange: (isChecked: boolean) => void;\n}) {\n return (\n <label\n htmlFor={permission.slug}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"var(--space-2) var(--space-4)\",\n gap: \"var(--space-3)\",\n borderBottom: \"1px solid var(--gray-6)\",\n cursor: \"pointer\",\n }}\n >\n <Checkbox\n checked={checked}\n id={permission.slug}\n name=\"permission\"\n value={permission.slug}\n onCheckedChange={(e) => onChange(e === \"indeterminate\" ? false : e)}\n />\n <Flex direction=\"column\">\n <Text size=\"2\" weight=\"bold\">\n {permission.name}\n </Text>\n <Text size=\"2\" color=\"gray\">\n {permission.description}\n </Text>\n </Flex>\n </label>\n );\n}\n\nfunction PermissionsField({\n permissions = [],\n}: {\n permissions: ListOrganizationApiKeyPermission[];\n}) {\n const [selectedPermissions, setSelectedPermissions] = React.useState<\n ListOrganizationApiKeyPermission[]\n >([]);\n const [filter, setFilter] = React.useState(\"\");\n const fieldRef = React.useRef<HTMLDivElement | null>(null);\n const translate = useTranslation();\n const filteredPermissions = React.useMemo(() => {\n return permissions.filter((p) => {\n return (\n !filter.trim().length ||\n p.description?.toLowerCase().match(filter.toLowerCase()) ||\n p.name.toLowerCase().match(filter.toLowerCase()) ||\n p.slug.toLowerCase().match(filter.toLowerCase())\n );\n });\n }, [permissions, filter]);\n\n React.useEffect(() => {\n const onResize = () => {\n if (fieldRef.current && filteredPermissions.length !== 0) {\n fieldRef.current.style.minHeight = \"unset\";\n }\n };\n window.addEventListener(\"resize\", onResize);\n return () => {\n window.removeEventListener(\"resize\", onResize);\n };\n }, [filteredPermissions.length]);\n\n return (\n <Form.Field name=\"permissions\" asChild>\n <Flex direction=\"column\" align=\"stretch\" gap=\"1\">\n <Form.Label asChild>\n <Label>\n <Translation\n defaultMessage=\"Permissions\"\n id=\"6YN1wO\"\n description=\"Label for API key permissions field\"\n />\n </Label>\n </Form.Label>\n <Text size=\"2\" mb=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"Only enable the minimum permissions required for your use case.\"\n id=\"q2VEtG\"\n description=\"Help text for API key permissions selection\"\n />\n </Text>\n <Flex\n ref={fieldRef}\n style={{\n borderRadius: \"var(--radius-3)\",\n borderColor: \"var(--gray-6)\",\n borderWidth: \"1px\",\n borderStyle: \"solid\",\n }}\n direction=\"column\"\n align=\"stretch\"\n >\n <TextField\n name=\"search\"\n value={filter}\n size=\"2\"\n onChange={(e) => {\n if (fieldRef.current) {\n fieldRef.current.style.minHeight =\n fieldRef.current.clientHeight + 2 + \"px\";\n }\n setFilter(e.target.value);\n }}\n style={{\n height: \"40px\",\n borderRadius: \"var(--radius-3) var(--radius-3) 0 0\",\n boxShadow: \"none\",\n }}\n placeholder={translate({\n defaultMessage: \"Search\",\n id: \"iyNcly\",\n description: \"Placeholder for permissions search input\",\n })}\n >\n <TextFieldSlot side=\"left\" px=\"3\">\n <MagnifyingGlassIcon aria-hidden=\"true\" height=\"16\" width=\"16\" />\n </TextFieldSlot>\n </TextField>\n {filteredPermissions.length > 0 ? (\n <>\n <ScrollArea\n scrollbars=\"vertical\"\n style={{\n flex: 1,\n maxHeight: \"calc(50vh - 150px)\",\n border: \"1px solid var(--gray-6)\",\n borderWidth: \"1px 0 0\",\n }}\n >\n {filteredPermissions.map((permission) => (\n <Permission\n checked={selectedPermissions.some(\n (sp) => sp.slug === permission.slug,\n )}\n key={permission.slug}\n permission={permission}\n onChange={(isChecked) =>\n setSelectedPermissions((prev) =>\n isChecked\n ? [...prev, permission]\n : prev.filter((sp) => sp.slug !== permission.slug),\n )\n }\n />\n ))}\n </ScrollArea>\n <Box\n px=\"4\"\n py=\"3\"\n mt={\"-1px\"}\n flexGrow=\"0\"\n style={{\n borderTop: \"0 solid var(--gray-6)\",\n borderWidth: \"1px 0 0\",\n }}\n >\n <SelectedPermissions\n total={permissions.length}\n selected={selectedPermissions.length}\n onToggle={() =>\n setSelectedPermissions((prev) =>\n prev.length === permissions.length ? [] : permissions,\n )\n }\n />\n </Box>\n </>\n ) : (\n <Flex\n gap=\"4\"\n pt=\"6\"\n pb=\"6\"\n style={{ borderTop: \"1px solid var(--gray-6)\" }}\n flexGrow=\"1\"\n justify=\"center\"\n align=\"center\"\n direction=\"column\"\n >\n <MagnifyingGlassIcon\n style={{\n color: \"var(--gray-9)\",\n width: \"var(--space-5)\",\n height: \"var(--space-5)\",\n }}\n />\n <Text size=\"2\" align=\"center\" mb=\"2\" wrap=\"balance\" weight=\"bold\">\n <Translation\n defaultMessage=\"No permissions match your search\"\n id=\"xjqT9f\"\n description=\"Empty state message when no permissions match search\"\n />\n </Text>\n <Button\n variant=\"secondary\"\n size=\"1\"\n onClick={() => setFilter(\"\")}\n >\n <Translation\n defaultMessage=\"Clear search\"\n id=\"YrMs63\"\n description=\"Button to clear permissions search\"\n />\n </Button>\n </Flex>\n )}\n </Flex>\n </Flex>\n </Form.Field>\n );\n}\n\nfunction SelectedPermissions({\n total,\n selected,\n onToggle,\n}: {\n total: number;\n selected: number;\n onToggle: () => void;\n}) {\n return (\n <Flex align=\"center\" gap=\"2\">\n <Text size=\"1\" style={{ lineHeight: 1, color: \"var(--gray-11)\" }}>\n {selected === 0 ? (\n <Translation\n defaultMessage=\"No permissions selected\"\n id=\"FqU9hv\"\n description=\"Message when no permissions are selected\"\n />\n ) : selected === 1 ? (\n <Translation\n defaultMessage=\"1 permission selected\"\n id=\"VouLgT\"\n description=\"Message showing count of selected permissions\"\n />\n ) : (\n <Translation\n defaultMessage=\"{count} permissions selected\"\n id=\"ZL90DI\"\n description=\"Message showing count of selected permissions\"\n values={{ count: selected }}\n />\n )}\n </Text>\n <Text\n size=\"1\"\n onClick={onToggle}\n style={{ cursor: \"pointer\", color: \"var(--accent-10)\" }}\n >\n {total === selected ? (\n <Translation\n defaultMessage=\"Deselect all\"\n id=\"63f7SY\"\n description=\"Button to deselect all permissions\"\n />\n ) : (\n <Translation\n defaultMessage=\"Select all\"\n id=\"onTqlA\"\n description=\"Button to select all permissions\"\n />\n )}\n </Text>\n </Flex>\n );\n}\n\nexport function CreateApiKeyButton() {\n const { dispatch } = useApiKeysContext();\n return (\n <Button onClick={() => dispatch({ type: \"OPEN_CREATE_DIALOG\" })}>\n <Translation\n defaultMessage=\"Create API key\"\n id=\"vq5724\"\n description=\"Button to open create API key dialog\"\n />\n </Button>\n );\n}\n"],"mappings":"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","names":["errors","error","data"]}
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useIsHydrated } from "../use-is-hydrated.js";
|
|
3
|
-
import { getComparativeReadableDate } from "../utils.js";
|
|
4
2
|
import { Translation } from "../i18n/translation.js";
|
|
5
|
-
import {
|
|
3
|
+
import { useComparativeReadableDate } from "../../use-comparative-readable-date.js";
|
|
6
4
|
function RelativeTime({ date }) {
|
|
7
|
-
const isHydrated = useIsHydrated();
|
|
8
|
-
const locale = useLocale();
|
|
9
5
|
if (!date) {
|
|
10
|
-
return /* @__PURE__ */ jsx(
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
11
7
|
Translation,
|
|
12
8
|
{
|
|
13
9
|
defaultMessage: "Never",
|
|
14
10
|
id: "yXCbRa",
|
|
15
11
|
description: "Text indicating API key was never used"
|
|
16
12
|
}
|
|
17
|
-
)
|
|
13
|
+
);
|
|
18
14
|
}
|
|
19
|
-
return /* @__PURE__ */ jsx(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}) });
|
|
15
|
+
return /* @__PURE__ */ jsx(RelativeTimeImpl, { date });
|
|
16
|
+
}
|
|
17
|
+
function RelativeTimeImpl({ date }) {
|
|
18
|
+
return /* @__PURE__ */ jsx(Fragment, { children: useComparativeReadableDate({ date }) });
|
|
24
19
|
}
|
|
25
20
|
export {
|
|
26
21
|
RelativeTime
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/api-keys/relative-time.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/api-keys/relative-time.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useComparativeReadableDate } from \"../../use-comparative-readable-date.js\";\n\nexport function RelativeTime({ date }: { date?: Date }) {\n if (!date) {\n return (\n <Translation\n defaultMessage=\"Never\"\n id=\"yXCbRa\"\n description=\"Text indicating API key was never used\"\n />\n );\n }\n\n return <RelativeTimeImpl date={date} />;\n}\n\nfunction RelativeTimeImpl({ date }: { date: Date }): React.ReactNode {\n return <>{useComparativeReadableDate({ date })}</>;\n}\n"],"mappings":"AAOM,SAYG,UAZH;AANN,SAAS,mBAAmB;AAC5B,SAAS,kCAAkC;AAEpC,SAAS,aAAa,EAAE,KAAK,GAAoB;AACtD,MAAI,CAAC,MAAM;AACT,WACE;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd;AAAA,EAEJ;AAEA,SAAO,oBAAC,oBAAiB,MAAY;AACvC;AAEA,SAAS,iBAAiB,EAAE,KAAK,GAAoC;AACnE,SAAO,gCAAG,qCAA2B,EAAE,KAAK,CAAC,GAAE;AACjD;","names":[]}
|
|
@@ -17,6 +17,7 @@ import { Translation } from "./i18n/translation.js";
|
|
|
17
17
|
import {
|
|
18
18
|
getDomProps
|
|
19
19
|
} from "./utils.js";
|
|
20
|
+
import { useComparativeReadableDate } from "../use-comparative-readable-date.js";
|
|
20
21
|
const DirectoryState = {
|
|
21
22
|
requires_type: "requires_type",
|
|
22
23
|
linked: "linked",
|
|
@@ -109,7 +110,10 @@ const DirectorySync = ({
|
|
|
109
110
|
const groupCount = metadata?.groups ?? 0;
|
|
110
111
|
const lastSyncAt = rest.lastSyncAt ? new Date(rest.lastSyncAt) : null;
|
|
111
112
|
const currentDate = "currentDate" in rest && rest.currentDate instanceof Date ? rest.currentDate : void 0;
|
|
112
|
-
const lastSyncString =
|
|
113
|
+
const lastSyncString = useComparativeReadableDate({
|
|
114
|
+
date: lastSyncAt,
|
|
115
|
+
currentDate
|
|
116
|
+
});
|
|
113
117
|
const showCounts = state === DirectoryState.linked || state === DirectoryState.invalid_credentials || state === DirectoryState.deleting;
|
|
114
118
|
const statusBadge = getStatusBadge(state);
|
|
115
119
|
const spinnerMessage = getSpinnerMessage(state, !!type);
|
|
@@ -252,35 +256,6 @@ function getWidgetRootDomProps(state, domProps) {
|
|
|
252
256
|
widgetState: state
|
|
253
257
|
});
|
|
254
258
|
}
|
|
255
|
-
function getRelativeTimeString(currentDate, lastSync) {
|
|
256
|
-
if (!currentDate || !lastSync) {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
260
|
-
const diff = lastSync.getTime() - currentDate.getTime();
|
|
261
|
-
const diffSeconds = Math.round(diff / 1e3);
|
|
262
|
-
const diffMinutes = Math.round(diffSeconds / 60);
|
|
263
|
-
const diffHours = Math.round(diffMinutes / 60);
|
|
264
|
-
const diffDays = Math.round(diffHours / 24);
|
|
265
|
-
const diffMonths = Math.round(diffDays / 30);
|
|
266
|
-
const diffYears = Math.round(diffMonths / 12);
|
|
267
|
-
if (Math.abs(diffSeconds) < 60) {
|
|
268
|
-
return "now";
|
|
269
|
-
}
|
|
270
|
-
if (Math.abs(diffMinutes) < 60) {
|
|
271
|
-
return rtf.format(diffMinutes, "minute");
|
|
272
|
-
}
|
|
273
|
-
if (Math.abs(diffHours) < 24) {
|
|
274
|
-
return rtf.format(diffHours, "hour");
|
|
275
|
-
}
|
|
276
|
-
if (Math.abs(diffDays) < 30) {
|
|
277
|
-
return rtf.format(diffDays, "day");
|
|
278
|
-
}
|
|
279
|
-
if (Math.abs(diffMonths) < 12) {
|
|
280
|
-
return rtf.format(diffMonths, "month");
|
|
281
|
-
}
|
|
282
|
-
return rtf.format(diffYears, "year");
|
|
283
|
-
}
|
|
284
259
|
export {
|
|
285
260
|
DirectoryState,
|
|
286
261
|
DirectorySync,
|