@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/user-sessions.tsx"],"sourcesContent":["\"use client\";\n\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport { Badge, Button, Skeleton } from \"./elements.js\";\nimport {\n getComparativeReadableDate,\n getDomProps,\n getUserLocation,\n parseUserAgent,\n type WidgetRootDomProps,\n type WidgetRootState,\n} from \"./utils.js\";\nimport { LogoutDialog } from \"./logout-dialog.js\";\nimport { LogoutAllSessionsDialog } from \"./logout-all-sessions-dialog.js\";\nimport type { SessionFieldsFragment } from \"../api/client-graphql/generated.js\";\nimport * as CardList from \"./card-list.js\";\nimport { IconPanel } from \"./icon-panel.js\";\nimport { useState } from \"react\";\nimport { GenericError } from \"./generic-error.js\";\nimport { LaptopIcon, MobileIcon } from \"@radix-ui/react-icons\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useLocale } from \"./i18n/use-locale.js\";\n\ninterface UserSessionsProps extends WidgetRootDomProps {\n sessionsData: SessionFieldsFragment[];\n currentSessionId: string;\n}\n\nconst UserSessions = ({\n sessionsData: sessions,\n currentSessionId,\n ...domProps\n}: UserSessionsProps) => {\n const currentSession = sessions.find(\n (session) => session.id === currentSessionId,\n );\n const otherSessions = sessions.filter(\n (session) => session.id !== currentSessionId,\n );\n const [openLogoutDialog, setOpenLogoutDialog] = useState(false);\n const [sessionToSignOut, setSessionToSignOut] =\n useState<SessionFieldsFragment | null>(null);\n const [openLogoutAllSessionsDialog, setOpenLogoutAllSessionsDialog] =\n useState(false);\n\n return (\n <Flex\n direction=\"column\"\n gap=\"4\"\n {...getWidgetRootDomProps(\"resolved\", domProps)}\n >\n <CardList.Root>\n {currentSession && (\n <CardList.Item>\n <UserSession session={currentSession} isCurrentSession />\n </CardList.Item>\n )}\n\n {otherSessions.map((session) => (\n <CardList.Item key={session.id}>\n <UserSession\n session={session}\n onSignOut={() => {\n setSessionToSignOut(session);\n setOpenLogoutDialog(true);\n }}\n />\n </CardList.Item>\n ))}\n </CardList.Root>\n\n {sessions.length > 1 && (\n <Card size=\"2\">\n <Flex gap=\"2\" justify=\"between\" align=\"center\">\n <Box>\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\">\n <Translation\n defaultMessage=\"Sign out of all other devices\"\n id=\"sfkEFa\"\n description=\"Heading for sign out all other devices action\"\n />\n </Text>\n <Text size=\"2\" color=\"gray\" as=\"p\">\n <Translation\n defaultMessage=\"Remove access from all devices except this one\"\n id=\"LsD5BK\"\n description=\"Description for sign out all other devices action\"\n />\n </Text>\n </Box>\n <Button\n variant=\"secondary\"\n onClick={() => {\n setOpenLogoutAllSessionsDialog(true);\n }}\n >\n <Translation\n defaultMessage=\"Sign out\"\n id=\"oKQ0rm\"\n description=\"Button text to sign out of all other devices\"\n />\n </Button>\n </Flex>\n </Card>\n )}\n\n {sessionToSignOut && (\n <LogoutDialog\n session={sessionToSignOut}\n open={openLogoutDialog}\n onOpenChange={setOpenLogoutDialog}\n />\n )}\n\n <LogoutAllSessionsDialog\n open={openLogoutAllSessionsDialog}\n onOpenChange={setOpenLogoutAllSessionsDialog}\n currentSessionId={currentSessionId}\n />\n </Flex>\n );\n};\n\ninterface UserSessionsLoadingProps extends WidgetRootDomProps {}\n\nconst UserSessionsLoading: React.FC<UserSessionsLoadingProps> = (props) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"loading\", props)}>\n <Flex gap=\"4\" align=\"center\">\n <Skeleton>\n <IconPanel />\n </Skeleton>\n\n <Flex direction=\"column\">\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\" mb=\"-2px\">\n <Skeleton>\n <Translation\n defaultMessage=\"The location\"\n id=\"+7UnNu\"\n description=\"Loading placeholder text for session location\"\n />\n </Skeleton>\n </Text>\n\n <Text size=\"2\" color=\"gray\" as=\"p\">\n <Skeleton>\n <Translation\n defaultMessage=\"The location\"\n id=\"+7UnNu\"\n description=\"Loading placeholder text for session location\"\n />\n </Skeleton>\n </Text>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\ninterface UserSessionsErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst UserSessionsError: React.FC<UserSessionsErrorProps> = ({\n error,\n ...domProps\n}) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"error\", domProps)}>\n <GenericError error={error} />\n </Card>\n );\n};\n\ninterface UserSessionProps {\n session: SessionFieldsFragment;\n isCurrentSession?: boolean;\n onSignOut?: (session: SessionFieldsFragment) => void;\n}\n\nconst UserSession = ({\n session,\n isCurrentSession = false,\n onSignOut,\n}: UserSessionProps) => {\n const userAgent = parseUserAgent(session.userAgent);\n const userLocation = getUserLocation(\n session.currentLocation,\n session.ipAddress,\n );\n const locale = useLocale();\n\n return (\n <Flex gap=\"4\" align=\"center\">\n <IconPanel>\n {userAgent?.isMobile ? <MobileIcon /> : <LaptopIcon />}\n </IconPanel>\n\n <Flex direction=\"column\">\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\" mb=\"-2px\">\n {userAgent?.pretty ?? (\n <Translation\n defaultMessage=\"Unknown device\"\n id=\"nF+P+T\"\n description=\"Label for a session whose device could not be determined from its user agent\"\n />\n )}\n </Text>\n\n <Flex gap=\"1\" align=\"center\">\n <Text size=\"2\" color=\"gray\" as=\"p\">\n {userLocation}\n </Text>\n\n {isCurrentSession && (\n <Badge ml=\"2\">\n <Translation\n defaultMessage=\"This device\"\n id=\"Wz8T8u\"\n description=\"Badge label indicating the current device session\"\n />\n </Badge>\n )}\n\n {!isCurrentSession && session.lastActivityAt && (\n <>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"2\" color=\"gray\">\n ∙\n </Text>\n <Text size=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"Last seen {timeAgo}\"\n id=\"gF+kUw\"\n description=\"Text showing when the session was last active\"\n values={{\n timeAgo: getComparativeReadableDate(\n new Date(),\n new Date(session.lastActivityAt),\n { locale },\n ),\n }}\n />\n </Text>\n </>\n )}\n </Flex>\n </Flex>\n\n {!isCurrentSession && (\n <Flex ml=\"auto\">\n <Button variant=\"secondary\" onClick={() => onSignOut?.(session)}>\n <Translation\n defaultMessage=\"Sign out\"\n id=\"sT1L78\"\n description=\"Button text to sign out of a session\"\n />\n </Button>\n </Flex>\n )}\n </Flex>\n );\n};\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"user-sessions\",\n widgetState: state,\n });\n}\n\nexport type { UserSessionsLoadingProps, UserSessionsErrorProps };\nexport { UserSessions, UserSessionsLoading, UserSessionsError };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/user-sessions.tsx"],"sourcesContent":["\"use client\";\n\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport { Badge, Button, Skeleton } from \"./elements.js\";\nimport {\n getDomProps,\n getUserLocation,\n parseUserAgent,\n type WidgetRootDomProps,\n type WidgetRootState,\n} from \"./utils.js\";\nimport { LogoutDialog } from \"./logout-dialog.js\";\nimport { LogoutAllSessionsDialog } from \"./logout-all-sessions-dialog.js\";\nimport type { SessionFieldsFragment } from \"../api/client-graphql/generated.js\";\nimport * as CardList from \"./card-list.js\";\nimport { IconPanel } from \"./icon-panel.js\";\nimport * as React from \"react\";\nimport { GenericError } from \"./generic-error.js\";\nimport { LaptopIcon, MobileIcon } from \"@radix-ui/react-icons\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useComparativeReadableDate } from \"../use-comparative-readable-date.js\";\n\ninterface UserSessionsProps extends WidgetRootDomProps {\n sessionsData: SessionFieldsFragment[];\n currentSessionId: string;\n}\n\nconst UserSessions = ({\n sessionsData: sessions,\n currentSessionId,\n ...domProps\n}: UserSessionsProps) => {\n const currentSession = sessions.find(\n (session) => session.id === currentSessionId,\n );\n const otherSessions = sessions.filter(\n (session) => session.id !== currentSessionId,\n );\n const [openLogoutDialog, setOpenLogoutDialog] = React.useState(false);\n const [sessionToSignOut, setSessionToSignOut] =\n React.useState<SessionFieldsFragment | null>(null);\n const [openLogoutAllSessionsDialog, setOpenLogoutAllSessionsDialog] =\n React.useState(false);\n\n return (\n <Flex\n direction=\"column\"\n gap=\"4\"\n {...getWidgetRootDomProps(\"resolved\", domProps)}\n >\n <CardList.Root>\n {currentSession && (\n <CardList.Item>\n <UserSession session={currentSession} isCurrentSession />\n </CardList.Item>\n )}\n\n {otherSessions.map((session) => (\n <CardList.Item key={session.id}>\n <UserSession\n session={session}\n onSignOut={() => {\n setSessionToSignOut(session);\n setOpenLogoutDialog(true);\n }}\n />\n </CardList.Item>\n ))}\n </CardList.Root>\n\n {sessions.length > 1 && (\n <Card size=\"2\">\n <Flex gap=\"2\" justify=\"between\" align=\"center\">\n <Box>\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\">\n <Translation\n defaultMessage=\"Sign out of all other devices\"\n id=\"sfkEFa\"\n description=\"Heading for sign out all other devices action\"\n />\n </Text>\n <Text size=\"2\" color=\"gray\" as=\"p\">\n <Translation\n defaultMessage=\"Remove access from all devices except this one\"\n id=\"LsD5BK\"\n description=\"Description for sign out all other devices action\"\n />\n </Text>\n </Box>\n <Button\n variant=\"secondary\"\n onClick={() => {\n setOpenLogoutAllSessionsDialog(true);\n }}\n >\n <Translation\n defaultMessage=\"Sign out\"\n id=\"oKQ0rm\"\n description=\"Button text to sign out of all other devices\"\n />\n </Button>\n </Flex>\n </Card>\n )}\n\n {sessionToSignOut && (\n <LogoutDialog\n session={sessionToSignOut}\n open={openLogoutDialog}\n onOpenChange={setOpenLogoutDialog}\n />\n )}\n\n <LogoutAllSessionsDialog\n open={openLogoutAllSessionsDialog}\n onOpenChange={setOpenLogoutAllSessionsDialog}\n currentSessionId={currentSessionId}\n />\n </Flex>\n );\n};\n\ninterface UserSessionsLoadingProps extends WidgetRootDomProps {}\n\nconst UserSessionsLoading: React.FC<UserSessionsLoadingProps> = (props) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"loading\", props)}>\n <Flex gap=\"4\" align=\"center\">\n <Skeleton>\n <IconPanel />\n </Skeleton>\n\n <Flex direction=\"column\">\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\" mb=\"-2px\">\n <Skeleton>\n <Translation\n defaultMessage=\"The location\"\n id=\"+7UnNu\"\n description=\"Loading placeholder text for session location\"\n />\n </Skeleton>\n </Text>\n\n <Text size=\"2\" color=\"gray\" as=\"p\">\n <Skeleton>\n <Translation\n defaultMessage=\"The location\"\n id=\"+7UnNu\"\n description=\"Loading placeholder text for session location\"\n />\n </Skeleton>\n </Text>\n </Flex>\n </Flex>\n </Card>\n );\n};\n\ninterface UserSessionsErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst UserSessionsError: React.FC<UserSessionsErrorProps> = ({\n error,\n ...domProps\n}) => {\n return (\n <Card size=\"2\" {...getWidgetRootDomProps(\"error\", domProps)}>\n <GenericError error={error} />\n </Card>\n );\n};\n\ninterface UserSessionProps {\n session: SessionFieldsFragment;\n isCurrentSession?: boolean;\n onSignOut?: (session: SessionFieldsFragment) => void;\n}\n\nconst UserSession = ({\n session,\n isCurrentSession = false,\n onSignOut,\n}: UserSessionProps) => {\n const userAgent = parseUserAgent(session.userAgent);\n const userLocation = getUserLocation(\n session.currentLocation,\n session.ipAddress,\n );\n const timeAgo = useComparativeReadableDate({\n date: session.lastActivityAt ? new Date(session.lastActivityAt) : null,\n });\n\n return (\n <Flex gap=\"4\" align=\"center\">\n <IconPanel>\n {userAgent?.isMobile ? <MobileIcon /> : <LaptopIcon />}\n </IconPanel>\n\n <Flex direction=\"column\">\n <Text size=\"2\" highContrast weight=\"bold\" as=\"p\" mb=\"-2px\">\n {userAgent?.pretty ?? (\n <Translation\n defaultMessage=\"Unknown device\"\n id=\"nF+P+T\"\n description=\"Label for a session whose device could not be determined from its user agent\"\n />\n )}\n </Text>\n\n <Flex gap=\"1\" align=\"center\">\n <Text size=\"2\" color=\"gray\" as=\"p\">\n {userLocation}\n </Text>\n\n {isCurrentSession && (\n <Badge ml=\"2\">\n <Translation\n defaultMessage=\"This device\"\n id=\"Wz8T8u\"\n description=\"Badge label indicating the current device session\"\n />\n </Badge>\n )}\n\n {!isCurrentSession && session.lastActivityAt && (\n <>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <Text size=\"2\" color=\"gray\">\n ∙\n </Text>\n <Text size=\"2\" color=\"gray\">\n <Translation\n defaultMessage=\"Last seen {timeAgo}\"\n id=\"gF+kUw\"\n description=\"Text showing when the session was last active\"\n values={{ timeAgo }}\n />\n </Text>\n </>\n )}\n </Flex>\n </Flex>\n\n {!isCurrentSession && (\n <Flex ml=\"auto\">\n <Button variant=\"secondary\" onClick={() => onSignOut?.(session)}>\n <Translation\n defaultMessage=\"Sign out\"\n id=\"sT1L78\"\n description=\"Button text to sign out of a session\"\n />\n </Button>\n </Flex>\n )}\n </Flex>\n );\n};\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"user-sessions\",\n widgetState: state,\n });\n}\n\nexport type { UserSessionsLoadingProps, UserSessionsErrorProps };\nexport { UserSessions, UserSessionsLoading, UserSessionsError };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkDM;AAhDN,oBAAsC;AACtC,sBAAwC;AACxC,mBAMO;AACP,2BAA6B;AAC7B,wCAAwC;AAExC,eAA0B;AAC1B,wBAA0B;AAC1B,YAAuB;AACvB,2BAA6B;AAC7B,yBAAuC;AACvC,yBAA4B;AAC5B,2CAA2C;AAO3C,MAAM,eAAe,CAAC;AAAA,EACpB,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,MAAyB;AACvB,QAAM,iBAAiB,SAAS;AAAA,IAC9B,CAAC,YAAY,QAAQ,OAAO;AAAA,EAC9B;AACA,QAAM,gBAAgB,SAAS;AAAA,IAC7B,CAAC,YAAY,QAAQ,OAAO;AAAA,EAC9B;AACA,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,QAAM,CAAC,kBAAkB,mBAAmB,IAC1C,MAAM,SAAuC,IAAI;AACnD,QAAM,CAAC,6BAA6B,8BAA8B,IAChE,MAAM,SAAS,KAAK;AAEtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,KAAI;AAAA,MACH,GAAG,sBAAsB,YAAY,QAAQ;AAAA,MAE9C;AAAA,qDAAC,SAAS,MAAT,EACE;AAAA,4BACC,4CAAC,SAAS,MAAT,EACC,sDAAC,eAAY,SAAS,gBAAgB,kBAAgB,MAAC,GACzD;AAAA,UAGD,cAAc,IAAI,CAAC,YAClB,4CAAC,SAAS,MAAT,EACC;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,WAAW,MAAM;AACf,oCAAoB,OAAO;AAC3B,oCAAoB,IAAI;AAAA,cAC1B;AAAA;AAAA,UACF,KAPkB,QAAQ,EAQ5B,CACD;AAAA,WACH;AAAA,QAEC,SAAS,SAAS,KACjB,4CAAC,sBAAK,MAAK,KACT,uDAAC,sBAAK,KAAI,KAAI,SAAQ,WAAU,OAAM,UACpC;AAAA,uDAAC,qBACC;AAAA,wDAAC,sBAAK,MAAK,KAAI,cAAY,MAAC,QAAO,QAAO,IAAG,KAC3C;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF;AAAA,YACA,4CAAC,sBAAK,MAAK,KAAI,OAAM,QAAO,IAAG,KAC7B;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF;AAAA,aACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,MAAM;AACb,+CAA+B,IAAI;AAAA,cACrC;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,gBAAe;AAAA,kBACf,IAAG;AAAA,kBACH,aAAY;AAAA;AAAA,cACd;AAAA;AAAA,UACF;AAAA,WACF,GACF;AAAA,QAGD,oBACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,MAAM;AAAA,YACN,cAAc;AAAA;AAAA,QAChB;AAAA,QAGF;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,cAAc;AAAA,YACd;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAIA,MAAM,sBAA0D,CAAC,UAAU;AACzE,SACE,4CAAC,sBAAK,MAAK,KAAK,GAAG,sBAAsB,WAAW,KAAK,GACvD,uDAAC,sBAAK,KAAI,KAAI,OAAM,UAClB;AAAA,gDAAC,4BACC,sDAAC,+BAAU,GACb;AAAA,IAEA,6CAAC,sBAAK,WAAU,UACd;AAAA,kDAAC,sBAAK,MAAK,KAAI,cAAY,MAAC,QAAO,QAAO,IAAG,KAAI,IAAG,QAClD,sDAAC,4BACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF,GACF;AAAA,MAEA,4CAAC,sBAAK,MAAK,KAAI,OAAM,QAAO,IAAG,KAC7B,sDAAC,4BACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF,GACF;AAAA,OACF;AAAA,KACF,GACF;AAEJ;AAMA,MAAM,oBAAsD,CAAC;AAAA,EAC3D;AAAA,EACA,GAAG;AACL,MAAM;AACJ,SACE,4CAAC,sBAAK,MAAK,KAAK,GAAG,sBAAsB,SAAS,QAAQ,GACxD,sDAAC,qCAAa,OAAc,GAC9B;AAEJ;AAQA,MAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA,mBAAmB;AAAA,EACnB;AACF,MAAwB;AACtB,QAAM,gBAAY,6BAAe,QAAQ,SAAS;AAClD,QAAM,mBAAe;AAAA,IACnB,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,cAAU,iEAA2B;AAAA,IACzC,MAAM,QAAQ,iBAAiB,IAAI,KAAK,QAAQ,cAAc,IAAI;AAAA,EACpE,CAAC;AAED,SACE,6CAAC,sBAAK,KAAI,KAAI,OAAM,UAClB;AAAA,gDAAC,+BACE,qBAAW,WAAW,4CAAC,iCAAW,IAAK,4CAAC,iCAAW,GACtD;AAAA,IAEA,6CAAC,sBAAK,WAAU,UACd;AAAA,kDAAC,sBAAK,MAAK,KAAI,cAAY,MAAC,QAAO,QAAO,IAAG,KAAI,IAAG,QACjD,qBAAW,UACV;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GAEJ;AAAA,MAEA,6CAAC,sBAAK,KAAI,KAAI,OAAM,UAClB;AAAA,oDAAC,sBAAK,MAAK,KAAI,OAAM,QAAO,IAAG,KAC5B,wBACH;AAAA,QAEC,oBACC,4CAAC,yBAAM,IAAG,KACR;AAAA,UAAC;AAAA;AAAA,YACC,gBAAe;AAAA,YACf,IAAG;AAAA,YACH,aAAY;AAAA;AAAA,QACd,GACF;AAAA,QAGD,CAAC,oBAAoB,QAAQ,kBAC5B,4EAEE;AAAA,sDAAC,sBAAK,MAAK,KAAI,OAAM,QAAO,oBAE5B;AAAA,UACA,4CAAC,sBAAK,MAAK,KAAI,OAAM,QACnB;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA,cACZ,QAAQ,EAAE,QAAQ;AAAA;AAAA,UACpB,GACF;AAAA,WACF;AAAA,SAEJ;AAAA,OACF;AAAA,IAEC,CAAC,oBACA,4CAAC,sBAAK,IAAG,QACP,sDAAC,0BAAO,SAAQ,aAAY,SAAS,MAAM,YAAY,OAAO,GAC5D;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF,GACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,sBACP,OACA,UACA;AACA,aAAO,0BAAY;AAAA,IACjB,GAAG;AAAA,IACH,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC;AACH;","names":[]}
@@ -1,6 +1,6 @@
1
- import * as React$1 from 'react';
2
1
  import { WidgetRootDomProps } from './utils.cjs';
