@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.
Files changed (73) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/admin-portal-sso-connection.client.cjs +72 -75
  3. package/dist/cjs/admin-portal-sso-connection.client.cjs.map +1 -1
  4. package/dist/cjs/lib/admin-portal-audit-log-streaming.cjs +5 -22
  5. package/dist/cjs/lib/admin-portal-audit-log-streaming.cjs.map +1 -1
  6. package/dist/cjs/lib/admin-portal-sso-connection.cjs +7 -41
  7. package/dist/cjs/lib/admin-portal-sso-connection.cjs.map +1 -1
  8. package/dist/cjs/lib/admin-portal-sso-connection.d.cts +0 -1
  9. package/dist/cjs/lib/api-keys/api-key-expiration.cjs +5 -3
  10. package/dist/cjs/lib/api-keys/api-key-expiration.cjs.map +1 -1
  11. package/dist/cjs/lib/api-keys/api-key-expiration.d.cts +1 -1
  12. package/dist/cjs/lib/api-keys/create-api-key.cjs +4 -1
  13. package/dist/cjs/lib/api-keys/create-api-key.cjs.map +1 -1
  14. package/dist/cjs/lib/api-keys/relative-time.cjs +7 -12
  15. package/dist/cjs/lib/api-keys/relative-time.cjs.map +1 -1
  16. package/dist/cjs/lib/directory-sync.cjs +5 -30
  17. package/dist/cjs/lib/directory-sync.cjs.map +1 -1
  18. package/dist/cjs/lib/domain-item.cjs +5 -10
  19. package/dist/cjs/lib/domain-item.cjs.map +1 -1
  20. package/dist/cjs/lib/user-api-keys/create-user-api-key.cjs +4 -1
  21. package/dist/cjs/lib/user-api-keys/create-user-api-key.cjs.map +1 -1
  22. package/dist/cjs/lib/user-security.cjs +9 -17
  23. package/dist/cjs/lib/user-security.cjs.map +1 -1
  24. package/dist/cjs/lib/user-sessions.cjs +9 -13
  25. package/dist/cjs/lib/user-sessions.cjs.map +1 -1
  26. package/dist/cjs/lib/user-sessions.d.cts +2 -2
  27. package/dist/cjs/lib/users-management.cjs +14 -24
  28. package/dist/cjs/lib/users-management.cjs.map +1 -1
  29. package/dist/cjs/lib/utils.cjs +49 -13
  30. package/dist/cjs/lib/utils.cjs.map +1 -1
  31. package/dist/cjs/use-comparative-readable-date.cjs +70 -0
  32. package/dist/cjs/use-comparative-readable-date.cjs.map +1 -0
  33. package/dist/cjs/use-comparative-readable-date.d.cts +8 -0
  34. package/dist/cjs/use-now.cjs +101 -0
  35. package/dist/cjs/use-now.cjs.map +1 -0
  36. package/dist/cjs/use-now.d.cts +3 -0
  37. package/dist/esm/admin-portal-sso-connection.client.js +73 -66
  38. package/dist/esm/admin-portal-sso-connection.client.js.map +1 -1
  39. package/dist/esm/lib/admin-portal-audit-log-streaming.js +5 -22
  40. package/dist/esm/lib/admin-portal-audit-log-streaming.js.map +1 -1
  41. package/dist/esm/lib/admin-portal-sso-connection.d.ts +0 -1
  42. package/dist/esm/lib/admin-portal-sso-connection.js +7 -41
  43. package/dist/esm/lib/admin-portal-sso-connection.js.map +1 -1
  44. package/dist/esm/lib/api-keys/api-key-expiration.d.ts +1 -1
  45. package/dist/esm/lib/api-keys/api-key-expiration.js +5 -3
  46. package/dist/esm/lib/api-keys/api-key-expiration.js.map +1 -1
  47. package/dist/esm/lib/api-keys/create-api-key.js +4 -1
  48. package/dist/esm/lib/api-keys/create-api-key.js.map +1 -1
  49. package/dist/esm/lib/api-keys/relative-time.js +7 -12
  50. package/dist/esm/lib/api-keys/relative-time.js.map +1 -1
  51. package/dist/esm/lib/directory-sync.js +5 -30
  52. package/dist/esm/lib/directory-sync.js.map +1 -1
  53. package/dist/esm/lib/domain-item.js +5 -10
  54. package/dist/esm/lib/domain-item.js.map +1 -1
  55. package/dist/esm/lib/identity-providers.d.ts +2 -2
  56. package/dist/esm/lib/user-api-keys/create-user-api-key.js +4 -1
  57. package/dist/esm/lib/user-api-keys/create-user-api-key.js.map +1 -1
  58. package/dist/esm/lib/user-security.js +9 -18
  59. package/dist/esm/lib/user-security.js.map +1 -1
  60. package/dist/esm/lib/user-sessions.d.ts +2 -2
  61. package/dist/esm/lib/user-sessions.js +9 -14
  62. package/dist/esm/lib/user-sessions.js.map +1 -1
  63. package/dist/esm/lib/users-management.js +14 -25
  64. package/dist/esm/lib/users-management.js.map +1 -1
  65. package/dist/esm/lib/utils.js +49 -13
  66. package/dist/esm/lib/utils.js.map +1 -1
  67. package/dist/esm/use-comparative-readable-date.d.ts +8 -0
  68. package/dist/esm/use-comparative-readable-date.js +36 -0
  69. package/dist/esm/use-comparative-readable-date.js.map +1 -0
  70. package/dist/esm/use-now.d.ts +3 -0
  71. package/dist/esm/use-now.js +67 -0
  72. package/dist/esm/use-now.js.map +1 -0
  73. 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 relativeTimeString = React.useMemo(() => {
186
- if (!lastSession || !currentDate || connectionStatus === "NotConfigured" || connectionStatus === "Inactive") {
187
- return null;
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
- relativeTimeString ? /* @__PURE__ */ jsx(Text, { color: "gray", size: "2", children: /* @__PURE__ */ jsx(
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: relativeTimeString }
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":";AAmGkB,mBAEI,KAFJ;AAjGlB,YAAY,WAAW;AACvB,SAAS,KAAK,MAAM,MAAM,YAAY;AACtC,SAAS,QAAQ,gBAAgB;AACjC,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,YAAY,cAAc;AAC1B,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,OAEK;AACP,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,kCAAkC;AA0C3C,MAAM,kCACJ,MAAM,cAA2D,IAAI;AACvE,gCAAgC,cAAc;AAE9C,SAAS,qCAAqC;AAC5C,QAAM,UAAU,MAAM,WAAW,+BAA+B;AAChE,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,2BAAoE,CAAC;AAAA,EACzE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,SACE,oBAAC,SAAS,MAAT,EAAc,MAAK,KAAK,GAAG,sBAAsB,YAAY,QAAQ,GACpE,+BAAC,gCAAgC,UAAhC,EAAyC,OAAO,EAAE,iBAAiB,GAClE;AAAA,wBAAC,SAAS,MAAT,EACC,8BAAC,QAAK,WAAU,OAAM,SAAQ,WAAU,OAAM,UAAS,KAAI,KACvD,iBAAM;AACN,UAAI,qBAAqB,iBAAiB;AACxC,eACE,iCACE;AAAA,8BAAC,QAAK,MAAK,KAAI,OAAM,QACnB;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd,GACF;AAAA,UACC;AAAA,WACH;AAAA,MAEJ;AAEA,aACE,iCACE;AAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QACA,qBAAC,QAAK,KAAI,KAAI,OAAM,UAClB;AAAA,8BAAC,aAAU,QAAQ,kBAAkB;AAAA,UACpC;AAAA,WACH;AAAA,SACF;AAAA,IAEJ,GAAG,GACL,GACF;AAAA,IACC,qBAAqB,aACpB,oBAAC,SAAS,MAAT,EACC,+BAAC,QAAK,OAAM,SAAQ,KAAI,KACtB;AAAA,0BAAC,OAAI,SAAO,MAAC,IAAG,OAAM,YAAW,KAC/B,8BAAC,mBAAgB,OAAM,QAAO,GAChC;AAAA,OACE,MAAM;AACN,YAAI,qBAAqB,WAAW;AAClC,iBACE,oBAAC,QAAK,MAAK,KAAI,OAAM,QACjB,iBAAM;AACN,gBAAI,CAAC,YAAY;AACf,qBACE;AAAA,gBAAC;AAAA;AAAA,kBACC,gBAAe;AAAA,kBACf,IAAG;AAAA,kBACH,aAAY;AAAA;AAAA,cACd;AAAA,YAEJ;AAEA,mBACE;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA,gBACZ,QAAQ;AAAA,kBACN,YACE,oBAAC,QAAK,QAAO,QAAO,IAAG,QACpB,sBAAY,eAAe,SAAS;AAAA,oBACnC,OAAO;AAAA,oBACP,KAAK;AAAA,oBACL,MAAM;AAAA,kBACR,CAAC,GACH;AAAA,gBAEJ;AAAA;AAAA,YACF;AAAA,UAEJ,GAAG,GACL;AAAA,QAEJ;AAEA,YAAI,qBAAqB,YAAY;AACnC,iBACE,oBAAC,QAAK,MAAK,KAAI,OAAM,QACjB,iBAAM;AACN,gBAAI,CAAC,YAAY;AACf,qBACE;AAAA,gBAAC;AAAA;AAAA,kBACC,gBAAe;AAAA,kBACf,IAAG;AAAA,kBACH,aAAY;AAAA;AAAA,cACd;AAAA,YAEJ;AAEA,mBACE;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA,gBACZ,QAAQ;AAAA,kBACN,YACE,oBAAC,QAAK,QAAO,QAAO,IAAG,QACpB,sBAAY,eAAe,SAAS;AAAA,oBACnC,OAAO;AAAA,oBACP,KAAK;AAAA,oBACL,MAAM;AAAA,kBACR,CAAC,GACH;AAAA,gBAEJ;AAAA;AAAA,YACF;AAAA,UAEJ,GAAG,GACL;AAAA,QAEJ;AAAA,MACF,GAAG;AAAA,OACL,GACF;AAAA,KAEJ,GACF;AAEJ;AAEA,SAAS,UAAU;AAAA,EACjB;AACF,GAEG;AACD,MAAI,WAAW,iBAAiB;AAC9B,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,YAAY;AACzB,WACE,oBAAC,UAAO,OAAM,WACZ;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF;AAAA,EAEJ;AAEA,MAAI,WAAW,WAAW;AACxB,WACE,oBAAC,UAAO,OAAM,SACZ;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF;AAAA,EAEJ;AAEA,MAAI,WAAW,YAAY,WAAW,YAAY;AAChD,WACE,oBAAC,UAAO,OAAM,WACZ;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF;AAAA,EAEJ;AAEA,SAAO,YAAY,MAAM;AAC3B;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,eAAe,2BAA2B;AAAA,IAC9C,MACE,qBAAqB,mBAAmB,qBAAqB,aACzD,OACA;AAAA,EACR,CAAC;AAED,SACE,qBAAC,QAAK,KAAI,KAAI,OAAM,UAClB;AAAA,wBAAC,aAAU,OAAM,SACf,8BAAC,gBAAa,UAAU,kBAAkB,MAAK,KAAI,GACrD;AAAA,IACC,cACC,qBAAC,QAAK,WAAU,UACd;AAAA,0BAAC,QAAK,MAAK,KAAI,QAAO,QACnB,kCAAwB,gBAAgB,GAC3C;AAAA,MACC,eACC,oBAAC,QAAK,OAAM,QAAO,MAAK,KACtB;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA,UACZ,QAAQ,EAAE,aAAa;AAAA;AAAA,MACzB,GACF,IACE;AAAA,OACN,IAEA,oBAAC,QAAK,MAAK,KAAI,QAAO,QACnB,kCAAwB,gBAAgB,GAC3C;AAAA,KAEJ;AAEJ;AAEA,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA;AACF,GAGG;AACD,QAAM,EAAE,iBAAiB,IAAI,mCAAmC;AAChE,QAAM,SAAS,MAAM;AACnB,YAAQ,kBAAkB;AAAA,MACxB,KAAK;AACH,eACE;AAAA,UAAC;AAAA;AAAA,YACC,gBAAe;AAAA,YACf,IAAG;AAAA,YACH,aAAY;AAAA;AAAA,QACd;AAAA,MAEJ,KAAK;AACH,eACE;AAAA,UAAC;AAAA;AAAA,YACC,gBAAe;AAAA,YACf,IAAG;AAAA,YACH,aAAY;AAAA;AAAA,QACd;AAAA,MAEJ,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eACE;AAAA,UAAC;AAAA;AAAA,YACC,gBAAe;AAAA,YACf,IAAG;AAAA,YACH,aAAY;AAAA;AAAA,QACd;AAAA,MAEJ;AACE,eAAO,YAAY,gBAAgB;AAAA,IACvC;AAAA,EACF,GAAG;AAEH,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS;AAAA,MAER;AAAA;AAAA,QAAM;AAAA,QAAC,oBAAC,oBAAiB,eAAW,MAAC;AAAA;AAAA;AAAA,EACxC;AAEJ;AAIA,MAAM,kCAEF,CAAC,UAAU;AACb,SACE,oBAAC,QAAK,MAAK,KAAK,GAAG,sBAAsB,WAAW,KAAK,GACvD,+BAAC,QAAK,WAAU,OAAM,SAAQ,WAAU,OAAM,UAAS,KAAI,KACzD;AAAA,yBAAC,QAAK,KAAI,KAAI,OAAM,UAClB;AAAA,0BAAC,YACC,8BAAC,aAAU,OAAM,SACf,8BAAC,gBAAa,UAAS,QAAO,MAAK,KAAI,GACzC,GACF;AAAA,MACA,qBAAC,QAAK,WAAU,UAAS,KAAI,KAAI,IAAG,QAClC;AAAA,4BAAC,YAEC,8BAAC,QAAK,MAAK,KAAI,kBAAI,GACrB;AAAA,QACA,oBAAC,YAEC,8BAAC,QAAK,MAAK,KAAI,yCAA2B,GAC5C;AAAA,SACF;AAAA,OACF;AAAA,IACA,qBAAC,QAAK,KAAI,KAAI,OAAM,UAClB;AAAA,0BAAC,YAEC,8BAAC,UAAO,OAAM,SAAQ,6BAAe,GACvC;AAAA,MACA,oBAAC,YAEC,+BAAC,UAAO,SAAQ,aAAY;AAAA;AAAA,QACnB,oBAAC,oBAAiB,eAAW,MAAC;AAAA,SACvC,GACF;AAAA,OACF;AAAA,KACF,GACF;AAEJ;AAMA,MAAM,gCAEF,CAAC,EAAE,OAAO,GAAG,SAAS,MAAM;AAC9B,QAAM,YAAY,eAAe;AACjC,QAAM,UAAU,MAAM;AACpB,YAAQ,MAAM,KAAK;AAAA,EACrB,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,EAAE,SAAS,QAAQ,IAAI,gBAAgB,OAAO,SAAS;AAE7D,SACE,oBAAC,QAAK,MAAK,KAAK,GAAG,sBAAsB,SAAS,QAAQ,GACxD,8BAAC,QAAK,WAAU,OAAM,SAAQ,WAAU,OAAM,UAAS,KAAI,KACzD,+BAAC,QAAK,KAAI,KAAI,OAAM,UAClB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,SAAQ;AAAA,QACR,OAAM;AAAA,QACN,QAAO;AAAA,QACP,OAAO;AAAA,UACL,cAAc;AAAA,UACd,iBAAiB;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,QACd;AAAA,QAEA,8BAAC,cAAW,OAAM,QAAO,QAAO,QAAO;AAAA;AAAA,IACzC;AAAA,IACA,qBAAC,QAAK,WAAU,UACd;AAAA,0BAAC,QAAK,MAAK,KAAI,QAAO,QACnB,mBACH;AAAA,MACA,oBAAC,QAAK,MAAK,KAAI,OAAM,QAClB,mBACH;AAAA,OACF;AAAA,KACF,GACF,GACF;AAEJ;AAEA,SAAS,sBACP,OACA,UACA;AACA,SAAO,YAAY;AAAA,IACjB,GAAG;AAAA,IACH,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC;AACH;","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(selectedExpiration);
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 { useLocale } from "../i18n/use-locale.js";
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(Fragment, { children: /* @__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(Fragment, { children: isHydrated ? getComparativeReadableDate(/* @__PURE__ */ new Date(), date, { locale }) : date.toLocaleDateString(locale, {
20
- month: "short",
21
- day: "numeric",
22
- year: "numeric"
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 { useIsHydrated } from \"../use-is-hydrated.js\";\nimport { getComparativeReadableDate } from \"../utils.js\";\nimport { Translation } from \"../i18n/translation.js\";\nimport { useLocale } from \"../i18n/use-locale.js\";\n\nexport function RelativeTime({ date }: { date?: Date }) {\n const isHydrated = useIsHydrated();\n const locale = useLocale();\n\n if (!date) {\n return (\n <>\n <Translation\n defaultMessage=\"Never\"\n id=\"yXCbRa\"\n description=\"Text indicating API key was never used\"\n />\n </>\n );\n }\n\n return (\n <>\n {isHydrated\n ? getComparativeReadableDate(new Date(), date, { locale })\n : date.toLocaleDateString(locale, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n })}\n </>\n );\n}\n"],"mappings":"AAWM,mBACE,WADF;AAXN,SAAS,qBAAqB;AAC9B,SAAS,kCAAkC;AAC3C,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAEnB,SAAS,aAAa,EAAE,KAAK,GAAoB;AACtD,QAAM,aAAa,cAAc;AACjC,QAAM,SAAS,UAAU;AAEzB,MAAI,CAAC,MAAM;AACT,WACE,gCACE;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF;AAAA,EAEJ;AAEA,SACE,gCACG,uBACG,2BAA2B,oBAAI,KAAK,GAAG,MAAM,EAAE,OAAO,CAAC,IACvD,KAAK,mBAAmB,QAAQ;AAAA,IAC9B,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR,CAAC,GACP;AAEJ;","names":[]}
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 = lastSyncAt ? getRelativeTimeString(currentDate ?? /* @__PURE__ */ new Date(), lastSyncAt) : null;
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,