3
2
  import { SessionFieldsFragment } from '../api/client-graphql/generated.cjs';
3
+ import * as React from 'react';
4
4
  import './api/types.cjs';
5
5
  import './elements.cjs';
6
6
  import '@radix-ui/themes';
@@ -19,7 +19,7 @@ interface UserSessionsProps extends WidgetRootDomProps {
19
19
  sessionsData: SessionFieldsFragment[];
20
20
  currentSessionId: string;
21
21
  }
22
- declare const UserSessions: ({ sessionsData: sessions, currentSessionId, ...domProps }: UserSessionsProps) => React$1.JSX.Element;
22
+ declare const UserSessions: ({ sessionsData: sessions, currentSessionId, ...domProps }: UserSessionsProps) => React.JSX.Element;
23
23
  interface UserSessionsLoadingProps extends WidgetRootDomProps {
24
24
  }
25
25
  declare const UserSessionsLoading: React.FC<UserSessionsLoadingProps>;
@@ -52,6 +52,7 @@ var import_react_icons = require("@radix-ui/react-icons");
52
52
  var import_translation = require("./i18n/translation.js");
53
53
  var import_use_translation = require("./i18n/use-translation.js");
54
54
  var import_use_locale = require("./i18n/use-locale.js");
55
+ var import_use_comparative_readable_date = require("../use-comparative-readable-date.js");
55
56
  const UsersManagement = ({
56
57
  userData,
57
58
  rolesData,
@@ -512,30 +513,19 @@ function LastActiveImpl({
512
513
  dim
513
514
  }) {
514
515
  const locale = (0, import_use_locale.useLocale)();
515
- const { lastActiveDateTime, lastActiveDisplay } = React.useMemo(() => {
516
- const defaultTimeZone = "America/Los_Angeles";
517
- const lastActiveDate = new Date(date);
518
- const lastActiveDateTime2 = lastActiveDate.toLocaleTimeString(locale, {
519
- // hard-coded timezone before hydration to prevent server/client mismatch
520
- timeZone: isHydrated ? void 0 : defaultTimeZone,
521
- month: "long",
522
- day: "numeric",
523
- year: "numeric",
524
- hour: "numeric",
525
- minute: "numeric"
526
- });
527
- const lastActiveDisplay2 = isHydrated ? (0, import_utils.getComparativeReadableDate)(/* @__PURE__ */ new Date(), lastActiveDate, { locale }) : lastActiveDate.toLocaleDateString(locale, {
528
- // hard-coded timezone to prevent server/client mismatch
529
- timeZone: defaultTimeZone,
530
- month: "long",
531
- day: "numeric",
532
- year: "numeric"
533
- });
534
- return {
535
- lastActiveDateTime: lastActiveDateTime2,
536
- lastActiveDisplay: lastActiveDisplay2
537
- };
538
- }, [isHydrated, date, locale]);
516
+ const lastActiveDate = new Date(date);
517
+ const lastActiveDateTime = lastActiveDate.toLocaleTimeString(locale, {
518
+ // hard-coded timezone before hydration to prevent server/client mismatch
519
+ timeZone: isHydrated ? void 0 : "America/Los_Angeles",
520
+ month: "long",
521
+ day: "numeric",
522
+ year: "numeric",
523
+ hour: "numeric",
524
+ minute: "numeric"
525
+ });
526
+ const lastActiveDisplay = (0, import_use_comparative_readable_date.useComparativeReadableDate)({
527
+ date: lastActiveDate
528
+ });
539
529
  if (lastActiveDisplay === "Invalid Date") {
540
530
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
541
531
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.VisuallyHidden, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/users-management.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport {\n Box,\n Flex,\n Grid,\n Separator,\n Table,\n Tooltip,\n Text,\n type TextProps,\n VisuallyHidden,\n} from \"@radix-ui/themes\";\nimport {\n Avatar,\n Badge,\n IconButton,\n Button,\n Select,\n Skeleton,\n TextField,\n} from \"./elements.js\";\nimport { InviteUserDialog } from \"./invite-user-dialog.js\";\nimport { SearchProvider, useSearchContext } from \"./search-provider.js\";\nimport { useIsHydrated } from \"./use-is-hydrated.js\";\nimport { UserActionsDropdown } from \"./user-actions-dropdown.js\";\nimport { UsersFilter } from \"./users-filter.js\";\nimport { UsersSearch } from \"./users-search.js\";\nimport {\n getBestName,\n getComparativeReadableDate,\n getDomProps,\n getListMetadata,\n WidgetRootState,\n type WidgetRootDomProps,\n} from \"./utils.js\";\nimport { USER_ROW_LIMIT } from \"./constants.js\";\nimport { useUsersManagementContext } from \"./users-management-context.js\";\nimport type { Member, MembersQueryResult, RolesAndConfig } from \"./api/user.js\";\nimport { GenericError } from \"./generic-error.js\";\nimport { DotsHorizontalIcon } from \"@radix-ui/react-icons\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useTranslation } from \"./i18n/use-translation.js\";\nimport { useLocale } from \"./i18n/use-locale.js\";\n\ninterface UsersManagementProps extends WidgetRootDomProps {\n rolesData: RolesAndConfig;\n userData: MembersQueryResult;\n disableRolesFilter?: boolean;\n // When the users list is loading new users\n isPending: boolean;\n}\n\nconst UsersManagement: React.FC<UsersManagementProps> = ({\n userData,\n rolesData,\n isPending,\n disableRolesFilter,\n ...domProps\n}) => {\n const users = userData?.data ?? [];\n const usersCount = users?.length ?? 0;\n const isHydrated = useIsHydrated();\n const pagination = getListMetadata(userData);\n\n const { dispatch } = useUsersManagementContext();\n const isMultipleRolesEnabled = rolesData.multipleRolesEnabled;\n const translate = useTranslation();\n\n // we only want to show the loading indicator for some buttons if the request\n // is still pending after 500ms. If the request is fast enough the indicator\n // is a bit jarring.\n const [deferredLoading, setDeferredLoading] = React.useState(false);\n React.useEffect(() => {\n if (isPending) {\n const timeoutId = window.setTimeout(() => {\n setDeferredLoading(true);\n }, 500);\n return () => {\n window.clearTimeout(timeoutId);\n };\n } else {\n setDeferredLoading(false);\n }\n }, [isPending]);\n\n const showPagination = !!(pagination.before || pagination.after);\n\n return (\n <SearchProvider>\n <Flex\n direction=\"column\"\n gap=\"3\"\n {...getWidgetRootDomProps(\"resolved\", domProps)}\n >\n <Grid columns=\"1fr auto\" gap=\"2\">\n <Flex gap=\"2\" align=\"center\">\n <Box flexBasis=\"380px\" flexGrow=\"0\" flexShrink=\"1\">\n <UsersSearch />\n </Box>\n <Box flexGrow=\"0\" flexShrink=\"0\">\n <UsersFilter\n roles={rolesData.roles}\n disabled={disableRolesFilter}\n />\n </Box>\n </Flex>\n <Box flexGrow=\"0\" flexShrink=\"0\" style={{ placeSelf: \"flex-end\" }}>\n <InviteUserDialog>\n <Button>\n <Translation\n defaultMessage=\"Invite user\"\n id=\"NBXpRj\"\n description=\"Button text to invite a new user to the organization\"\n />\n </Button>\n </InviteUserDialog>\n </Box>\n </Grid>\n <Table.Root variant=\"ghost\" size=\"1\">\n <Table.Header>\n <Table.Row>\n <Table.ColumnHeaderCell width=\"260px\">\n <Translation\n defaultMessage=\"User\"\n id=\"BBh86k\"\n description=\"Column header for user information in the users table\"\n />\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"100px\">\n {isMultipleRolesEnabled ? (\n <Translation\n defaultMessage=\"Roles\"\n id=\"A1DuWM\"\n description=\"Column header for user roles when multiple roles are enabled\"\n />\n ) : (\n <Translation\n defaultMessage=\"Role\"\n id=\"nQbe+Y\"\n description=\"Column header for user role when only single role is allowed\"\n />\n )}\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"140px\">\n <Translation\n defaultMessage=\"Last active\"\n id=\"oOwLGt\"\n description=\"Column header showing when users were last active\"\n />\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"28px\" />\n </Table.Row>\n </Table.Header>\n\n <Table.Body\n style={{\n transition: `opacity 0.2s ease-out ${isPending ? \"0.2s\" : \"0s\"}`,\n opacity: isPending && usersCount > 0 ? 0.5 : 1,\n }}\n >\n {users.length > 0 ? (\n users.map((user) => {\n const userDisplayName = getBestName(user);\n const dimText =\n user.status === \"InviteRevoked\" ||\n user.status === \"InviteExpired\";\n return (\n <Table.Row key={user.id} align=\"center\">\n <Table.RowHeaderCell>\n <Flex\n align=\"center\"\n gap=\"3\"\n overflow=\"hidden\"\n height=\"var(--space-7)\"\n >\n <Avatar\n size=\"2\"\n fallback={<FallbackUserIcon />}\n src={user.profilePictureUrl ?? undefined}\n dim={dimText}\n />\n\n {userDisplayName ? (\n <Flex\n direction=\"column\"\n align=\"start\"\n height=\"var(--space-7)\"\n justify=\"center\"\n overflow=\"hidden\"\n >\n <Flex gap=\"2\" align=\"center\" minWidth=\"0\">\n <TableCellText dim={dimText}>\n {userDisplayName}\n </TableCellText>\n <UserBadge user={user} />\n </Flex>\n <TableCellText\n level=\"secondary\"\n title={user.email}\n dim={dimText}\n >\n {user.email}\n </TableCellText>\n </Flex>\n ) : (\n <Flex gap=\"2\" align=\"center\" minWidth=\"0\">\n <TableCellText dim={dimText} title={user.email}>\n {user.email}\n </TableCellText>\n <UserBadge user={user} />\n </Flex>\n )}\n </Flex>\n </Table.RowHeaderCell>\n <Table.Cell>\n <UserRolesCellContent\n user={user}\n isMultipleRolesEnabled={isMultipleRolesEnabled}\n dimText={dimText}\n />\n </Table.Cell>\n <Table.Cell>\n <LastActive user={user} isHydrated={isHydrated} />\n </Table.Cell>\n <Table.Cell justify=\"end\">\n <UserActionsDropdown user={user}>\n <IconButton\n title={translate({\n defaultMessage: \"User actions\",\n id: \"1nsmNS\",\n description:\n \"Button tooltip for opening user actions menu\",\n })}\n >\n <DotsHorizontalIcon />\n </IconButton>\n </UserActionsDropdown>\n </Table.Cell>\n </Table.Row>\n );\n })\n ) : (\n <Table.Row align=\"center\">\n <Table.Cell colSpan={4}>\n <UsersManagementEmptyState isPending={isPending} />\n </Table.Cell>\n </Table.Row>\n )}\n </Table.Body>\n </Table.Root>\n\n {showPagination ? (\n <Flex gap=\"2\" justify=\"end\">\n <Button\n variant=\"secondary\"\n size=\"1\"\n disabled={!pagination.after || isPending || undefined}\n loading={deferredLoading}\n onClick={() => {\n if (pagination.after) {\n dispatch({\n type: \"SET_PAGINATION\",\n pagination: { after: pagination.after, before: null },\n });\n }\n }}\n >\n <Translation\n defaultMessage=\"Previous\"\n id=\"910Q6b\"\n description=\"Button text to navigate to the previous page of users\"\n />\n </Button>\n <Button\n variant=\"secondary\"\n size=\"1\"\n disabled={!pagination.before || isPending || undefined}\n loading={deferredLoading}\n onClick={() => {\n if (pagination.before) {\n dispatch({\n type: \"SET_PAGINATION\",\n pagination: { before: pagination.before, after: null },\n });\n }\n }}\n >\n <Translation\n defaultMessage=\"Next\"\n id=\"UlAY08\"\n description=\"Button text to navigate to the next page of users\"\n />\n </Button>\n </Flex>\n ) : null}\n </Flex>\n </SearchProvider>\n );\n};\n\ninterface UsersManagementLoadingProps extends WidgetRootDomProps {}\n\nconst UsersManagementLoading: React.FC<UsersManagementLoadingProps> = (\n props,\n) => {\n const translate = useTranslation();\n const placeholderText = (\n <Translation\n defaultMessage=\"All\"\n id=\"l/Do9/\"\n description=\"Filter option to show all users\"\n />\n );\n return (\n <Flex\n direction=\"column\"\n gap=\"3\"\n {...getWidgetRootDomProps(\"loading\", props)}\n >\n <Grid columns=\"1fr auto\" gap=\"2\">\n <Flex gap=\"2\" align=\"center\">\n <Skeleton loading>\n <Box flexBasis=\"380px\" flexGrow=\"0\" flexShrink=\"1\">\n <TextField\n disabled\n aria-label={translate({\n defaultMessage: \"User search\",\n id: \"lzPOKv\",\n description: \"Label for the user search input field\",\n })}\n />\n </Box>\n </Skeleton>\n <Skeleton loading>\n <Box flexGrow=\"0\" flexShrink=\"0\">\n <Select.Root disabled value=\"all\" onValueChange={() => void 0}>\n <Select.Trigger>{placeholderText}</Select.Trigger>\n <Select.Content>\n <Select.Item value=\"all\">{placeholderText}</Select.Item>\n </Select.Content>\n </Select.Root>\n </Box>\n </Skeleton>\n </Flex>\n <Skeleton loading>\n <Box flexGrow=\"0\" flexShrink=\"0\" style={{ placeSelf: \"flex-end\" }}>\n <Button disabled>\n <Translation\n defaultMessage=\"Invite user\"\n id=\"NBXpRj\"\n description=\"Button text to invite a new user to the organization\"\n />\n </Button>\n </Box>\n </Skeleton>\n </Grid>\n <Table.Root variant=\"ghost\" size=\"1\">\n <Table.Header>\n <Table.Row>\n <Table.ColumnHeaderCell width=\"260px\">\n <Skeleton loading>\n <Translation\n defaultMessage=\"User\"\n id=\"BBh86k\"\n description=\"Column header for user information in the users table\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"100px\">\n <Skeleton>\n <Translation\n defaultMessage=\"Role\"\n id=\"FK7mP4\"\n description=\"Column header for user role\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"140px\">\n <Skeleton>\n <Translation\n defaultMessage=\"Last active\"\n id=\"oOwLGt\"\n description=\"Column header showing when users were last active\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"28px\" />\n </Table.Row>\n </Table.Header>\n\n <Table.Body>\n {Array.from({ length: USER_ROW_LIMIT }, (_, index) => (\n <Table.Row key={index} align=\"center\">\n <Table.RowHeaderCell>\n <Flex align=\"center\" gap=\"3\">\n <Skeleton>\n <Avatar size=\"2\" fallback=\"F\" />\n </Skeleton>\n\n <Flex\n direction=\"column\"\n height=\"var(--space-7)\"\n justify=\"center\"\n >\n <Skeleton width=\"180px\" height=\"var(--space-4)\" />\n <Skeleton width=\"90px\" height=\"var(--space-3)\" mt=\"1\" />\n </Flex>\n </Flex>\n </Table.RowHeaderCell>\n <Table.Cell>\n <Flex wrap=\"wrap\" gap=\"1\">\n <Skeleton width=\"75px\" height=\"var(--space-4)\" />\n </Flex>\n </Table.Cell>\n <Table.Cell>\n <Skeleton width=\"120px\" height=\"var(--space-4)\" />\n </Table.Cell>\n <Table.Cell justify=\"end\" />\n </Table.Row>\n ))}\n </Table.Body>\n </Table.Root>\n\n <Flex gap=\"2\" justify=\"end\">\n <Skeleton loading>\n <Button disabled variant=\"secondary\" size=\"1\">\n <Translation\n defaultMessage=\"Previous\"\n id=\"910Q6b\"\n description=\"Button text to navigate to the previous page of users\"\n />\n </Button>\n </Skeleton>\n <Skeleton loading>\n <Button disabled variant=\"secondary\" size=\"1\">\n <Translation\n defaultMessage=\"Next\"\n id=\"UlAY08\"\n description=\"Button text to navigate to the next page of users\"\n />\n </Button>\n </Skeleton>\n </Flex>\n </Flex>\n );\n};\n\ninterface UsersManagementErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst UsersManagementError: React.FC<UsersManagementErrorProps> = ({\n error,\n ...domProps\n}) => {\n return (\n <GenericError\n error={error}\n minHeight=\"676px\"\n {...getWidgetRootDomProps(\"error\", domProps)}\n />\n );\n};\n\nfunction UserBadge({ user }: { user: Member }) {\n if (user.isLoggedInUser) {\n return (\n <Badge color=\"gray\" style={{ userSelect: \"none\" }}>\n <Translation\n defaultMessage=\"You\"\n id=\"ER1Vmv\"\n description=\"Badge indicating the current logged-in user\"\n />\n </Badge>\n );\n }\n if (user.status === \"Invited\") {\n return (\n <Badge color=\"amber\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: \"\n id=\"E2o/+H\"\n description=\"Prefix for status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Invited\"\n id=\"byIkUu\"\n description=\"Badge status indicating user has been invited but not yet accepted\"\n />\n </Badge>\n );\n }\n if (user.status === \"InviteExpired\") {\n return (\n <Badge color=\"red\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: Invite \"\n id=\"BWvbwY\"\n description=\"Prefix for invite status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Expired\"\n id=\"+0sQaN\"\n description=\"Badge status indicating user invitation has expired\"\n />\n </Badge>\n );\n }\n if (user.status === \"InviteRevoked\") {\n return (\n <Badge color=\"red\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: Invite \"\n id=\"BWvbwY\"\n description=\"Prefix for invite status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Revoked\"\n id=\"NNBv+r\"\n description=\"Badge status indicating user invitation has been revoked\"\n />\n </Badge>\n );\n }\n return null;\n}\n\ninterface LastActiveProps {\n user: Member;\n isHydrated: boolean;\n dim?: boolean;\n}\n\nfunction LastActive(props: LastActiveProps) {\n if (!props.user.lastActivityAt) {\n return (\n <>\n <VisuallyHidden>\n {props.user.status === \"Active\" ? (\n <Translation\n defaultMessage=\"Never\"\n id=\"0duvF4\"\n description=\"Screen reader text indicating user has never been active\"\n />\n ) : (\n <Translation\n defaultMessage=\"Not active\"\n id=\"hd2+kk\"\n description=\"Screen reader text indicating user is not active\"\n />\n )}\n </VisuallyHidden>\n <Separator />\n </>\n );\n }\n return <LastActiveImpl {...props} date={props.user.lastActivityAt} />;\n}\n\nfunction LastActiveImpl({\n date,\n isHydrated,\n dim,\n}: LastActiveProps & { date: string }) {\n const locale = useLocale();\n const { lastActiveDateTime, lastActiveDisplay } = React.useMemo(() => {\n const defaultTimeZone = \"America/Los_Angeles\";\n const lastActiveDate = new Date(date);\n const lastActiveDateTime = lastActiveDate.toLocaleTimeString(locale, {\n // hard-coded timezone before hydration to prevent server/client mismatch\n timeZone: isHydrated ? undefined : defaultTimeZone,\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n // Server and client may produce a different 'now' date, so only\n // show comparative date if the component is hydrated to prevent a\n // server/client mismatch\n const lastActiveDisplay = isHydrated\n ? getComparativeReadableDate(new Date(), lastActiveDate, { locale })\n : lastActiveDate.toLocaleDateString(locale, {\n // hard-coded timezone to prevent server/client mismatch\n timeZone: defaultTimeZone,\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n });\n\n return {\n lastActiveDateTime,\n lastActiveDisplay,\n };\n }, [isHydrated, date, locale]);\n\n // handle cases where the DB might return an invalid date string\n if (lastActiveDisplay === \"Invalid Date\") {\n return (\n <>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Unknown\"\n id=\"UUigrK\"\n description=\"Screen reader text indicating last active date is unknown\"\n />\n </VisuallyHidden>\n <Separator />\n </>\n );\n }\n\n return (\n <TableCellText asChild dim={dim}>\n <time dateTime={date} title={lastActiveDateTime}>\n {lastActiveDisplay}\n </time>\n </TableCellText>\n );\n}\n\nconst TableCellText = React.forwardRef<HTMLSpanElement, TableCellTextProps>(\n function TableCellText(\n { children, dim, level = \"primary\", ...props },\n forwardedRef,\n ) {\n return (\n <Text\n ref={forwardedRef}\n color={level === \"secondary\" ? \"gray\" : undefined}\n weight={level === \"secondary\" ? \"regular\" : \"medium\"}\n size={level === \"secondary\" ? \"1\" : \"2\"}\n truncate\n {...props}\n style={\n dim\n ? {\n // TODO: use CSS var instead of hard-coded value for opacity\n opacity: 0.6,\n ...props.style,\n }\n : props.style\n }\n >\n {children}\n </Text>\n );\n },\n);\n\ntype TableCellTextProps = TextProps & {\n level?: \"primary\" | \"secondary\";\n dim?: boolean;\n};\n\nconst FallbackUserIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n fill=\"currentColor\"\n viewBox=\"0 0 256 256\"\n >\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <title>User icon</title>\n <path d=\"M229.19,213c-15.81-27.32-40.63-46.49-69.47-54.62a70,70,0,1,0-63.44,0C67.44,166.5,42.62,185.67,26.81,213a6,6,0,1,0,10.38,6C56.4,185.81,90.34,166,128,166s71.6,19.81,90.81,53a6,6,0,1,0,10.38-6ZM70,96a58,58,0,1,1,58,58A58.07,58.07,0,0,1,70,96Z\" />\n </svg>\n);\n\nconst UsersManagementEmptyState = ({ isPending }: { isPending: boolean }) => {\n const { clearSearch } = useSearchContext();\n const {\n state: { searchQuery },\n } = useUsersManagementContext();\n\n // When the search query is cleared, the users query is re-fetched which sends\n // us into a pending state. When this happens we want to keep a snapshot of\n // the previous search query while the query is revalidated. We can use this\n // to keep the 'No users found for query' UI in place until re-fetching is\n // complete, otherwise the view flips to 'No users found' very quickly before\n // the full table is shown again.\n const [{ isClearing, lastSearchQuery }, setClearing] = React.useState({\n isClearing: false,\n lastSearchQuery: null as null | string,\n });\n const [wasPending, setWasPending] = React.useState(isPending);\n if (wasPending !== isPending) {\n setWasPending(isPending);\n if (!isPending) {\n setClearing((prev) =>\n prev.isClearing ? { isClearing: false, lastSearchQuery: null } : prev,\n );\n }\n }\n\n if (searchQuery || isClearing) {\n return (\n <Flex align=\"center\" justify=\"center\" py=\"8\" direction=\"column\" gap=\"2\">\n <Text size=\"2\">\n <Translation\n defaultMessage=\"No users found for query '{query}'\"\n id=\"wrEZ8+\"\n description=\"Message shown when no users match the search query\"\n values={{\n query: isClearing ? lastSearchQuery : searchQuery,\n }}\n />\n </Text>\n\n <Button\n variant=\"secondary\"\n size=\"1\"\n onClick={() => {\n setClearing({ isClearing: true, lastSearchQuery: searchQuery });\n clearSearch();\n }}\n loading={isPending}\n >\n <Translation\n defaultMessage=\"Clear search\"\n id=\"Xv3fVI\"\n description=\"Button text to clear the current search query\"\n />\n </Button>\n </Flex>\n );\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" py=\"8\" gap=\"2\">\n <Text size=\"2\">\n <Translation\n defaultMessage=\"No users found\"\n id=\"MQ+LoA\"\n description=\"Message shown when there are no users in the system\"\n />\n </Text>\n </Flex>\n );\n};\n\ninterface UserRolesCellContentProps {\n user: Member;\n isMultipleRolesEnabled: boolean;\n dimText: boolean;\n}\n\nfunction UserRolesCellContent({\n user,\n isMultipleRolesEnabled,\n dimText,\n}: UserRolesCellContentProps) {\n if (!user.roles || user.roles.length === 0) {\n return (\n <>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"No roles assigned\"\n id=\"CVOVB0\"\n description=\"Screen reader text indicating user has no roles assigned\"\n />\n </VisuallyHidden>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <span aria-hidden style={{ userSelect: \"none\" }}>\n –\n </span>\n </>\n );\n }\n\n if (isMultipleRolesEnabled && user.roles.length > 1) {\n const roleNames = user.roles?.map((role) => role.name).join(\", \");\n return (\n <Tooltip content={roleNames} maxWidth=\"250px\">\n <TableCellText dim={dimText}>\n <Translation\n defaultMessage=\"{count} roles\"\n id=\"lWAmRC\"\n description=\"Shows the number of roles assigned to a user (plural)\"\n values={{ count: user.roles.length }}\n />\n </TableCellText>\n </Tooltip>\n );\n }\n\n return <TableCellText dim={dimText}>{user.roles?.[0]?.name}</TableCellText>;\n}\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"users-management\",\n widgetState: state,\n });\n}\n\nexport type { UsersManagementLoadingProps, UsersManagementErrorProps };\nexport { UsersManagement, UsersManagementLoading, UsersManagementError };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiGU;AA/FV,YAAuB;AACvB,oBAUO;AACP,sBAQO;AACP,gCAAiC;AACjC,6BAAiD;AACjD,6BAA8B;AAC9B,mCAAoC;AACpC,0BAA4B;AAC5B,0BAA4B;AAC5B,mBAOO;AACP,uBAA+B;AAC/B,sCAA0C;AAE1C,2BAA6B;AAC7B,yBAAmC;AACnC,yBAA4B;AAC5B,6BAA+B;AAC/B,wBAA0B;AAU1B,MAAM,kBAAkD,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,QAAQ,UAAU,QAAQ,CAAC;AACjC,QAAM,aAAa,OAAO,UAAU;AACpC,QAAM,iBAAa,sCAAc;AACjC,QAAM,iBAAa,8BAAgB,QAAQ;AAE3C,QAAM,EAAE,SAAS,QAAI,2DAA0B;AAC/C,QAAM,yBAAyB,UAAU;AACzC,QAAM,gBAAY,uCAAe;AAKjC,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAClE,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW;AACb,YAAM,YAAY,OAAO,WAAW,MAAM;AACxC,2BAAmB,IAAI;AAAA,MACzB,GAAG,GAAG;AACN,aAAO,MAAM;AACX,eAAO,aAAa,SAAS;AAAA,MAC/B;AAAA,IACF,OAAO;AACL,yBAAmB,KAAK;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,iBAAiB,CAAC,EAAE,WAAW,UAAU,WAAW;AAE1D,SACE,4CAAC,yCACC;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,KAAI;AAAA,MACH,GAAG,sBAAsB,YAAY,QAAQ;AAAA,MAE9C;AAAA,qDAAC,sBAAK,SAAQ,YAAW,KAAI,KAC3B;AAAA,uDAAC,sBAAK,KAAI,KAAI,OAAM,UAClB;AAAA,wDAAC,qBAAI,WAAU,SAAQ,UAAS,KAAI,YAAW,KAC7C,sDAAC,mCAAY,GACf;AAAA,YACA,4CAAC,qBAAI,UAAS,KAAI,YAAW,KAC3B;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO,UAAU;AAAA,gBACjB,UAAU;AAAA;AAAA,YACZ,GACF;AAAA,aACF;AAAA,UACA,4CAAC,qBAAI,UAAS,KAAI,YAAW,KAAI,OAAO,EAAE,WAAW,WAAW,GAC9D,sDAAC,8CACC,sDAAC,0BACC;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd,GACF,GACF,GACF;AAAA,WACF;AAAA,QACA,6CAAC,oBAAM,MAAN,EAAW,SAAQ,SAAQ,MAAK,KAC/B;AAAA,sDAAC,oBAAM,QAAN,EACC,uDAAC,oBAAM,KAAN,EACC;AAAA,wDAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC5B;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC3B,mCACC;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,IAEA;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GAEJ;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC5B;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,QAAO;AAAA,aACvC,GACF;AAAA,UAEA;AAAA,YAAC,oBAAM;AAAA,YAAN;AAAA,cACC,OAAO;AAAA,gBACL,YAAY,yBAAyB,YAAY,SAAS,IAAI;AAAA,gBAC9D,SAAS,aAAa,aAAa,IAAI,MAAM;AAAA,cAC/C;AAAA,cAEC,gBAAM,SAAS,IACd,MAAM,IAAI,CAAC,SAAS;AAClB,sBAAM,sBAAkB,0BAAY,IAAI;AACxC,sBAAM,UACJ,KAAK,WAAW,mBAChB,KAAK,WAAW;AAClB,uBACE,6CAAC,oBAAM,KAAN,EAAwB,OAAM,UAC7B;AAAA,8DAAC,oBAAM,eAAN,EACC;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAM;AAAA,sBACN,KAAI;AAAA,sBACJ,UAAS;AAAA,sBACT,QAAO;AAAA,sBAEP;AAAA;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,UAAU,4CAAC,oBAAiB;AAAA,4BAC5B,KAAK,KAAK,qBAAqB;AAAA,4BAC/B,KAAK;AAAA;AAAA,wBACP;AAAA,wBAEC,kBACC;AAAA,0BAAC;AAAA;AAAA,4BACC,WAAU;AAAA,4BACV,OAAM;AAAA,4BACN,QAAO;AAAA,4BACP,SAAQ;AAAA,4BACR,UAAS;AAAA,4BAET;AAAA,2EAAC,sBAAK,KAAI,KAAI,OAAM,UAAS,UAAS,KACpC;AAAA,4EAAC,iBAAc,KAAK,SACjB,2BACH;AAAA,gCACA,4CAAC,aAAU,MAAY;AAAA,iCACzB;AAAA,8BACA;AAAA,gCAAC;AAAA;AAAA,kCACC,OAAM;AAAA,kCACN,OAAO,KAAK;AAAA,kCACZ,KAAK;AAAA,kCAEJ,eAAK;AAAA;AAAA,8BACR;AAAA;AAAA;AAAA,wBACF,IAEA,6CAAC,sBAAK,KAAI,KAAI,OAAM,UAAS,UAAS,KACpC;AAAA,sEAAC,iBAAc,KAAK,SAAS,OAAO,KAAK,OACtC,eAAK,OACR;AAAA,0BACA,4CAAC,aAAU,MAAY;AAAA,2BACzB;AAAA;AAAA;AAAA,kBAEJ,GACF;AAAA,kBACA,4CAAC,oBAAM,MAAN,EACC;AAAA,oBAAC;AAAA;AAAA,sBACC;AAAA,sBACA;AAAA,sBACA;AAAA;AAAA,kBACF,GACF;AAAA,kBACA,4CAAC,oBAAM,MAAN,EACC,sDAAC,cAAW,MAAY,YAAwB,GAClD;AAAA,kBACA,4CAAC,oBAAM,MAAN,EAAW,SAAQ,OAClB,sDAAC,oDAAoB,MACnB;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,UAAU;AAAA,wBACf,gBAAgB;AAAA,wBAChB,IAAI;AAAA,wBACJ,aACE;AAAA,sBACJ,CAAC;AAAA,sBAED,sDAAC,yCAAmB;AAAA;AAAA,kBACtB,GACF,GACF;AAAA,qBAtEc,KAAK,EAuErB;AAAA,cAEJ,CAAC,IAED,4CAAC,oBAAM,KAAN,EAAU,OAAM,UACf,sDAAC,oBAAM,MAAN,EAAW,SAAS,GACnB,sDAAC,6BAA0B,WAAsB,GACnD,GACF;AAAA;AAAA,UAEJ;AAAA,WACF;AAAA,QAEC,iBACC,6CAAC,sBAAK,KAAI,KAAI,SAAQ,OACpB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,UAAU,CAAC,WAAW,SAAS,aAAa;AAAA,cAC5C,SAAS;AAAA,cACT,SAAS,MAAM;AACb,oBAAI,WAAW,OAAO;AACpB,2BAAS;AAAA,oBACP,MAAM;AAAA,oBACN,YAAY,EAAE,OAAO,WAAW,OAAO,QAAQ,KAAK;AAAA,kBACtD,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,gBAAe;AAAA,kBACf,IAAG;AAAA,kBACH,aAAY;AAAA;AAAA,cACd;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,UAAU,CAAC,WAAW,UAAU,aAAa;AAAA,cAC7C,SAAS;AAAA,cACT,SAAS,MAAM;AACb,oBAAI,WAAW,QAAQ;AACrB,2BAAS;AAAA,oBACP,MAAM;AAAA,oBACN,YAAY,EAAE,QAAQ,WAAW,QAAQ,OAAO,KAAK;AAAA,kBACvD,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,gBAAe;AAAA,kBACf,IAAG;AAAA,kBACH,aAAY;AAAA;AAAA,cACd;AAAA;AAAA,UACF;AAAA,WACF,IACE;AAAA;AAAA;AAAA,EACN,GACF;AAEJ;AAIA,MAAM,yBAAgE,CACpE,UACG;AACH,QAAM,gBAAY,uCAAe;AACjC,QAAM,kBACJ;AAAA,IAAC;AAAA;AAAA,MACC,gBAAe;AAAA,MACf,IAAG;AAAA,MACH,aAAY;AAAA;AAAA,EACd;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,KAAI;AAAA,MACH,GAAG,sBAAsB,WAAW,KAAK;AAAA,MAE1C;AAAA,qDAAC,sBAAK,SAAQ,YAAW,KAAI,KAC3B;AAAA,uDAAC,sBAAK,KAAI,KAAI,OAAM,UAClB;AAAA,wDAAC,4BAAS,SAAO,MACf,sDAAC,qBAAI,WAAU,SAAQ,UAAS,KAAI,YAAW,KAC7C;AAAA,cAAC;AAAA;AAAA,gBACC,UAAQ;AAAA,gBACR,cAAY,UAAU;AAAA,kBACpB,gBAAgB;AAAA,kBAChB,IAAI;AAAA,kBACJ,aAAa;AAAA,gBACf,CAAC;AAAA;AAAA,YACH,GACF,GACF;AAAA,YACA,4CAAC,4BAAS,SAAO,MACf,sDAAC,qBAAI,UAAS,KAAI,YAAW,KAC3B,uDAAC,uBAAO,MAAP,EAAY,UAAQ,MAAC,OAAM,OAAM,eAAe,MAAM,QACrD;AAAA,0DAAC,uBAAO,SAAP,EAAgB,2BAAgB;AAAA,cACjC,4CAAC,uBAAO,SAAP,EACC,sDAAC,uBAAO,MAAP,EAAY,OAAM,OAAO,2BAAgB,GAC5C;AAAA,eACF,GACF,GACF;AAAA,aACF;AAAA,UACA,4CAAC,4BAAS,SAAO,MACf,sDAAC,qBAAI,UAAS,KAAI,YAAW,KAAI,OAAO,EAAE,WAAW,WAAW,GAC9D,sDAAC,0BAAO,UAAQ,MACd;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd,GACF,GACF,GACF;AAAA,WACF;AAAA,QACA,6CAAC,oBAAM,MAAN,EAAW,SAAQ,SAAQ,MAAK,KAC/B;AAAA,sDAAC,oBAAM,QAAN,EACC,uDAAC,oBAAM,KAAN,EACC;AAAA,wDAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC5B,sDAAC,4BAAS,SAAO,MACf;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF,GACF;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC5B,sDAAC,4BACC;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF,GACF;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,SAC5B,sDAAC,4BACC;AAAA,cAAC;AAAA;AAAA,gBACC,gBAAe;AAAA,gBACf,IAAG;AAAA,gBACH,aAAY;AAAA;AAAA,YACd,GACF,GACF;AAAA,YACA,4CAAC,oBAAM,kBAAN,EAAuB,OAAM,QAAO;AAAA,aACvC,GACF;AAAA,UAEA,4CAAC,oBAAM,MAAN,EACE,gBAAM,KAAK,EAAE,QAAQ,gCAAe,GAAG,CAAC,GAAG,UAC1C,6CAAC,oBAAM,KAAN,EAAsB,OAAM,UAC3B;AAAA,wDAAC,oBAAM,eAAN,EACC,uDAAC,sBAAK,OAAM,UAAS,KAAI,KACvB;AAAA,0DAAC,4BACC,sDAAC,0BAAO,MAAK,KAAI,UAAS,KAAI,GAChC;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,QAAO;AAAA,kBACP,SAAQ;AAAA,kBAER;AAAA,gEAAC,4BAAS,OAAM,SAAQ,QAAO,kBAAiB;AAAA,oBAChD,4CAAC,4BAAS,OAAM,QAAO,QAAO,kBAAiB,IAAG,KAAI;AAAA;AAAA;AAAA,cACxD;AAAA,eACF,GACF;AAAA,YACA,4CAAC,oBAAM,MAAN,EACC,sDAAC,sBAAK,MAAK,QAAO,KAAI,KACpB,sDAAC,4BAAS,OAAM,QAAO,QAAO,kBAAiB,GACjD,GACF;AAAA,YACA,4CAAC,oBAAM,MAAN,EACC,sDAAC,4BAAS,OAAM,SAAQ,QAAO,kBAAiB,GAClD;AAAA,YACA,4CAAC,oBAAM,MAAN,EAAW,SAAQ,OAAM;AAAA,eAzBZ,KA0BhB,CACD,GACH;AAAA,WACF;AAAA,QAEA,6CAAC,sBAAK,KAAI,KAAI,SAAQ,OACpB;AAAA,sDAAC,4BAAS,SAAO,MACf,sDAAC,0BAAO,UAAQ,MAAC,SAAQ,aAAY,MAAK,KACxC;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd,GACF,GACF;AAAA,UACA,4CAAC,4BAAS,SAAO,MACf,sDAAC,0BAAO,UAAQ,MAAC,SAAQ,aAAY,MAAK,KACxC;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd,GACF,GACF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAMA,MAAM,uBAA4D,CAAC;AAAA,EACjE;AAAA,EACA,GAAG;AACL,MAAM;AACJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACT,GAAG,sBAAsB,SAAS,QAAQ;AAAA;AAAA,EAC7C;AAEJ;AAEA,SAAS,UAAU,EAAE,KAAK,GAAqB;AAC7C,MAAI,KAAK,gBAAgB;AACvB,WACE,4CAAC,yBAAM,OAAM,QAAO,OAAO,EAAE,YAAY,OAAO,GAC9C;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA;AAAA,IACd,GACF;AAAA,EAEJ;AACA,MAAI,KAAK,WAAW,WAAW;AAC7B,WACE,6CAAC,yBAAM,OAAM,SAAQ,OAAO,EAAE,YAAY,OAAO,GAC/C;AAAA,kDAAC,gCACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd;AAAA,OACF;AAAA,EAEJ;AACA,MAAI,KAAK,WAAW,iBAAiB;AACnC,WACE,6CAAC,yBAAM,OAAM,OAAM,OAAO,EAAE,YAAY,OAAO,GAC7C;AAAA,kDAAC,gCACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd;AAAA,OACF;AAAA,EAEJ;AACA,MAAI,KAAK,WAAW,iBAAiB;AACnC,WACE,6CAAC,yBAAM,OAAM,OAAM,OAAO,EAAE,YAAY,OAAO,GAC7C;AAAA,kDAAC,gCACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd;AAAA,OACF;AAAA,EAEJ;AACA,SAAO;AACT;AAQA,SAAS,WAAW,OAAwB;AAC1C,MAAI,CAAC,MAAM,KAAK,gBAAgB;AAC9B,WACE,4EACE;AAAA,kDAAC,gCACE,gBAAM,KAAK,WAAW,WACrB;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,IAEA;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GAEJ;AAAA,MACA,4CAAC,2BAAU;AAAA,OACb;AAAA,EAEJ;AACA,SAAO,4CAAC,kBAAgB,GAAG,OAAO,MAAM,MAAM,KAAK,gBAAgB;AACrE;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF,GAAuC;AACrC,QAAM,aAAS,6BAAU;AACzB,QAAM,EAAE,oBAAoB,kBAAkB,IAAI,MAAM,QAAQ,MAAM;AACpE,UAAM,kBAAkB;AACxB,UAAM,iBAAiB,IAAI,KAAK,IAAI;AACpC,UAAMA,sBAAqB,eAAe,mBAAmB,QAAQ;AAAA;AAAA,MAEnE,UAAU,aAAa,SAAY;AAAA,MACnC,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AAKD,UAAMC,qBAAoB,iBACtB,yCAA2B,oBAAI,KAAK,GAAG,gBAAgB,EAAE,OAAO,CAAC,IACjE,eAAe,mBAAmB,QAAQ;AAAA;AAAA,MAExC,UAAU;AAAA,MACV,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,IACR,CAAC;AAEL,WAAO;AAAA,MACL,oBAAAD;AAAA,MACA,mBAAAC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,YAAY,MAAM,MAAM,CAAC;AAG7B,MAAI,sBAAsB,gBAAgB;AACxC,WACE,4EACE;AAAA,kDAAC,gCACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF;AAAA,MACA,4CAAC,2BAAU;AAAA,OACb;AAAA,EAEJ;AAEA,SACE,4CAAC,iBAAc,SAAO,MAAC,KACrB,sDAAC,UAAK,UAAU,MAAM,OAAO,oBAC1B,6BACH,GACF;AAEJ;AAEA,MAAM,gBAAgB,MAAM;AAAA,EAC1B,SAASC,eACP,EAAE,UAAU,KAAK,QAAQ,WAAW,GAAG,MAAM,GAC7C,cACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,OAAO,UAAU,cAAc,SAAS;AAAA,QACxC,QAAQ,UAAU,cAAc,YAAY;AAAA,QAC5C,MAAM,UAAU,cAAc,MAAM;AAAA,QACpC,UAAQ;AAAA,QACP,GAAG;AAAA,QACJ,OACE,MACI;AAAA;AAAA,UAEE,SAAS;AAAA,UACT,GAAG,MAAM;AAAA,QACX,IACA,MAAM;AAAA,QAGX;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAOA,MAAM,mBAAmB,MACvB;AAAA,EAAC;AAAA;AAAA,IACC,OAAM;AAAA,IACN,OAAM;AAAA,IACN,QAAO;AAAA,IACP,MAAK;AAAA,IACL,SAAQ;AAAA,IAGR;AAAA,kDAAC,WAAM,uBAAS;AAAA,MAChB,4CAAC,UAAK,GAAE,mPAAkP;AAAA;AAAA;AAC5P;AAGF,MAAM,4BAA4B,CAAC,EAAE,UAAU,MAA8B;AAC3E,QAAM,EAAE,YAAY,QAAI,yCAAiB;AACzC,QAAM;AAAA,IACJ,OAAO,EAAE,YAAY;AAAA,EACvB,QAAI,2DAA0B;AAQ9B,QAAM,CAAC,EAAE,YAAY,gBAAgB,GAAG,WAAW,IAAI,MAAM,SAAS;AAAA,IACpE,YAAY;AAAA,IACZ,iBAAiB;AAAA,EACnB,CAAC;AACD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,SAAS;AAC5D,MAAI,eAAe,WAAW;AAC5B,kBAAc,SAAS;AACvB,QAAI,CAAC,WAAW;AACd;AAAA,QAAY,CAAC,SACX,KAAK,aAAa,EAAE,YAAY,OAAO,iBAAiB,KAAK,IAAI;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,MAAI,eAAe,YAAY;AAC7B,WACE,6CAAC,sBAAK,OAAM,UAAS,SAAQ,UAAS,IAAG,KAAI,WAAU,UAAS,KAAI,KAClE;AAAA,kDAAC,sBAAK,MAAK,KACT;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA,UACZ,QAAQ;AAAA,YACN,OAAO,aAAa,kBAAkB;AAAA,UACxC;AAAA;AAAA,MACF,GACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS,MAAM;AACb,wBAAY,EAAE,YAAY,MAAM,iBAAiB,YAAY,CAAC;AAC9D,wBAAY;AAAA,UACd;AAAA,UACA,SAAS;AAAA,UAET;AAAA,YAAC;AAAA;AAAA,cACC,gBAAe;AAAA,cACf,IAAG;AAAA,cACH,aAAY;AAAA;AAAA,UACd;AAAA;AAAA,MACF;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,4CAAC,sBAAK,OAAM,UAAS,SAAQ,UAAS,IAAG,KAAI,KAAI,KAC/C,sDAAC,sBAAK,MAAK,KACT;AAAA,IAAC;AAAA;AAAA,MACC,gBAAe;AAAA,MACf,IAAG;AAAA,MACH,aAAY;AAAA;AAAA,EACd,GACF,GACF;AAEJ;AAQA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,MAAI,CAAC,KAAK,SAAS,KAAK,MAAM,WAAW,GAAG;AAC1C,WACE,4EACE;AAAA,kDAAC,gCACC;AAAA,QAAC;AAAA;AAAA,UACC,gBAAe;AAAA,UACf,IAAG;AAAA,UACH,aAAY;AAAA;AAAA,MACd,GACF;AAAA,MAEA,4CAAC,UAAK,eAAW,MAAC,OAAO,EAAE,YAAY,OAAO,GAAG,oBAEjD;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,0BAA0B,KAAK,MAAM,SAAS,GAAG;AACnD,UAAM,YAAY,KAAK,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,KAAK,IAAI;AAChE,WACE,4CAAC,yBAAQ,SAAS,WAAW,UAAS,SACpC,sDAAC,iBAAc,KAAK,SAClB;AAAA,MAAC;AAAA;AAAA,QACC,gBAAe;AAAA,QACf,IAAG;AAAA,QACH,aAAY;AAAA,QACZ,QAAQ,EAAE,OAAO,KAAK,MAAM,OAAO;AAAA;AAAA,IACrC,GACF,GACF;AAAA,EAEJ;AAEA,SAAO,4CAAC,iBAAc,KAAK,SAAU,eAAK,QAAQ,CAAC,GAAG,MAAK;AAC7D;AAEA,SAAS,sBACP,OACA,UACA;AACA,aAAO,0BAAY;AAAA,IACjB,GAAG;AAAA,IACH,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC;AACH;","names":["lastActiveDateTime","lastActiveDisplay","TableCellText"]}
1
+ {"version":3,"sources":["../../../src/lib/users-management.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport {\n Box,\n Flex,\n Grid,\n Separator,\n Table,\n Tooltip,\n Text,\n type TextProps,\n VisuallyHidden,\n} from \"@radix-ui/themes\";\nimport {\n Avatar,\n Badge,\n IconButton,\n Button,\n Select,\n Skeleton,\n TextField,\n} from \"./elements.js\";\nimport { InviteUserDialog } from \"./invite-user-dialog.js\";\nimport { SearchProvider, useSearchContext } from \"./search-provider.js\";\nimport { useIsHydrated } from \"./use-is-hydrated.js\";\nimport { UserActionsDropdown } from \"./user-actions-dropdown.js\";\nimport { UsersFilter } from \"./users-filter.js\";\nimport { UsersSearch } from \"./users-search.js\";\nimport {\n getBestName,\n getDomProps,\n getListMetadata,\n WidgetRootState,\n type WidgetRootDomProps,\n} from \"./utils.js\";\nimport { USER_ROW_LIMIT } from \"./constants.js\";\nimport { useUsersManagementContext } from \"./users-management-context.js\";\nimport type { Member, MembersQueryResult, RolesAndConfig } from \"./api/user.js\";\nimport { GenericError } from \"./generic-error.js\";\nimport { DotsHorizontalIcon } from \"@radix-ui/react-icons\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useTranslation } from \"./i18n/use-translation.js\";\nimport { useLocale } from \"./i18n/use-locale.js\";\nimport { useComparativeReadableDate } from \"../use-comparative-readable-date.js\";\n\ninterface UsersManagementProps extends WidgetRootDomProps {\n rolesData: RolesAndConfig;\n userData: MembersQueryResult;\n disableRolesFilter?: boolean;\n // When the users list is loading new users\n isPending: boolean;\n}\n\nconst UsersManagement: React.FC<UsersManagementProps> = ({\n userData,\n rolesData,\n isPending,\n disableRolesFilter,\n ...domProps\n}) => {\n const users = userData?.data ?? [];\n const usersCount = users?.length ?? 0;\n const isHydrated = useIsHydrated();\n const pagination = getListMetadata(userData);\n\n const { dispatch } = useUsersManagementContext();\n const isMultipleRolesEnabled = rolesData.multipleRolesEnabled;\n const translate = useTranslation();\n\n // we only want to show the loading indicator for some buttons if the request\n // is still pending after 500ms. If the request is fast enough the indicator\n // is a bit jarring.\n const [deferredLoading, setDeferredLoading] = React.useState(false);\n React.useEffect(() => {\n if (isPending) {\n const timeoutId = window.setTimeout(() => {\n setDeferredLoading(true);\n }, 500);\n return () => {\n window.clearTimeout(timeoutId);\n };\n } else {\n // TODO: Fix this\n // oxlint-disable-next-line react/set-state-in-effect\n setDeferredLoading(false);\n }\n }, [isPending]);\n\n const showPagination = !!(pagination.before || pagination.after);\n\n return (\n <SearchProvider>\n <Flex\n direction=\"column\"\n gap=\"3\"\n {...getWidgetRootDomProps(\"resolved\", domProps)}\n >\n <Grid columns=\"1fr auto\" gap=\"2\">\n <Flex gap=\"2\" align=\"center\">\n <Box flexBasis=\"380px\" flexGrow=\"0\" flexShrink=\"1\">\n <UsersSearch />\n </Box>\n <Box flexGrow=\"0\" flexShrink=\"0\">\n <UsersFilter\n roles={rolesData.roles}\n disabled={disableRolesFilter}\n />\n </Box>\n </Flex>\n <Box flexGrow=\"0\" flexShrink=\"0\" style={{ placeSelf: \"flex-end\" }}>\n <InviteUserDialog>\n <Button>\n <Translation\n defaultMessage=\"Invite user\"\n id=\"NBXpRj\"\n description=\"Button text to invite a new user to the organization\"\n />\n </Button>\n </InviteUserDialog>\n </Box>\n </Grid>\n <Table.Root variant=\"ghost\" size=\"1\">\n <Table.Header>\n <Table.Row>\n <Table.ColumnHeaderCell width=\"260px\">\n <Translation\n defaultMessage=\"User\"\n id=\"BBh86k\"\n description=\"Column header for user information in the users table\"\n />\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"100px\">\n {isMultipleRolesEnabled ? (\n <Translation\n defaultMessage=\"Roles\"\n id=\"A1DuWM\"\n description=\"Column header for user roles when multiple roles are enabled\"\n />\n ) : (\n <Translation\n defaultMessage=\"Role\"\n id=\"nQbe+Y\"\n description=\"Column header for user role when only single role is allowed\"\n />\n )}\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"140px\">\n <Translation\n defaultMessage=\"Last active\"\n id=\"oOwLGt\"\n description=\"Column header showing when users were last active\"\n />\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"28px\" />\n </Table.Row>\n </Table.Header>\n\n <Table.Body\n style={{\n transition: `opacity 0.2s ease-out ${isPending ? \"0.2s\" : \"0s\"}`,\n opacity: isPending && usersCount > 0 ? 0.5 : 1,\n }}\n >\n {users.length > 0 ? (\n users.map((user) => {\n const userDisplayName = getBestName(user);\n const dimText =\n user.status === \"InviteRevoked\" ||\n user.status === \"InviteExpired\";\n return (\n <Table.Row key={user.id} align=\"center\">\n <Table.RowHeaderCell>\n <Flex\n align=\"center\"\n gap=\"3\"\n overflow=\"hidden\"\n height=\"var(--space-7)\"\n >\n <Avatar\n size=\"2\"\n fallback={<FallbackUserIcon />}\n src={user.profilePictureUrl ?? undefined}\n dim={dimText}\n />\n\n {userDisplayName ? (\n <Flex\n direction=\"column\"\n align=\"start\"\n height=\"var(--space-7)\"\n justify=\"center\"\n overflow=\"hidden\"\n >\n <Flex gap=\"2\" align=\"center\" minWidth=\"0\">\n <TableCellText dim={dimText}>\n {userDisplayName}\n </TableCellText>\n <UserBadge user={user} />\n </Flex>\n <TableCellText\n level=\"secondary\"\n title={user.email}\n dim={dimText}\n >\n {user.email}\n </TableCellText>\n </Flex>\n ) : (\n <Flex gap=\"2\" align=\"center\" minWidth=\"0\">\n <TableCellText dim={dimText} title={user.email}>\n {user.email}\n </TableCellText>\n <UserBadge user={user} />\n </Flex>\n )}\n </Flex>\n </Table.RowHeaderCell>\n <Table.Cell>\n <UserRolesCellContent\n user={user}\n isMultipleRolesEnabled={isMultipleRolesEnabled}\n dimText={dimText}\n />\n </Table.Cell>\n <Table.Cell>\n <LastActive user={user} isHydrated={isHydrated} />\n </Table.Cell>\n <Table.Cell justify=\"end\">\n <UserActionsDropdown user={user}>\n <IconButton\n title={translate({\n defaultMessage: \"User actions\",\n id: \"1nsmNS\",\n description:\n \"Button tooltip for opening user actions menu\",\n })}\n >\n <DotsHorizontalIcon />\n </IconButton>\n </UserActionsDropdown>\n </Table.Cell>\n </Table.Row>\n );\n })\n ) : (\n <Table.Row align=\"center\">\n <Table.Cell colSpan={4}>\n <UsersManagementEmptyState isPending={isPending} />\n </Table.Cell>\n </Table.Row>\n )}\n </Table.Body>\n </Table.Root>\n\n {showPagination ? (\n <Flex gap=\"2\" justify=\"end\">\n <Button\n variant=\"secondary\"\n size=\"1\"\n disabled={!pagination.after || isPending || undefined}\n loading={deferredLoading}\n onClick={() => {\n if (pagination.after) {\n dispatch({\n type: \"SET_PAGINATION\",\n pagination: { after: pagination.after, before: null },\n });\n }\n }}\n >\n <Translation\n defaultMessage=\"Previous\"\n id=\"910Q6b\"\n description=\"Button text to navigate to the previous page of users\"\n />\n </Button>\n <Button\n variant=\"secondary\"\n size=\"1\"\n disabled={!pagination.before || isPending || undefined}\n loading={deferredLoading}\n onClick={() => {\n if (pagination.before) {\n dispatch({\n type: \"SET_PAGINATION\",\n pagination: { before: pagination.before, after: null },\n });\n }\n }}\n >\n <Translation\n defaultMessage=\"Next\"\n id=\"UlAY08\"\n description=\"Button text to navigate to the next page of users\"\n />\n </Button>\n </Flex>\n ) : null}\n </Flex>\n </SearchProvider>\n );\n};\n\ninterface UsersManagementLoadingProps extends WidgetRootDomProps {}\n\nconst UsersManagementLoading: React.FC<UsersManagementLoadingProps> = (\n props,\n) => {\n const translate = useTranslation();\n const placeholderText = (\n <Translation\n defaultMessage=\"All\"\n id=\"l/Do9/\"\n description=\"Filter option to show all users\"\n />\n );\n return (\n <Flex\n direction=\"column\"\n gap=\"3\"\n {...getWidgetRootDomProps(\"loading\", props)}\n >\n <Grid columns=\"1fr auto\" gap=\"2\">\n <Flex gap=\"2\" align=\"center\">\n <Skeleton loading>\n <Box flexBasis=\"380px\" flexGrow=\"0\" flexShrink=\"1\">\n <TextField\n disabled\n aria-label={translate({\n defaultMessage: \"User search\",\n id: \"lzPOKv\",\n description: \"Label for the user search input field\",\n })}\n />\n </Box>\n </Skeleton>\n <Skeleton loading>\n <Box flexGrow=\"0\" flexShrink=\"0\">\n <Select.Root disabled value=\"all\" onValueChange={() => void 0}>\n <Select.Trigger>{placeholderText}</Select.Trigger>\n <Select.Content>\n <Select.Item value=\"all\">{placeholderText}</Select.Item>\n </Select.Content>\n </Select.Root>\n </Box>\n </Skeleton>\n </Flex>\n <Skeleton loading>\n <Box flexGrow=\"0\" flexShrink=\"0\" style={{ placeSelf: \"flex-end\" }}>\n <Button disabled>\n <Translation\n defaultMessage=\"Invite user\"\n id=\"NBXpRj\"\n description=\"Button text to invite a new user to the organization\"\n />\n </Button>\n </Box>\n </Skeleton>\n </Grid>\n <Table.Root variant=\"ghost\" size=\"1\">\n <Table.Header>\n <Table.Row>\n <Table.ColumnHeaderCell width=\"260px\">\n <Skeleton loading>\n <Translation\n defaultMessage=\"User\"\n id=\"BBh86k\"\n description=\"Column header for user information in the users table\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"100px\">\n <Skeleton>\n <Translation\n defaultMessage=\"Role\"\n id=\"FK7mP4\"\n description=\"Column header for user role\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"140px\">\n <Skeleton>\n <Translation\n defaultMessage=\"Last active\"\n id=\"oOwLGt\"\n description=\"Column header showing when users were last active\"\n />\n </Skeleton>\n </Table.ColumnHeaderCell>\n <Table.ColumnHeaderCell width=\"28px\" />\n </Table.Row>\n </Table.Header>\n\n <Table.Body>\n {Array.from({ length: USER_ROW_LIMIT }, (_, index) => (\n <Table.Row key={index} align=\"center\">\n <Table.RowHeaderCell>\n <Flex align=\"center\" gap=\"3\">\n <Skeleton>\n <Avatar size=\"2\" fallback=\"F\" />\n </Skeleton>\n\n <Flex\n direction=\"column\"\n height=\"var(--space-7)\"\n justify=\"center\"\n >\n <Skeleton width=\"180px\" height=\"var(--space-4)\" />\n <Skeleton width=\"90px\" height=\"var(--space-3)\" mt=\"1\" />\n </Flex>\n </Flex>\n </Table.RowHeaderCell>\n <Table.Cell>\n <Flex wrap=\"wrap\" gap=\"1\">\n <Skeleton width=\"75px\" height=\"var(--space-4)\" />\n </Flex>\n </Table.Cell>\n <Table.Cell>\n <Skeleton width=\"120px\" height=\"var(--space-4)\" />\n </Table.Cell>\n <Table.Cell justify=\"end\" />\n </Table.Row>\n ))}\n </Table.Body>\n </Table.Root>\n\n <Flex gap=\"2\" justify=\"end\">\n <Skeleton loading>\n <Button disabled variant=\"secondary\" size=\"1\">\n <Translation\n defaultMessage=\"Previous\"\n id=\"910Q6b\"\n description=\"Button text to navigate to the previous page of users\"\n />\n </Button>\n </Skeleton>\n <Skeleton loading>\n <Button disabled variant=\"secondary\" size=\"1\">\n <Translation\n defaultMessage=\"Next\"\n id=\"UlAY08\"\n description=\"Button text to navigate to the next page of users\"\n />\n </Button>\n </Skeleton>\n </Flex>\n </Flex>\n );\n};\n\ninterface UsersManagementErrorProps extends WidgetRootDomProps {\n error: unknown;\n}\n\nconst UsersManagementError: React.FC<UsersManagementErrorProps> = ({\n error,\n ...domProps\n}) => {\n return (\n <GenericError\n error={error}\n minHeight=\"676px\"\n {...getWidgetRootDomProps(\"error\", domProps)}\n />\n );\n};\n\nfunction UserBadge({ user }: { user: Member }) {\n if (user.isLoggedInUser) {\n return (\n <Badge color=\"gray\" style={{ userSelect: \"none\" }}>\n <Translation\n defaultMessage=\"You\"\n id=\"ER1Vmv\"\n description=\"Badge indicating the current logged-in user\"\n />\n </Badge>\n );\n }\n if (user.status === \"Invited\") {\n return (\n <Badge color=\"amber\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: \"\n id=\"E2o/+H\"\n description=\"Prefix for status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Invited\"\n id=\"byIkUu\"\n description=\"Badge status indicating user has been invited but not yet accepted\"\n />\n </Badge>\n );\n }\n if (user.status === \"InviteExpired\") {\n return (\n <Badge color=\"red\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: Invite \"\n id=\"BWvbwY\"\n description=\"Prefix for invite status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Expired\"\n id=\"+0sQaN\"\n description=\"Badge status indicating user invitation has expired\"\n />\n </Badge>\n );\n }\n if (user.status === \"InviteRevoked\") {\n return (\n <Badge color=\"red\" style={{ userSelect: \"none\" }}>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Status: Invite \"\n id=\"BWvbwY\"\n description=\"Prefix for invite status indicator in screen reader\"\n />\n </VisuallyHidden>\n <Translation\n defaultMessage=\"Revoked\"\n id=\"NNBv+r\"\n description=\"Badge status indicating user invitation has been revoked\"\n />\n </Badge>\n );\n }\n return null;\n}\n\ninterface LastActiveProps {\n user: Member;\n isHydrated: boolean;\n dim?: boolean;\n}\n\nfunction LastActive(props: LastActiveProps) {\n if (!props.user.lastActivityAt) {\n return (\n <>\n <VisuallyHidden>\n {props.user.status === \"Active\" ? (\n <Translation\n defaultMessage=\"Never\"\n id=\"0duvF4\"\n description=\"Screen reader text indicating user has never been active\"\n />\n ) : (\n <Translation\n defaultMessage=\"Not active\"\n id=\"hd2+kk\"\n description=\"Screen reader text indicating user is not active\"\n />\n )}\n </VisuallyHidden>\n <Separator />\n </>\n );\n }\n return <LastActiveImpl {...props} date={props.user.lastActivityAt} />;\n}\n\nfunction LastActiveImpl({\n date,\n isHydrated,\n dim,\n}: LastActiveProps & { date: string }) {\n const locale = useLocale();\n\n const lastActiveDate = new Date(date);\n const lastActiveDateTime = lastActiveDate.toLocaleTimeString(locale, {\n // hard-coded timezone before hydration to prevent server/client mismatch\n timeZone: isHydrated ? undefined : \"America/Los_Angeles\",\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n const lastActiveDisplay = useComparativeReadableDate({\n date: lastActiveDate,\n });\n\n // handle cases where the DB might return an invalid date string\n if (lastActiveDisplay === \"Invalid Date\") {\n return (\n <>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"Unknown\"\n id=\"UUigrK\"\n description=\"Screen reader text indicating last active date is unknown\"\n />\n </VisuallyHidden>\n <Separator />\n </>\n );\n }\n\n return (\n <TableCellText asChild dim={dim}>\n <time dateTime={date} title={lastActiveDateTime}>\n {lastActiveDisplay}\n </time>\n </TableCellText>\n );\n}\n\nconst TableCellText = React.forwardRef<HTMLSpanElement, TableCellTextProps>(\n function TableCellText(\n { children, dim, level = \"primary\", ...props },\n forwardedRef,\n ) {\n return (\n <Text\n ref={forwardedRef}\n color={level === \"secondary\" ? \"gray\" : undefined}\n weight={level === \"secondary\" ? \"regular\" : \"medium\"}\n size={level === \"secondary\" ? \"1\" : \"2\"}\n truncate\n {...props}\n style={\n dim\n ? {\n // TODO: use CSS var instead of hard-coded value for opacity\n opacity: 0.6,\n ...props.style,\n }\n : props.style\n }\n >\n {children}\n </Text>\n );\n },\n);\n\ntype TableCellTextProps = TextProps & {\n level?: \"primary\" | \"secondary\";\n dim?: boolean;\n};\n\nconst FallbackUserIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n fill=\"currentColor\"\n viewBox=\"0 0 256 256\"\n >\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <title>User icon</title>\n <path d=\"M229.19,213c-15.81-27.32-40.63-46.49-69.47-54.62a70,70,0,1,0-63.44,0C67.44,166.5,42.62,185.67,26.81,213a6,6,0,1,0,10.38,6C56.4,185.81,90.34,166,128,166s71.6,19.81,90.81,53a6,6,0,1,0,10.38-6ZM70,96a58,58,0,1,1,58,58A58.07,58.07,0,0,1,70,96Z\" />\n </svg>\n);\n\nconst UsersManagementEmptyState = ({ isPending }: { isPending: boolean }) => {\n const { clearSearch } = useSearchContext();\n const {\n state: { searchQuery },\n } = useUsersManagementContext();\n\n // When the search query is cleared, the users query is re-fetched which sends\n // us into a pending state. When this happens we want to keep a snapshot of\n // the previous search query while the query is revalidated. We can use this\n // to keep the 'No users found for query' UI in place until re-fetching is\n // complete, otherwise the view flips to 'No users found' very quickly before\n // the full table is shown again.\n const [{ isClearing, lastSearchQuery }, setClearing] = React.useState({\n isClearing: false,\n lastSearchQuery: null as null | string,\n });\n const [wasPending, setWasPending] = React.useState(isPending);\n if (wasPending !== isPending) {\n setWasPending(isPending);\n if (!isPending) {\n setClearing((prev) =>\n prev.isClearing ? { isClearing: false, lastSearchQuery: null } : prev,\n );\n }\n }\n\n if (searchQuery || isClearing) {\n return (\n <Flex align=\"center\" justify=\"center\" py=\"8\" direction=\"column\" gap=\"2\">\n <Text size=\"2\">\n <Translation\n defaultMessage=\"No users found for query '{query}'\"\n id=\"wrEZ8+\"\n description=\"Message shown when no users match the search query\"\n values={{\n query: isClearing ? lastSearchQuery : searchQuery,\n }}\n />\n </Text>\n\n <Button\n variant=\"secondary\"\n size=\"1\"\n onClick={() => {\n setClearing({ isClearing: true, lastSearchQuery: searchQuery });\n clearSearch();\n }}\n loading={isPending}\n >\n <Translation\n defaultMessage=\"Clear search\"\n id=\"Xv3fVI\"\n description=\"Button text to clear the current search query\"\n />\n </Button>\n </Flex>\n );\n }\n\n return (\n <Flex align=\"center\" justify=\"center\" py=\"8\" gap=\"2\">\n <Text size=\"2\">\n <Translation\n defaultMessage=\"No users found\"\n id=\"MQ+LoA\"\n description=\"Message shown when there are no users in the system\"\n />\n </Text>\n </Flex>\n );\n};\n\ninterface UserRolesCellContentProps {\n user: Member;\n isMultipleRolesEnabled: boolean;\n dimText: boolean;\n}\n\nfunction UserRolesCellContent({\n user,\n isMultipleRolesEnabled,\n dimText,\n}: UserRolesCellContentProps) {\n if (!user.roles || user.roles.length === 0) {\n return (\n <>\n <VisuallyHidden>\n <Translation\n defaultMessage=\"No roles assigned\"\n id=\"CVOVB0\"\n description=\"Screen reader text indicating user has no roles assigned\"\n />\n </VisuallyHidden>\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <span aria-hidden style={{ userSelect: \"none\" }}>\n –\n </span>\n </>\n );\n }\n\n if (isMultipleRolesEnabled && user.roles.length > 1) {\n const roleNames = user.roles?.map((role) => role.name).join(\", \");\n return (\n <Tooltip content={roleNames} maxWidth=\"250px\">\n <TableCellText dim={dimText}>\n <Translation\n defaultMessage=\"{count} roles\"\n id=\"lWAmRC\"\n description=\"Shows the number of roles assigned to a user (plural)\"\n values={{ count: user.roles.length }}\n />\n </TableCellText>\n </Tooltip>\n );\n }\n\n return <TableCellText dim={dimText}>{user.roles?.[0]?.name}</TableCellText>;\n}\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"users-management\",\n widgetState: state,\n });\n}\n\nexport type { UsersManagementLoadingProps, UsersManagementErrorProps };\nexport { UsersManagement, UsersManagementLoading, UsersManagementError };\n"],"mappings":"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","names":["TableCellText"]}
@@ -60,24 +60,33 @@ function getComparativeReadableDate(now, then, options) {
60
60
  const locale = options.locale ?? "en-US";
61
61
  const timeSince = now.getTime() - then.getTime();
62
62
  const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
63
- if (timeSince < 6e4) {
63
+ const elapsed = Math.abs(timeSince);
64
+ if (elapsed < 6e4) {
64
65
  return formatter.format(0, "second");
65
66
  }
66
- if (timeSince < 36e5) {
67
- const timePassed = Math.floor(timeSince / 6e4);
68
- return formatter.format(-timePassed, "minute");
67
+ const future = timeSince < 0;
68
+ const direction = future ? 1 : -1;
69
+ if (elapsed < 36e5) {
70
+ const timePassed = Math.floor(elapsed / 6e4);
71
+ return formatter.format(direction * timePassed, "minute");
69
72
  }
70
- if (timeSince < 864e5) {
71
- const timePassed = Math.floor(timeSince / 36e5);
72
- return formatter.format(-timePassed, "hour");
73
+ if (elapsed < 864e5) {
74
+ const timePassed = Math.floor(elapsed / 36e5);
75
+ return formatter.format(direction * timePassed, "hour");
73
76
  }
74
- if (timeSince < 6048e5) {
75
- const timePassed = Math.floor(timeSince / 864e5);
76
- return formatter.format(-timePassed, "day");
77
+ if (elapsed < 6048e5) {
78
+ const timePassed = Math.floor(elapsed / 864e5);
79
+ return formatter.format(direction * timePassed, "day");
77
80
  }
78
- if (timeSince < 2592e6) {
79
- const timePassed = Math.floor(timeSince / 6048e5);
80
- return formatter.format(-timePassed, "week");
81
+ if (elapsed < 2592e6) {
82
+ const timePassed = Math.floor(elapsed / 6048e5);
83
+ return formatter.format(direction * timePassed, "week");
84
+ }
85
+ if (elapsed < 7776e6) {
86
+ const months = future ? fullCalendarMonthsBetween(now, then, options.timeZone) : fullCalendarMonthsBetween(then, now, options.timeZone);
87
+ if (months === 1 || months === 2 || months === 3) {
88
+ return formatter.format(direction * months, "month");
89
+ }
81
90
  }
82
91
  return then.toLocaleDateString(locale, {
83
92
  timeZone: options.timeZone,
@@ -87,6 +96,33 @@ function getComparativeReadableDate(now, then, options) {
87
96
  year: now.getFullYear() !== then.getFullYear() ? "numeric" : void 0
88
97
  });
89
98
  }
99
+ function fullCalendarMonthsBetween(earlier, later, timeZone) {
100
+ const from = getCalendarParts(earlier, timeZone);
101
+ const to = getCalendarParts(later, timeZone);
102
+ const months = (to.year - from.year) * 12 + (to.month - from.month);
103
+ return to.day < from.day ? months - 1 : months;
104
+ }
105
+ function getCalendarParts(date, timeZone) {
106
+ const parts = new Intl.DateTimeFormat("en-US", {
107
+ timeZone,
108
+ year: "numeric",
109
+ month: "numeric",
110
+ day: "numeric"
111
+ }).formatToParts(date);
112
+ let year = 0;
113
+ let month = 0;
114
+ let day = 0;
115
+ for (const part of parts) {
116
+ if (part.type === "year") {
117
+ year = Number(part.value);
118
+ } else if (part.type === "month") {
119
+ month = Number(part.value);
120
+ } else if (part.type === "day") {
121
+ day = Number(part.value);
122
+ }
123
+ }
124
+ return { year, month, day };
125
+ }
90
126
  function isObjectLike(value) {
91
127
  return typeof value === "object" && value !== null;
92
128
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils.ts"],"sourcesContent":["import { ListMetadata } from \"./api/types.js\";\nimport Bowser from \"bowser\";\nimport cx from \"clsx\";\nimport {\n WIDGETS_CLASS_NAMESPACE,\n WIDGETS_DATA_ATTRIBUTE_NAMESPACE,\n} from \"./constants.js\";\nimport type { Elements } from \"./elements.js\";\n\nexport const canUseDOM = !!(\n typeof window !== \"undefined\" &&\n window.document &&\n window.document.createElement\n);\n\nexport function getBestName({\n firstName,\n lastName,\n}: {\n firstName?: string | null;\n lastName?: string | null;\n}) {\n return [firstName, lastName].filter(Boolean).join(\" \") || null;\n}\n\nexport function getComparativeReadableDate(\n now: Date,\n then: Date,\n options: { locale: string; timeZone?: string },\n): string {\n const locale = options.locale ?? \"en-US\";\n const timeSince = now.getTime() - then.getTime();\n const formatter = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n\n // Has it been less than a minute?\n if (timeSince < 60_000) {\n return formatter.format(0, \"second\"); // Returns \"now\" / \"maintenant\" / \"jetzt\" etc.\n }\n\n // Has it been less than an hour?\n if (timeSince < 3_600_000) {\n const timePassed = Math.floor(timeSince / 60_000);\n return formatter.format(-timePassed, \"minute\");\n }\n\n // Has it been less than a day?\n if (timeSince < 86_400_000) {\n const timePassed = Math.floor(timeSince / 3_600_000);\n return formatter.format(-timePassed, \"hour\");\n }\n\n // Has it been less than a week?\n if (timeSince < 604_800_000) {\n const timePassed = Math.floor(timeSince / 86_400_000);\n return formatter.format(-timePassed, \"day\");\n }\n\n // Has it been less than a month?\n if (timeSince < 2_592_000_000) {\n const timePassed = Math.floor(timeSince / 604_800_000);\n return formatter.format(-timePassed, \"week\");\n }\n\n // Any later?\n return then.toLocaleDateString(locale, {\n timeZone: options.timeZone,\n month: \"long\",\n day: \"numeric\",\n // omit year if it's the same as the current year\n year: now.getFullYear() !== then.getFullYear() ? \"numeric\" : undefined,\n });\n}\n\nexport function isObjectLike(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isErrorLike(\n value: unknown,\n): value is Record<string, unknown> & { message: string } {\n return isObjectLike(value) && typeof value.message === \"string\";\n}\n\nexport function isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"then\" in value &&\n typeof value.then === \"function\"\n );\n}\n\nexport async function parseErrorResponse(\n response: Response,\n): Promise<{ message: string; status: number }> {\n try {\n const json = await response.json();\n if (!isObjectLike(json) || typeof json.message !== \"string\") {\n return {\n status: response.status,\n message: response.statusText,\n };\n }\n return {\n ...json,\n status: response.status,\n message: json.message || response.statusText,\n };\n } catch {\n return {\n status: response.status,\n message: response.statusText,\n };\n }\n}\n\nexport function namespaceClassNames(\n ...classNames: (string | undefined | null | boolean)[]\n): string {\n return classNames\n .filter(Boolean)\n .map((className) => `${WIDGETS_CLASS_NAMESPACE}-${className}`)\n .join(\" \");\n}\n\ninterface CommonDomArgs {\n className?: string | undefined | null;\n dataAttributes?: Record<string, string | boolean | undefined | null>;\n}\n\ninterface WidgetRootDomArgs {\n isWidgetRoot: true;\n widgetId: string;\n elementId?: never;\n widgetState: WidgetRootState;\n}\n\ninterface WidgetElementDomArgs {\n isWidgetRoot?: false;\n widgetId?: string;\n elementId?: keyof Elements;\n widgetState?: never;\n className: string | undefined | null;\n}\n\ntype DomArgs = CommonDomArgs & (WidgetRootDomArgs | WidgetElementDomArgs);\n\ntype DataAttributeProps = {\n [K in `data-${string}`]?: string | boolean;\n};\n\ntype DomProps = {\n className: string | undefined;\n} & DataAttributeProps;\n\nexport function getDomProps(args: DomArgs) {\n let {\n className,\n dataAttributes,\n isWidgetRoot = false,\n widgetId,\n elementId,\n widgetState,\n ...passthroughProps\n } = args;\n\n const elementClassName = elementId\n ? (() => {\n // TODO: Overrides for specific elements are mostly here for backwards\n // compatibility. They should be removed in the next major version.\n if (\n elementId === \"primaryButton\" ||\n elementId === \"secondaryButton\" ||\n elementId === \"destructiveButton\"\n ) {\n return namespaceClassNames(\"button\");\n }\n\n if (elementId === \"iconButton\") {\n return namespaceClassNames(\"button\", \"icon-button\");\n }\n\n if (\n elementId === \"primaryMenuItem\" ||\n elementId === \"destructiveMenuItem\"\n ) {\n return namespaceClassNames(\"menu-item\");\n }\n\n if (elementId === \"textfield\") {\n return namespaceClassNames(elementId, \"text-field\");\n }\n\n return namespaceClassNames(fastKebabCase(elementId));\n })()\n : undefined;\n\n const props: DomProps = {\n ...passthroughProps,\n className: cx(\n className,\n isWidgetRoot && namespaceClassNames(\"widget\"),\n elementClassName,\n ),\n };\n\n if (widgetId) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget-id`] = widgetId;\n }\n\n if (isWidgetRoot) {\n widgetState ??= \"resolved\";\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget`] = true;\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget-state`] =\n widgetState;\n }\n\n if (dataAttributes) {\n for (const [key, value] of Object.entries(dataAttributes)) {\n if (value != null) {\n const parts = [widgetId, elementId, key]\n .filter((v) => v != null)\n .map(fastKebabCase)\n .join(\"-\");\n if (parts) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-${parts}`] = value;\n }\n }\n }\n }\n\n if (elementId) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-element`] =\n fastKebabCase(elementId);\n }\n\n return props;\n}\n\n/**\n * Opt for simplicity and performance over capturing edge cases. Input strings\n * are expected to only be alphabetic.\n */\nfunction fastKebabCase(alphaString: string) {\n return alphaString.replace(/([A-Z])/g, \"-$1\").toLowerCase();\n}\n\n// Returns null when the device can't be determined. Sessions created through\n// the public authenticate API may have no user agent, and Bowser throws on\n// empty input rather than returning an empty result.\nexport function parseUserAgent(\n userAgent?: string | null,\n): { pretty: string; isMobile: boolean } | null {\n if (!userAgent?.trim()) {\n return null;\n }\n\n let browser: Bowser.Parser.Parser;\n try {\n browser = Bowser.getParser(userAgent);\n } catch {\n return null;\n }\n\n const browserName = browser.getBrowserName().replace(/^Mobile\\s*/i, \"\");\n const osName = browser.getOSName();\n const pretty = [browserName, osName].filter(Boolean).join(\" on \");\n\n if (!pretty) {\n return null;\n }\n\n return { pretty, isMobile: browser.getPlatformType() === \"mobile\" };\n}\n\nexport function getUserLocation(\n location?: {\n cityName: string;\n countryISOCode: string;\n } | null,\n ipAddress?: string | null,\n) {\n if (location) {\n return (\n [location.cityName, location.countryISOCode].filter(Boolean).join(\", \") ||\n \"Unknown location\"\n );\n }\n\n if (ipAddress) {\n return ipAddress;\n }\n\n return \"Unknown location\";\n}\n\nexport function unreachable(value: never): never {\n throw new TypeError(`Unreachable code: ${value}`);\n}\n\nexport function pluralize(word = \"\", amount = 0, showAmount = true): string {\n return `${showAmount ? `${amount} ` : \"\"}${word}${amount === 1 ? \"\" : \"s\"}`;\n}\n\n// Props that all widgets should accept.\nexport interface WidgetRootDomProps {\n className?: string;\n}\n\nexport type WidgetRootState = \"loading\" | \"error\" | \"resolved\";\n\n// The API response previously returned an object with the `listMetadata`\n// property where `before` and `after` fields were not strictly required.\ntype Pagination = Nullable<{ before: string; after: string }>;\ntype DeprecatedListMetadataResponse = {\n listMetadata: Pagination;\n};\ntype NewListMetadataResponse = {\n list_metadata: Pagination;\n};\n\nexport function getListMetadata<\n T extends DeprecatedListMetadataResponse | NewListMetadataResponse,\n>(responseObject: T | undefined | null): ListMetadata {\n const listMetadata =\n responseObject && \"listMetadata\" in responseObject\n ? responseObject.listMetadata\n : responseObject?.list_metadata;\n\n return {\n before: listMetadata?.before ?? null,\n after: listMetadata?.after ?? null,\n };\n}\n\nexport type Nullable<T> = {\n [K in keyof T]?: T[K] | null;\n};\n/**\n * Opens the Admin Portal via form submission instead of URL query parameters.\n * This keeps the API token in the request body rather than the URL,\n * reducing exposure in server logs, browser history, and referrer headers.\n */\nexport function openAdminPortalViaForm(\n link: string,\n options: { target?: string; rel?: string } = {},\n): void {\n let url: URL;\n try {\n url = new URL(link);\n } catch {\n throw new TypeError(\"Invalid URL\");\n }\n\n let { target, rel } = options;\n const apiToken = url.searchParams.get(\"apiToken\");\n // clear the apiToken from the URL search params to avoid exposing it to the\n // browser console\n url.searchParams.delete(\"apiToken\");\n\n const formAction = url.toString();\n const form = document.createElement(\"form\");\n form.method = \"POST\";\n form.action = formAction;\n if (target) {\n form.target = target;\n }\n\n if (!rel && target === \"_blank\") {\n rel = \"noopener noreferrer\";\n }\n\n if (rel) {\n form.rel = rel;\n }\n\n const tokenInput = document.createElement(\"input\");\n tokenInput.type = \"hidden\";\n tokenInput.name = \"apiToken\";\n tokenInput.value = apiToken ?? \"\";\n form.appendChild(tokenInput);\n\n document.body.appendChild(form);\n form.submit();\n document.body.removeChild(form);\n}\n\nexport function composeEventHandlers<E extends { defaultPrevented: boolean }>(\n originalEventHandler?: (event: E) => void,\n ourEventHandler?: (event: E) => void,\n) {\n return function handleEvent(event: E) {\n originalEventHandler?.(event);\n if (!event || !event.defaultPrevented) {\n return ourEventHandler?.(event);\n }\n };\n}\n"],"mappings":"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","names":["cx","Bowser"]}
1
+ {"version":3,"sources":["../../../src/lib/utils.ts"],"sourcesContent":["import { ListMetadata } from \"./api/types.js\";\nimport Bowser from \"bowser\";\nimport cx from \"clsx\";\nimport {\n WIDGETS_CLASS_NAMESPACE,\n WIDGETS_DATA_ATTRIBUTE_NAMESPACE,\n} from \"./constants.js\";\nimport type { Elements } from \"./elements.js\";\n\nexport const canUseDOM = !!(\n typeof window !== \"undefined\" &&\n window.document &&\n window.document.createElement\n);\n\nexport function getBestName({\n firstName,\n lastName,\n}: {\n firstName?: string | null;\n lastName?: string | null;\n}) {\n return [firstName, lastName].filter(Boolean).join(\" \") || null;\n}\n\nexport function getComparativeReadableDate(\n now: Date,\n then: Date,\n options: { locale: string; timeZone?: string },\n): string {\n const locale = options.locale ?? \"en-US\";\n const timeSince = now.getTime() - then.getTime();\n const formatter = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n\n // Sub-minute skew in either direction reads as \"now\". A larger gap ahead of\n // the comparison clock stays relative so a server/client discrepancy is visible.\n const elapsed = Math.abs(timeSince);\n\n if (elapsed < 60_000) {\n return formatter.format(0, \"second\"); // Returns \"now\" / \"maintenant\" / \"jetzt\" etc.\n }\n\n const future = timeSince < 0;\n const direction = future ? 1 : -1;\n\n // Has it been less than an hour?\n if (elapsed < 3_600_000) {\n const timePassed = Math.floor(elapsed / 60_000);\n return formatter.format(direction * timePassed, \"minute\");\n }\n\n // Has it been less than a day?\n if (elapsed < 86_400_000) {\n const timePassed = Math.floor(elapsed / 3_600_000);\n return formatter.format(direction * timePassed, \"hour\");\n }\n\n // Has it been less than a week?\n if (elapsed < 604_800_000) {\n const timePassed = Math.floor(elapsed / 86_400_000);\n return formatter.format(direction * timePassed, \"day\");\n }\n\n // Has it been less than a month?\n if (elapsed < 2_592_000_000) {\n const timePassed = Math.floor(elapsed / 604_800_000);\n return formatter.format(direction * timePassed, \"week\");\n }\n\n if (elapsed < 7_776_000_000) {\n const months = future\n ? fullCalendarMonthsBetween(now, then, options.timeZone)\n : fullCalendarMonthsBetween(then, now, options.timeZone);\n if (months === 1 || months === 2 || months === 3) {\n return formatter.format(direction * months, \"month\");\n }\n }\n\n // Any later?\n return then.toLocaleDateString(locale, {\n timeZone: options.timeZone,\n month: \"long\",\n day: \"numeric\",\n // omit year if it's the same as the current year\n year: now.getFullYear() !== then.getFullYear() ? \"numeric\" : undefined,\n });\n}\n\nfunction fullCalendarMonthsBetween(\n earlier: Date,\n later: Date,\n timeZone?: string,\n): number {\n const from = getCalendarParts(earlier, timeZone);\n const to = getCalendarParts(later, timeZone);\n const months = (to.year - from.year) * 12 + (to.month - from.month);\n return to.day < from.day ? months - 1 : months;\n}\n\nfunction getCalendarParts(date: Date, timeZone?: string) {\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n }).formatToParts(date);\n\n let year = 0;\n let month = 0;\n let day = 0;\n for (const part of parts) {\n if (part.type === \"year\") {\n year = Number(part.value);\n } else if (part.type === \"month\") {\n month = Number(part.value);\n } else if (part.type === \"day\") {\n day = Number(part.value);\n }\n }\n\n return { year, month, day };\n}\n\nexport function isObjectLike(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isErrorLike(\n value: unknown,\n): value is Record<string, unknown> & { message: string } {\n return isObjectLike(value) && typeof value.message === \"string\";\n}\n\nexport function isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"then\" in value &&\n typeof value.then === \"function\"\n );\n}\n\nexport async function parseErrorResponse(\n response: Response,\n): Promise<{ message: string; status: number }> {\n try {\n const json = await response.json();\n if (!isObjectLike(json) || typeof json.message !== \"string\") {\n return {\n status: response.status,\n message: response.statusText,\n };\n }\n return {\n ...json,\n status: response.status,\n message: json.message || response.statusText,\n };\n } catch {\n return {\n status: response.status,\n message: response.statusText,\n };\n }\n}\n\nexport function namespaceClassNames(\n ...classNames: (string | undefined | null | boolean)[]\n): string {\n return classNames\n .filter(Boolean)\n .map((className) => `${WIDGETS_CLASS_NAMESPACE}-${className}`)\n .join(\" \");\n}\n\ninterface CommonDomArgs {\n className?: string | undefined | null;\n dataAttributes?: Record<string, string | boolean | undefined | null>;\n}\n\ninterface WidgetRootDomArgs {\n isWidgetRoot: true;\n widgetId: string;\n elementId?: never;\n widgetState: WidgetRootState;\n}\n\ninterface WidgetElementDomArgs {\n isWidgetRoot?: false;\n widgetId?: string;\n elementId?: keyof Elements;\n widgetState?: never;\n className: string | undefined | null;\n}\n\ntype DomArgs = CommonDomArgs & (WidgetRootDomArgs | WidgetElementDomArgs);\n\ntype DataAttributeProps = {\n [K in `data-${string}`]?: string | boolean;\n};\n\ntype DomProps = {\n className: string | undefined;\n} & DataAttributeProps;\n\nexport function getDomProps(args: DomArgs) {\n let {\n className,\n dataAttributes,\n isWidgetRoot = false,\n widgetId,\n elementId,\n widgetState,\n ...passthroughProps\n } = args;\n\n const elementClassName = elementId\n ? (() => {\n // TODO: Overrides for specific elements are mostly here for backwards\n // compatibility. They should be removed in the next major version.\n if (\n elementId === \"primaryButton\" ||\n elementId === \"secondaryButton\" ||\n elementId === \"destructiveButton\"\n ) {\n return namespaceClassNames(\"button\");\n }\n\n if (elementId === \"iconButton\") {\n return namespaceClassNames(\"button\", \"icon-button\");\n }\n\n if (\n elementId === \"primaryMenuItem\" ||\n elementId === \"destructiveMenuItem\"\n ) {\n return namespaceClassNames(\"menu-item\");\n }\n\n if (elementId === \"textfield\") {\n return namespaceClassNames(elementId, \"text-field\");\n }\n\n return namespaceClassNames(fastKebabCase(elementId));\n })()\n : undefined;\n\n const props: DomProps = {\n ...passthroughProps,\n className: cx(\n className,\n isWidgetRoot && namespaceClassNames(\"widget\"),\n elementClassName,\n ),\n };\n\n if (widgetId) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget-id`] = widgetId;\n }\n\n if (isWidgetRoot) {\n widgetState ??= \"resolved\";\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget`] = true;\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-widget-state`] =\n widgetState;\n }\n\n if (dataAttributes) {\n for (const [key, value] of Object.entries(dataAttributes)) {\n if (value != null) {\n const parts = [widgetId, elementId, key]\n .filter((v) => v != null)\n .map(fastKebabCase)\n .join(\"-\");\n if (parts) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-${parts}`] = value;\n }\n }\n }\n }\n\n if (elementId) {\n props[`data-${WIDGETS_DATA_ATTRIBUTE_NAMESPACE}-element`] =\n fastKebabCase(elementId);\n }\n\n return props;\n}\n\n/**\n * Opt for simplicity and performance over capturing edge cases. Input strings\n * are expected to only be alphabetic.\n */\nfunction fastKebabCase(alphaString: string) {\n return alphaString.replace(/([A-Z])/g, \"-$1\").toLowerCase();\n}\n\n// Returns null when the device can't be determined. Sessions created through\n// the public authenticate API may have no user agent, and Bowser throws on\n// empty input rather than returning an empty result.\nexport function parseUserAgent(\n userAgent?: string | null,\n): { pretty: string; isMobile: boolean } | null {\n if (!userAgent?.trim()) {\n return null;\n }\n\n let browser: Bowser.Parser.Parser;\n try {\n browser = Bowser.getParser(userAgent);\n } catch {\n return null;\n }\n\n const browserName = browser.getBrowserName().replace(/^Mobile\\s*/i, \"\");\n const osName = browser.getOSName();\n const pretty = [browserName, osName].filter(Boolean).join(\" on \");\n\n if (!pretty) {\n return null;\n }\n\n return { pretty, isMobile: browser.getPlatformType() === \"mobile\" };\n}\n\nexport function getUserLocation(\n location?: {\n cityName: string;\n countryISOCode: string;\n } | null,\n ipAddress?: string | null,\n) {\n if (location) {\n return (\n [location.cityName, location.countryISOCode].filter(Boolean).join(\", \") ||\n \"Unknown location\"\n );\n }\n\n if (ipAddress) {\n return ipAddress;\n }\n\n return \"Unknown location\";\n}\n\nexport function unreachable(value: never): never {\n throw new TypeError(`Unreachable code: ${value}`);\n}\n\nexport function pluralize(word = \"\", amount = 0, showAmount = true): string {\n return `${showAmount ? `${amount} ` : \"\"}${word}${amount === 1 ? \"\" : \"s\"}`;\n}\n\n// Props that all widgets should accept.\nexport interface WidgetRootDomProps {\n className?: string;\n}\n\nexport type WidgetRootState = \"loading\" | \"error\" | \"resolved\";\n\n// The API response previously returned an object with the `listMetadata`\n// property where `before` and `after` fields were not strictly required.\ntype Pagination = Nullable<{ before: string; after: string }>;\ntype DeprecatedListMetadataResponse = {\n listMetadata: Pagination;\n};\ntype NewListMetadataResponse = {\n list_metadata: Pagination;\n};\n\nexport function getListMetadata<\n T extends DeprecatedListMetadataResponse | NewListMetadataResponse,\n>(responseObject: T | undefined | null): ListMetadata {\n const listMetadata =\n responseObject && \"listMetadata\" in responseObject\n ? responseObject.listMetadata\n : responseObject?.list_metadata;\n\n return {\n before: listMetadata?.before ?? null,\n after: listMetadata?.after ?? null,\n };\n}\n\nexport type Nullable<T> = {\n [K in keyof T]?: T[K] | null;\n};\n/**\n * Opens the Admin Portal via form submission instead of URL query parameters.\n * This keeps the API token in the request body rather than the URL,\n * reducing exposure in server logs, browser history, and referrer headers.\n */\nexport function openAdminPortalViaForm(\n link: string,\n options: { target?: string; rel?: string } = {},\n): void {\n let url: URL;\n try {\n url = new URL(link);\n } catch {\n throw new TypeError(\"Invalid URL\");\n }\n\n let { target, rel } = options;\n const apiToken = url.searchParams.get(\"apiToken\");\n // clear the apiToken from the URL search params to avoid exposing it to the\n // browser console\n url.searchParams.delete(\"apiToken\");\n\n const formAction = url.toString();\n const form = document.createElement(\"form\");\n form.method = \"POST\";\n form.action = formAction;\n if (target) {\n form.target = target;\n }\n\n if (!rel && target === \"_blank\") {\n rel = \"noopener noreferrer\";\n }\n\n if (rel) {\n form.rel = rel;\n }\n\n const tokenInput = document.createElement(\"input\");\n tokenInput.type = \"hidden\";\n tokenInput.name = \"apiToken\";\n tokenInput.value = apiToken ?? \"\";\n form.appendChild(tokenInput);\n\n document.body.appendChild(form);\n form.submit();\n document.body.removeChild(form);\n}\n\nexport function composeEventHandlers<E extends { defaultPrevented: boolean }>(\n originalEventHandler?: (event: E) => void,\n ourEventHandler?: (event: E) => void,\n) {\n return function handleEvent(event: E) {\n originalEventHandler?.(event);\n if (!event || !event.defaultPrevented) {\n return ourEventHandler?.(event);\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,oBAAmB;AACnB,kBAAe;AACf,uBAGO;AAGA,MAAM,YAAY,CAAC,EACxB,OAAO,WAAW,eAClB,OAAO,YACP,OAAO,SAAS;AAGX,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AACF,GAGG;AACD,SAAO,CAAC,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAC5D;AAEO,SAAS,2BACd,KACA,MACA,SACQ;AACR,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,YAAY,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC/C,QAAM,YAAY,IAAI,KAAK,mBAAmB,QAAQ,EAAE,SAAS,OAAO,CAAC;AAIzE,QAAM,UAAU,KAAK,IAAI,SAAS;AAElC,MAAI,UAAU,KAAQ;AACpB,WAAO,UAAU,OAAO,GAAG,QAAQ;AAAA,EACrC;AAEA,QAAM,SAAS,YAAY;AAC3B,QAAM,YAAY,SAAS,IAAI;AAG/B,MAAI,UAAU,MAAW;AACvB,UAAM,aAAa,KAAK,MAAM,UAAU,GAAM;AAC9C,WAAO,UAAU,OAAO,YAAY,YAAY,QAAQ;AAAA,EAC1D;AAGA,MAAI,UAAU,OAAY;AACxB,UAAM,aAAa,KAAK,MAAM,UAAU,IAAS;AACjD,WAAO,UAAU,OAAO,YAAY,YAAY,MAAM;AAAA,EACxD;AAGA,MAAI,UAAU,QAAa;AACzB,UAAM,aAAa,KAAK,MAAM,UAAU,KAAU;AAClD,WAAO,UAAU,OAAO,YAAY,YAAY,KAAK;AAAA,EACvD;AAGA,MAAI,UAAU,QAAe;AAC3B,UAAM,aAAa,KAAK,MAAM,UAAU,MAAW;AACnD,WAAO,UAAU,OAAO,YAAY,YAAY,MAAM;AAAA,EACxD;AAEA,MAAI,UAAU,QAAe;AAC3B,UAAM,SAAS,SACX,0BAA0B,KAAK,MAAM,QAAQ,QAAQ,IACrD,0BAA0B,MAAM,KAAK,QAAQ,QAAQ;AACzD,QAAI,WAAW,KAAK,WAAW,KAAK,WAAW,GAAG;AAChD,aAAO,UAAU,OAAO,YAAY,QAAQ,OAAO;AAAA,IACrD;AAAA,EACF;AAGA,SAAO,KAAK,mBAAmB,QAAQ;AAAA,IACrC,UAAU,QAAQ;AAAA,IAClB,OAAO;AAAA,IACP,KAAK;AAAA;AAAA,IAEL,MAAM,IAAI,YAAY,MAAM,KAAK,YAAY,IAAI,YAAY;AAAA,EAC/D,CAAC;AACH;AAEA,SAAS,0BACP,SACA,OACA,UACQ;AACR,QAAM,OAAO,iBAAiB,SAAS,QAAQ;AAC/C,QAAM,KAAK,iBAAiB,OAAO,QAAQ;AAC3C,QAAM,UAAU,GAAG,OAAO,KAAK,QAAQ,MAAM,GAAG,QAAQ,KAAK;AAC7D,SAAO,GAAG,MAAM,KAAK,MAAM,SAAS,IAAI;AAC1C;AAEA,SAAS,iBAAiB,MAAY,UAAmB;AACvD,QAAM,QAAQ,IAAI,KAAK,eAAe,SAAS;AAAA,IAC7C;AAAA,IACA,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC,EAAE,cAAc,IAAI;AAErB,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAQ;AACxB,aAAO,OAAO,KAAK,KAAK;AAAA,IAC1B,WAAW,KAAK,SAAS,SAAS;AAChC,cAAQ,OAAO,KAAK,KAAK;AAAA,IAC3B,WAAW,KAAK,SAAS,OAAO;AAC9B,YAAM,OAAO,KAAK,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,OAAO,IAAI;AAC5B;AAEO,SAAS,aAAa,OAAkD;AAC7E,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEO,SAAS,YACd,OACwD;AACxD,SAAO,aAAa,KAAK,KAAK,OAAO,MAAM,YAAY;AACzD;AAEO,SAAS,cAAc,OAA+C;AAC3E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAO,MAAM,SAAS;AAE1B;AAEA,eAAsB,mBACpB,UAC8C;AAC9C,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,CAAC,aAAa,IAAI,KAAK,OAAO,KAAK,YAAY,UAAU;AAC3D,aAAO;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB,SAAS,SAAS;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,SAAS;AAAA,MACjB,SAAS,KAAK,WAAW,SAAS;AAAA,IACpC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,IACpB;AAAA,EACF;AACF;AAEO,SAAS,uBACX,YACK;AACR,SAAO,WACJ,OAAO,OAAO,EACd,IAAI,CAAC,cAAc,GAAG,wCAAuB,IAAI,SAAS,EAAE,EAC5D,KAAK,GAAG;AACb;AAgCO,SAAS,YAAY,MAAe;AACzC,MAAI;AAAA,IACF;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,mBAAmB,aACpB,MAAM;AAGL,QACE,cAAc,mBACd,cAAc,qBACd,cAAc,qBACd;AACA,aAAO,oBAAoB,QAAQ;AAAA,IACrC;AAEA,QAAI,cAAc,cAAc;AAC9B,aAAO,oBAAoB,UAAU,aAAa;AAAA,IACpD;AAEA,QACE,cAAc,qBACd,cAAc,uBACd;AACA,aAAO,oBAAoB,WAAW;AAAA,IACxC;AAEA,QAAI,cAAc,aAAa;AAC7B,aAAO,oBAAoB,WAAW,YAAY;AAAA,IACpD;AAEA,WAAO,oBAAoB,cAAc,SAAS,CAAC;AAAA,EACrD,GAAG,IACH;AAEJ,QAAM,QAAkB;AAAA,IACtB,GAAG;AAAA,IACH,eAAW,YAAAA;AAAA,MACT;AAAA,MACA,gBAAgB,oBAAoB,QAAQ;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,UAAM,QAAQ,iDAAgC,YAAY,IAAI;AAAA,EAChE;AAEA,MAAI,cAAc;AAChB,oBAAgB;AAChB,UAAM,QAAQ,iDAAgC,SAAS,IAAI;AAC3D,UAAM,QAAQ,iDAAgC,eAAe,IAC3D;AAAA,EACJ;AAEA,MAAI,gBAAgB;AAClB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACzD,UAAI,SAAS,MAAM;AACjB,cAAM,QAAQ,CAAC,UAAU,WAAW,GAAG,EACpC,OAAO,CAAC,MAAM,KAAK,IAAI,EACvB,IAAI,aAAa,EACjB,KAAK,GAAG;AACX,YAAI,OAAO;AACT,gBAAM,QAAQ,iDAAgC,IAAI,KAAK,EAAE,IAAI;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW;AACb,UAAM,QAAQ,iDAAgC,UAAU,IACtD,cAAc,SAAS;AAAA,EAC3B;AAEA,SAAO;AACT;AAMA,SAAS,cAAc,aAAqB;AAC1C,SAAO,YAAY,QAAQ,YAAY,KAAK,EAAE,YAAY;AAC5D;AAKO,SAAS,eACd,WAC8C;AAC9C,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,cAAAC,QAAO,UAAU,SAAS;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,QAAQ,eAAe,EAAE,QAAQ,eAAe,EAAE;AACtE,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,SAAS,CAAC,aAAa,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,MAAM;AAEhE,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,QAAQ,UAAU,QAAQ,gBAAgB,MAAM,SAAS;AACpE;AAEO,SAAS,gBACd,UAIA,WACA;AACA,MAAI,UAAU;AACZ,WACE,CAAC,SAAS,UAAU,SAAS,cAAc,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,KACtE;AAAA,EAEJ;AAEA,MAAI,WAAW;AACb,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,YAAY,OAAqB;AAC/C,QAAM,IAAI,UAAU,qBAAqB,KAAK,EAAE;AAClD;AAEO,SAAS,UAAU,OAAO,IAAI,SAAS,GAAG,aAAa,MAAc;AAC1E,SAAO,GAAG,aAAa,GAAG,MAAM,MAAM,EAAE,GAAG,IAAI,GAAG,WAAW,IAAI,KAAK,GAAG;AAC3E;AAmBO,SAAS,gBAEd,gBAAoD;AACpD,QAAM,eACJ,kBAAkB,kBAAkB,iBAChC,eAAe,eACf,gBAAgB;AAEtB,SAAO;AAAA,IACL,QAAQ,cAAc,UAAU;AAAA,IAChC,OAAO,cAAc,SAAS;AAAA,EAChC;AACF;AAUO,SAAS,uBACd,MACA,UAA6C,CAAC,GACxC;AACN,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,IAAI;AAAA,EACpB,QAAQ;AACN,UAAM,IAAI,UAAU,aAAa;AAAA,EACnC;AAEA,MAAI,EAAE,QAAQ,IAAI,IAAI;AACtB,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU;AAGhD,MAAI,aAAa,OAAO,UAAU;AAElC,QAAM,aAAa,IAAI,SAAS;AAChC,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,SAAS;AACd,OAAK,SAAS;AACd,MAAI,QAAQ;AACV,SAAK,SAAS;AAAA,EAChB;AAEA,MAAI,CAAC,OAAO,WAAW,UAAU;AAC/B,UAAM;AAAA,EACR;AAEA,MAAI,KAAK;AACP,SAAK,MAAM;AAAA,EACb;AAEA,QAAM,aAAa,SAAS,cAAc,OAAO;AACjD,aAAW,OAAO;AAClB,aAAW,OAAO;AAClB,aAAW,QAAQ,YAAY;AAC/B,OAAK,YAAY,UAAU;AAE3B,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,OAAO;AACZ,WAAS,KAAK,YAAY,IAAI;AAChC;AAEO,SAAS,qBACd,sBACA,iBACA;AACA,SAAO,SAAS,YAAY,OAAU;AACpC,2BAAuB,KAAK;AAC5B,QAAI,CAAC,SAAS,CAAC,MAAM,kBAAkB;AACrC,aAAO,kBAAkB,KAAK;AAAA,IAChC;AAAA,EACF;AACF;","names":["cx","Bowser"]}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var use_comparative_readable_date_exports = {};
30
+ __export(use_comparative_readable_date_exports, {
31
+ useComparativeReadableDate: () => useComparativeReadableDate
32
+ });
33
+ module.exports = __toCommonJS(use_comparative_readable_date_exports);
34
+ var React = __toESM(require("react"), 1);
35
+ var import_use_locale = require("./lib/i18n/use-locale.js");
36
+ var import_utils = require("./lib/utils.js");
37
+ var import_use_now = require("./use-now.js");
38
+ const DEFAULT_TIME_ZONE = "America/Los_Angeles";
39
+ function useComparativeReadableDate({
40
+ date = null,
41
+ currentDate = null,
42
+ fallback,
43
+ timeZone
44
+ }) {
45
+ const locale = (0, import_use_locale.useLocale)();
46
+ const now = (0, import_use_now.useNow)();
47
+ return React.useMemo(() => {
48
+ if (!date) {
49
+ return fallback || null;
50
+ }
51
+ const comparedDate = currentDate || now;
52
+ if (!comparedDate) {
53
+ return date.toLocaleDateString(locale, {
54
+ month: "short",
55
+ day: "numeric",
56
+ year: "numeric",
57
+ timeZone: timeZone ?? DEFAULT_TIME_ZONE
58
+ });
59
+ }
60
+ return (0, import_utils.getComparativeReadableDate)(comparedDate, date, {
61
+ locale,
62
+ timeZone: timeZone ?? void 0
63
+ });
64
+ }, [date, currentDate, now, fallback, locale, timeZone]);
65
+ }
66
+ // Annotate the CommonJS export names for ESM import in node:
67
+ 0 && (module.exports = {
68
+ useComparativeReadableDate
69
+ });
70
+ //# sourceMappingURL=use-comparative-readable-date.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/use-comparative-readable-date.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useLocale } from \"./lib/i18n/use-locale.js\";\nimport { getComparativeReadableDate } from \"./lib/utils.js\";\nimport { useNow } from \"./use-now.js\";\n\nconst DEFAULT_TIME_ZONE = \"America/Los_Angeles\";\n\nexport function useComparativeReadableDate({\n date = null,\n currentDate = null,\n fallback,\n timeZone,\n}: {\n date?: Date | null;\n currentDate?: Date | null;\n fallback?: string;\n timeZone?: string | null;\n}) {\n const locale = useLocale();\n const now = useNow();\n\n return React.useMemo(() => {\n if (!date) {\n return fallback || null;\n }\n\n const comparedDate = currentDate || now;\n if (!comparedDate) {\n return date.toLocaleDateString(locale, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n timeZone: timeZone ?? DEFAULT_TIME_ZONE,\n });\n }\n\n return getComparativeReadableDate(comparedDate, date, {\n locale,\n timeZone: timeZone ?? undefined,\n });\n }, [date, currentDate, now, fallback, locale, timeZone]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAuB;AACvB,wBAA0B;AAC1B,mBAA2C;AAC3C,qBAAuB;AAEvB,MAAM,oBAAoB;AAEnB,SAAS,2BAA2B;AAAA,EACzC,OAAO;AAAA,EACP,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAKG;AACD,QAAM,aAAS,6BAAU;AACzB,QAAM,UAAM,uBAAO;AAEnB,SAAO,MAAM,QAAQ,MAAM;AACzB,QAAI,CAAC,MAAM;AACT,aAAO,YAAY;AAAA,IACrB;AAEA,UAAM,eAAe,eAAe;AACpC,QAAI,CAAC,cAAc;AACjB,aAAO,KAAK,mBAAmB,QAAQ;AAAA,QACrC,OAAO;AAAA,QACP,KAAK;AAAA,QACL,MAAM;AAAA,QACN,UAAU,YAAY;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,eAAO,yCAA2B,cAAc,MAAM;AAAA,MACpD;AAAA,MACA,UAAU,YAAY;AAAA,IACxB,CAAC;AAAA,EACH,GAAG,CAAC,MAAM,aAAa,KAAK,UAAU,QAAQ,QAAQ,CAAC;AACzD;","names":[]}
@@ -0,0 +1,8 @@
1
+ declare function useComparativeReadableDate({ date, currentDate, fallback, timeZone, }: {
2
+ date?: Date | null;
3
+ currentDate?: Date | null;
4
+ fallback?: string;
5
+ timeZone?: string | null;
6
+ }): string | null;
7
+
8
+ export { useComparativeReadableDate };