@workos-inc/widgets 1.20.2-next.1792708307096 → 1.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -12
- package/dist/cjs/admin-portal-audit-log-streaming.client.cjs +2 -1
- package/dist/cjs/admin-portal-audit-log-streaming.client.cjs.map +1 -1
- package/dist/cjs/admin-portal-domain-verification.client.cjs +3 -2
- package/dist/cjs/admin-portal-domain-verification.client.cjs.map +1 -1
- package/dist/cjs/admin-portal-sso-connection.client.cjs +2 -1
- package/dist/cjs/admin-portal-sso-connection.client.cjs.map +1 -1
- package/dist/cjs/api/api-provider.cjs +23 -1
- package/dist/cjs/api/api-provider.cjs.map +1 -1
- package/dist/cjs/api/api-provider.d.cts.map +1 -1
- package/dist/cjs/api/claims.cjs.map +1 -1
- package/dist/cjs/api/claims.d.cts +2 -1
- package/dist/cjs/api/claims.d.cts.map +1 -1
- package/dist/cjs/api/client-graphql/hooks.cjs +3 -20
- package/dist/cjs/api/client-graphql/hooks.cjs.map +1 -1
- package/dist/cjs/api/client-graphql/hooks.d.cts +3 -12
- package/dist/cjs/api/client-graphql/hooks.d.cts.map +1 -1
- package/dist/cjs/api/client-graphql/query-keys.cjs +25 -0
- package/dist/cjs/api/client-graphql/query-keys.cjs.map +1 -0
- package/dist/cjs/api/client-graphql/query-keys.d.cts +16 -0
- package/dist/cjs/api/client-graphql/query-keys.d.cts.map +1 -0
- package/dist/cjs/components/copy-text.cjs +1 -1
- package/dist/cjs/components/copy-text.cjs.map +1 -1
- package/dist/cjs/components/otp-input.cjs +1 -0
- package/dist/cjs/components/otp-input.cjs.map +1 -1
- package/dist/cjs/directory-sync.client.cjs +2 -1
- package/dist/cjs/directory-sync.client.cjs.map +1 -1
- package/dist/cjs/lib/api/api-key.cjs +4 -3
- package/dist/cjs/lib/api/api-key.cjs.map +1 -1
- package/dist/cjs/lib/api/me.cjs +2 -1
- package/dist/cjs/lib/api/me.cjs.map +1 -1
- package/dist/cjs/lib/api/pipes.cjs +7 -6
- package/dist/cjs/lib/api/pipes.cjs.map +1 -1
- package/dist/cjs/lib/api/user-api-key.cjs +3 -2
- package/dist/cjs/lib/api/user-api-key.cjs.map +1 -1
- package/dist/cjs/lib/use-widgets-invalidator.cjs +3 -2
- package/dist/cjs/lib/use-widgets-invalidator.cjs.map +1 -1
- package/dist/cjs/organization-switcher.client.cjs +2 -1
- package/dist/cjs/organization-switcher.client.cjs.map +1 -1
- package/dist/cjs/pipes-admin.client.cjs +2 -1
- package/dist/cjs/pipes-admin.client.cjs.map +1 -1
- package/dist/cjs/pipes.client.cjs +2 -1
- package/dist/cjs/pipes.client.cjs.map +1 -1
- package/dist/cjs/user-security.client.cjs +2 -1
- package/dist/cjs/user-security.client.cjs.map +1 -1
- package/dist/cjs/user-sessions.client.cjs +2 -1
- package/dist/cjs/user-sessions.client.cjs.map +1 -1
- package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs +9 -4
- package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs.map +1 -1
- package/dist/cjs/widgets/organization-switcher/organization-switcher.d.cts.map +1 -1
- package/dist/cjs/widgets/pipes/pipes.cjs +3 -2
- package/dist/cjs/widgets/pipes/pipes.cjs.map +1 -1
- package/dist/cjs/widgets/user-sessions/logout-all-sessions-dialog.cjs +2 -1
- package/dist/cjs/widgets/user-sessions/logout-all-sessions-dialog.cjs.map +1 -1
- package/dist/cjs/widgets/user-sessions/logout-dialog.cjs +2 -1
- package/dist/cjs/widgets/user-sessions/logout-dialog.cjs.map +1 -1
- package/dist/cjs/widgets/users-management/users-management-utils.cjs +4 -3
- package/dist/cjs/widgets/users-management/users-management-utils.cjs.map +1 -1
- package/dist/esm/admin-portal-audit-log-streaming.client.js +2 -1
- package/dist/esm/admin-portal-audit-log-streaming.client.js.map +1 -1
- package/dist/esm/admin-portal-domain-verification.client.js +2 -1
- package/dist/esm/admin-portal-domain-verification.client.js.map +1 -1
- package/dist/esm/admin-portal-sso-connection.client.js +2 -1
- package/dist/esm/admin-portal-sso-connection.client.js.map +1 -1
- package/dist/esm/api/api-provider.d.ts.map +1 -1
- package/dist/esm/api/api-provider.js +23 -1
- package/dist/esm/api/api-provider.js.map +1 -1
- package/dist/esm/api/claims.d.ts +2 -1
- package/dist/esm/api/claims.d.ts.map +1 -1
- package/dist/esm/api/claims.js.map +1 -1
- package/dist/esm/api/client-graphql/hooks.d.ts +3 -12
- package/dist/esm/api/client-graphql/hooks.d.ts.map +1 -1
- package/dist/esm/api/client-graphql/hooks.js +1 -18
- package/dist/esm/api/client-graphql/hooks.js.map +1 -1
- package/dist/esm/api/client-graphql/query-keys.d.ts +16 -0
- package/dist/esm/api/client-graphql/query-keys.d.ts.map +1 -0
- package/dist/esm/api/client-graphql/query-keys.js +23 -0
- package/dist/esm/api/client-graphql/query-keys.js.map +1 -0
- package/dist/esm/components/copy-text.js +1 -1
- package/dist/esm/components/copy-text.js.map +1 -1
- package/dist/esm/components/otp-input.js +1 -0
- package/dist/esm/components/otp-input.js.map +1 -1
- package/dist/esm/directory-sync.client.js +2 -1
- package/dist/esm/directory-sync.client.js.map +1 -1
- package/dist/esm/lib/api/api-key.js +2 -1
- package/dist/esm/lib/api/api-key.js.map +1 -1
- package/dist/esm/lib/api/me.js +2 -1
- package/dist/esm/lib/api/me.js.map +1 -1
- package/dist/esm/lib/api/pipes.js +2 -1
- package/dist/esm/lib/api/pipes.js.map +1 -1
- package/dist/esm/lib/api/user-api-key.js +2 -1
- package/dist/esm/lib/api/user-api-key.js.map +1 -1
- package/dist/esm/lib/use-widgets-invalidator.js +2 -1
- package/dist/esm/lib/use-widgets-invalidator.js.map +1 -1
- package/dist/esm/organization-switcher.client.js +2 -1
- package/dist/esm/organization-switcher.client.js.map +1 -1
- package/dist/esm/pipes-admin.client.js +2 -1
- package/dist/esm/pipes-admin.client.js.map +1 -1
- package/dist/esm/pipes.client.js +2 -1
- package/dist/esm/pipes.client.js.map +1 -1
- package/dist/esm/user-security.client.js +2 -1
- package/dist/esm/user-security.client.js.map +1 -1
- package/dist/esm/user-sessions.client.js +2 -1
- package/dist/esm/user-sessions.client.js.map +1 -1
- package/dist/esm/widgets/organization-switcher/organization-switcher.d.ts.map +1 -1
- package/dist/esm/widgets/organization-switcher/organization-switcher.js +9 -4
- package/dist/esm/widgets/organization-switcher/organization-switcher.js.map +1 -1
- package/dist/esm/widgets/pipes/pipes.js +2 -1
- package/dist/esm/widgets/pipes/pipes.js.map +1 -1
- package/dist/esm/widgets/user-sessions/logout-all-sessions-dialog.js +2 -1
- package/dist/esm/widgets/user-sessions/logout-all-sessions-dialog.js.map +1 -1
- package/dist/esm/widgets/user-sessions/logout-dialog.js +2 -1
- package/dist/esm/widgets/user-sessions/logout-dialog.js.map +1 -1
- package/dist/esm/widgets/users-management/users-management-utils.js +2 -1
- package/dist/esm/widgets/users-management/users-management-utils.js.map +1 -1
- package/package.json +1 -1
- package/src/api/api-provider.tsx +39 -1
- package/src/api/claims.ts +2 -1
- package/src/api/client-graphql/hooks.ts +8 -27
- package/src/api/client-graphql/query-keys.ts +29 -0
- package/src/components/copy-text.tsx +1 -1
- package/src/components/otp-input.tsx +2 -0
- package/src/widgets/organization-switcher/organization-switcher.tsx +16 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { clientGraphqlQueryKey
|
|
2
|
+
import { clientGraphqlQueryKey } from "../../api/client-graphql/query-keys.js";
|
|
3
|
+
import { useClientGraphqlMutation } from "../../api/client-graphql/hooks.js";
|
|
3
4
|
import { Button as Button$1 } from "../../components/elements/button.js";
|
|
4
5
|
import { Cancel, Content as AlertDialogContent, Description as AlertDialogDescription, Root, Title } from "../../components/elements/alert-dialog.js";
|
|
5
6
|
import "../../components/elements.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout-all-sessions-dialog.js","names":["AlertDialog.Root","AlertDialog.Content","AlertDialog.Title","AlertDialog.Description","AlertDialog.Cancel","Button"],"sources":["../../../../src/widgets/user-sessions/logout-all-sessions-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Flex } from \"@radix-ui/themes\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlMutation,\n} from \"../../api/client-graphql/hooks.js\";\nimport { AlertDialog, Button } from \"../../components/elements.js\";\nimport { SaveButton } from \"../../components/save-button.js\";\nimport { expectResultMember } from \"../../lib/client-graphql-result.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\n\ninterface LogoutAllSessionsDialogProps extends AlertDialog.RootProps {\n currentSessionId?: string;\n children?: ReactNode;\n}\n\nconst LogoutAllSessionsDialog: React.FC<LogoutAllSessionsDialogProps> = ({\n open,\n onOpenChange,\n currentSessionId,\n children,\n ...props\n}) => {\n const client = useQueryClient();\n\n const revokeAllSessions = useClientGraphqlMutation({\n mutationFn: async (sdk) =>\n expectResultMember(\n (\n await sdk.RevokeAllSessions({\n input: { currentSessionId: currentSessionId || null },\n })\n ).revokeAllSessions,\n \"AllSessionsRevoked\",\n ),\n });\n\n const onSubmitForm = () => {\n revokeAllSessions.mutate();\n };\n\n const handleDone = React.useCallback(() => {\n onOpenChange?.(false);\n\n client.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"Sessions\"),\n });\n }, [client, onOpenChange]);\n\n return (\n <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>\n <AlertDialog.Content maxWidth=\"480px\">\n <AlertDialog.Title>\n <Translation\n defaultMessage=\"Sign out of all other devices?\"\n id=\"x//BWg\"\n description=\"Dialog title asking to confirm signing out of all other devices\"\n />\n </AlertDialog.Title>\n <AlertDialog.Description>\n <Translation\n defaultMessage=\"You will be logged out of all other active sessions on other devices, except this one.\"\n id=\"GftsX9\"\n description=\"Description explaining all other sessions will be logged out\"\n />\n </AlertDialog.Description>\n\n <Flex gap=\"3\" justify=\"end\" mt=\"5\" asChild>\n <form\n onSubmit={(event) => {\n event.preventDefault();\n onSubmitForm();\n }}\n >\n <AlertDialog.Cancel>\n <Button\n variant=\"secondary\"\n disabled={\n revokeAllSessions.isPending || revokeAllSessions.isSuccess\n }\n >\n <Translation\n defaultMessage=\"Cancel\"\n id=\"Pnmy4H\"\n description=\"Button to cancel signing out of all devices\"\n />\n </Button>\n </AlertDialog.Cancel>\n\n <SaveButton\n asChild\n loading={revokeAllSessions.isPending}\n done={revokeAllSessions.isSuccess}\n onDone={handleDone}\n >\n <Button variant=\"destructive\" type=\"submit\">\n <Translation\n defaultMessage=\"Sign out\"\n id=\"1Fb48o\"\n description=\"Button to confirm signing out of all other devices\"\n />\n </Button>\n </SaveButton>\n </form>\n </Flex>\n </AlertDialog.Content>\n </AlertDialog.Root>\n );\n};\nexport { LogoutAllSessionsDialog };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout-all-sessions-dialog.js","names":["AlertDialog.Root","AlertDialog.Content","AlertDialog.Title","AlertDialog.Description","AlertDialog.Cancel","Button"],"sources":["../../../../src/widgets/user-sessions/logout-all-sessions-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Flex } from \"@radix-ui/themes\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlMutation,\n} from \"../../api/client-graphql/hooks.js\";\nimport { AlertDialog, Button } from \"../../components/elements.js\";\nimport { SaveButton } from \"../../components/save-button.js\";\nimport { expectResultMember } from \"../../lib/client-graphql-result.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\n\ninterface LogoutAllSessionsDialogProps extends AlertDialog.RootProps {\n currentSessionId?: string;\n children?: ReactNode;\n}\n\nconst LogoutAllSessionsDialog: React.FC<LogoutAllSessionsDialogProps> = ({\n open,\n onOpenChange,\n currentSessionId,\n children,\n ...props\n}) => {\n const client = useQueryClient();\n\n const revokeAllSessions = useClientGraphqlMutation({\n mutationFn: async (sdk) =>\n expectResultMember(\n (\n await sdk.RevokeAllSessions({\n input: { currentSessionId: currentSessionId || null },\n })\n ).revokeAllSessions,\n \"AllSessionsRevoked\",\n ),\n });\n\n const onSubmitForm = () => {\n revokeAllSessions.mutate();\n };\n\n const handleDone = React.useCallback(() => {\n onOpenChange?.(false);\n\n client.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"Sessions\"),\n });\n }, [client, onOpenChange]);\n\n return (\n <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>\n <AlertDialog.Content maxWidth=\"480px\">\n <AlertDialog.Title>\n <Translation\n defaultMessage=\"Sign out of all other devices?\"\n id=\"x//BWg\"\n description=\"Dialog title asking to confirm signing out of all other devices\"\n />\n </AlertDialog.Title>\n <AlertDialog.Description>\n <Translation\n defaultMessage=\"You will be logged out of all other active sessions on other devices, except this one.\"\n id=\"GftsX9\"\n description=\"Description explaining all other sessions will be logged out\"\n />\n </AlertDialog.Description>\n\n <Flex gap=\"3\" justify=\"end\" mt=\"5\" asChild>\n <form\n onSubmit={(event) => {\n event.preventDefault();\n onSubmitForm();\n }}\n >\n <AlertDialog.Cancel>\n <Button\n variant=\"secondary\"\n disabled={\n revokeAllSessions.isPending || revokeAllSessions.isSuccess\n }\n >\n <Translation\n defaultMessage=\"Cancel\"\n id=\"Pnmy4H\"\n description=\"Button to cancel signing out of all devices\"\n />\n </Button>\n </AlertDialog.Cancel>\n\n <SaveButton\n asChild\n loading={revokeAllSessions.isPending}\n done={revokeAllSessions.isSuccess}\n onDone={handleDone}\n >\n <Button variant=\"destructive\" type=\"submit\">\n <Translation\n defaultMessage=\"Sign out\"\n id=\"1Fb48o\"\n description=\"Button to confirm signing out of all other devices\"\n />\n </Button>\n </SaveButton>\n </form>\n </Flex>\n </AlertDialog.Content>\n </AlertDialog.Root>\n );\n};\nexport { LogoutAllSessionsDialog };\n"],"mappings":";;;;;;;;;;;;;;AAoBA,MAAM,2BAAmE,EACvE,MACA,cACA,kBACA,UACA,GAAG,YACC;CACJ,MAAM,SAAS,eAAe;CAE9B,MAAM,oBAAoB,yBAAyB,EACjD,YAAY,OAAO,QACjB,oBAEI,MAAM,IAAI,kBAAkB,EAC1B,OAAO,EAAE,kBAAkB,oBAAoB,KAAK,EACtD,CAAC,EAAA,CACD,mBACF,oBACF,EACJ,CAAC;CAED,MAAM,qBAAqB;EACzB,kBAAkB,OAAO;CAC3B;CAEA,MAAM,aAAa,MAAM,kBAAkB;EACzC,eAAe,KAAK;EAEpB,OAAO,kBAAkB,EACvB,UAAU,sBAAsB,UAAU,EAC5C,CAAC;CACH,GAAG,CAAC,QAAQ,YAAY,CAAC;CAEzB,OACE,oBAACA,MAAD;EAAwB;EAAoB;EAAc,GAAI;EAC5D,UAAA,qBAACC,oBAAD;GAAqB,UAAS;GAA9B,UAAA;IACE,oBAACC,OAAD,EAAA,UACE,oBAAC,aAAD;KACE,gBAAe;KACf,IAAG;KACH,aAAY;IACb,CAAA,EACgB,CAAA;IACnB,oBAACC,wBAAD,EAAA,UACE,oBAAC,aAAD;KACE,gBAAe;KACf,IAAG;KACH,aAAY;IACb,CAAA,EACsB,CAAA;IAEzB,oBAAC,MAAD;KAAM,KAAI;KAAI,SAAQ;KAAM,IAAG;KAAI,SAAA;KACjC,UAAA,qBAAC,QAAD;MACE,WAAW,UAAU;OACnB,MAAM,eAAe;OACrB,aAAa;MACf;MAJF,UAAA,CAME,oBAACC,QAAD,EAAA,UACE,oBAACC,UAAD;OACE,SAAQ;OACR,UACE,kBAAkB,aAAa,kBAAkB;OAGnD,UAAA,oBAAC,aAAD;QACE,gBAAe;QACf,IAAG;QACH,aAAY;OACb,CAAA;MACK,CAAA,EACU,CAAA,GAEpB,oBAAC,YAAD;OACE,SAAA;OACA,SAAS,kBAAkB;OAC3B,MAAM,kBAAkB;OACxB,QAAQ;OAER,UAAA,oBAACA,UAAD;QAAQ,SAAQ;QAAc,MAAK;QACjC,UAAA,oBAAC,aAAD;SACE,gBAAe;SACf,IAAG;SACH,aAAY;QACb,CAAA;OACK,CAAA;MACE,CAAA,CACR;;IACF,CAAA;GACa;;CACL,CAAA;AAEtB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { clientGraphqlQueryKey
|
|
2
|
+
import { clientGraphqlQueryKey } from "../../api/client-graphql/query-keys.js";
|
|
3
|
+
import { useClientGraphqlMutation } from "../../api/client-graphql/hooks.js";
|
|
3
4
|
import { Button as Button$1 } from "../../components/elements/button.js";
|
|
4
5
|
import { useTranslation } from "../../lib/i18n/use-translation.js";
|
|
5
6
|
import { Cancel, Content as AlertDialogContent, Description as AlertDialogDescription, Root, Title } from "../../components/elements/alert-dialog.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout-dialog.js","names":["AlertDialog.Root","AlertDialog.Content","AlertDialog.Title","AlertDialog.Description","AlertDialog.Cancel","Button"],"sources":["../../../../src/widgets/user-sessions/logout-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Flex, Strong } from \"@radix-ui/themes\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { SessionFieldsFragment } from \"../../api/client-graphql/generated.js\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlMutation,\n} from \"../../api/client-graphql/hooks.js\";\nimport { AlertDialog, Button } from \"../../components/elements.js\";\nimport { SaveButton } from \"../../components/save-button.js\";\nimport { expectResultMember } from \"../../lib/client-graphql-result.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { parseUserAgent } from \"../../lib/parse-user-agent.js\";\n\ninterface LogoutDialogProps extends AlertDialog.RootProps {\n children?: ReactNode;\n session: SessionFieldsFragment;\n}\n\nconst LogoutDialog: React.FC<LogoutDialogProps> = ({\n children,\n session,\n open,\n onOpenChange,\n ...props\n}) => {\n const client = useQueryClient();\n const translate = useTranslation();\n const device =\n parseUserAgent(session.userAgent)?.pretty ??\n translate({\n defaultMessage: \"Unknown device\",\n id: \"nF+P+T\",\n description:\n \"Label for a session whose device could not be determined from its user agent\",\n });\n\n const revokeSession = useClientGraphqlMutation({\n mutationFn: async (sdk, sessionId: string) =>\n expectResultMember(\n (await sdk.RevokeSession({ input: { sessionId } })).revokeSession,\n \"SessionRevoked\",\n ),\n });\n\n const handleDone = React.useCallback(() => {\n onOpenChange?.(false);\n\n client.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"Sessions\"),\n exact: false,\n });\n }, [onOpenChange, client]);\n\n const onSubmitForm = () => {\n revokeSession.mutate(session.id);\n };\n\n return (\n <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>\n <AlertDialog.Content maxWidth=\"480px\">\n <AlertDialog.Title>\n <Translation\n defaultMessage=\"Sign out of device?\"\n id=\"Jic5nB\"\n description=\"Dialog title asking to confirm signing out of a device\"\n />\n </AlertDialog.Title>\n <AlertDialog.Description>\n <Translation\n defaultMessage=\"You will be signed out of {device}.\"\n id=\"diFnRK\"\n description=\"Description explaining the user will be signed out of a specific device\"\n values={{ device: <Strong>{device}</Strong> }}\n />\n </AlertDialog.Description>\n\n <Flex gap=\"3\" justify=\"end\" mt=\"5\" asChild>\n <form\n onSubmit={(event) => {\n event.preventDefault();\n onSubmitForm();\n }}\n >\n <AlertDialog.Cancel>\n <Button\n variant=\"secondary\"\n disabled={revokeSession.isPending || revokeSession.isSuccess}\n >\n <Translation\n defaultMessage=\"Cancel\"\n id=\"KjY2RX\"\n description=\"Button to cancel the logout action\"\n />\n </Button>\n </AlertDialog.Cancel>\n\n <SaveButton\n asChild\n loading={revokeSession.isPending}\n done={revokeSession.isSuccess}\n onDone={handleDone}\n >\n <Button variant=\"destructive\" type=\"submit\">\n <Translation\n defaultMessage=\"Sign out\"\n id=\"tRJt/S\"\n description=\"Button to confirm signing out of device\"\n />\n </Button>\n </SaveButton>\n </form>\n </Flex>\n </AlertDialog.Content>\n </AlertDialog.Root>\n );\n};\nexport { LogoutDialog };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout-dialog.js","names":["AlertDialog.Root","AlertDialog.Content","AlertDialog.Title","AlertDialog.Description","AlertDialog.Cancel","Button"],"sources":["../../../../src/widgets/user-sessions/logout-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Flex, Strong } from \"@radix-ui/themes\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { SessionFieldsFragment } from \"../../api/client-graphql/generated.js\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlMutation,\n} from \"../../api/client-graphql/hooks.js\";\nimport { AlertDialog, Button } from \"../../components/elements.js\";\nimport { SaveButton } from \"../../components/save-button.js\";\nimport { expectResultMember } from \"../../lib/client-graphql-result.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { parseUserAgent } from \"../../lib/parse-user-agent.js\";\n\ninterface LogoutDialogProps extends AlertDialog.RootProps {\n children?: ReactNode;\n session: SessionFieldsFragment;\n}\n\nconst LogoutDialog: React.FC<LogoutDialogProps> = ({\n children,\n session,\n open,\n onOpenChange,\n ...props\n}) => {\n const client = useQueryClient();\n const translate = useTranslation();\n const device =\n parseUserAgent(session.userAgent)?.pretty ??\n translate({\n defaultMessage: \"Unknown device\",\n id: \"nF+P+T\",\n description:\n \"Label for a session whose device could not be determined from its user agent\",\n });\n\n const revokeSession = useClientGraphqlMutation({\n mutationFn: async (sdk, sessionId: string) =>\n expectResultMember(\n (await sdk.RevokeSession({ input: { sessionId } })).revokeSession,\n \"SessionRevoked\",\n ),\n });\n\n const handleDone = React.useCallback(() => {\n onOpenChange?.(false);\n\n client.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"Sessions\"),\n exact: false,\n });\n }, [onOpenChange, client]);\n\n const onSubmitForm = () => {\n revokeSession.mutate(session.id);\n };\n\n return (\n <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>\n <AlertDialog.Content maxWidth=\"480px\">\n <AlertDialog.Title>\n <Translation\n defaultMessage=\"Sign out of device?\"\n id=\"Jic5nB\"\n description=\"Dialog title asking to confirm signing out of a device\"\n />\n </AlertDialog.Title>\n <AlertDialog.Description>\n <Translation\n defaultMessage=\"You will be signed out of {device}.\"\n id=\"diFnRK\"\n description=\"Description explaining the user will be signed out of a specific device\"\n values={{ device: <Strong>{device}</Strong> }}\n />\n </AlertDialog.Description>\n\n <Flex gap=\"3\" justify=\"end\" mt=\"5\" asChild>\n <form\n onSubmit={(event) => {\n event.preventDefault();\n onSubmitForm();\n }}\n >\n <AlertDialog.Cancel>\n <Button\n variant=\"secondary\"\n disabled={revokeSession.isPending || revokeSession.isSuccess}\n >\n <Translation\n defaultMessage=\"Cancel\"\n id=\"KjY2RX\"\n description=\"Button to cancel the logout action\"\n />\n </Button>\n </AlertDialog.Cancel>\n\n <SaveButton\n asChild\n loading={revokeSession.isPending}\n done={revokeSession.isSuccess}\n onDone={handleDone}\n >\n <Button variant=\"destructive\" type=\"submit\">\n <Translation\n defaultMessage=\"Sign out\"\n id=\"tRJt/S\"\n description=\"Button to confirm signing out of device\"\n />\n </Button>\n </SaveButton>\n </form>\n </Flex>\n </AlertDialog.Content>\n </AlertDialog.Root>\n );\n};\nexport { LogoutDialog };\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,MAAM,gBAA6C,EACjD,UACA,SACA,MACA,cACA,GAAG,YACC;CACJ,MAAM,SAAS,eAAe;CAC9B,MAAM,YAAY,eAAe;CACjC,MAAM,SACJ,eAAe,QAAQ,SAAS,CAAC,EAAE,UACnC,UAAU;EACR,gBAAgB;EAChB,IAAI;EACJ,aACE;CACJ,CAAC;CAEH,MAAM,gBAAgB,yBAAyB,EAC7C,YAAY,OAAO,KAAK,cACtB,oBACG,MAAM,IAAI,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAA,CAAG,eACpD,gBACF,EACJ,CAAC;CAED,MAAM,aAAa,MAAM,kBAAkB;EACzC,eAAe,KAAK;EAEpB,OAAO,kBAAkB;GACvB,UAAU,sBAAsB,UAAU;GAC1C,OAAO;EACT,CAAC;CACH,GAAG,CAAC,cAAc,MAAM,CAAC;CAEzB,MAAM,qBAAqB;EACzB,cAAc,OAAO,QAAQ,EAAE;CACjC;CAEA,OACE,oBAACA,MAAD;EAAwB;EAAoB;EAAc,GAAI;EAC5D,UAAA,qBAACC,oBAAD;GAAqB,UAAS;GAA9B,UAAA;IACE,oBAACC,OAAD,EAAA,UACE,oBAAC,aAAD;KACE,gBAAe;KACf,IAAG;KACH,aAAY;IACb,CAAA,EACgB,CAAA;IACnB,oBAACC,wBAAD,EAAA,UACE,oBAAC,aAAD;KACE,gBAAe;KACf,IAAG;KACH,aAAY;KACZ,QAAQ,EAAE,QAAQ,oBAAC,QAAD,EAAA,UAAS,OAAe,CAAA,EAAE;IAC7C,CAAA,EACsB,CAAA;IAEzB,oBAAC,MAAD;KAAM,KAAI;KAAI,SAAQ;KAAM,IAAG;KAAI,SAAA;KACjC,UAAA,qBAAC,QAAD;MACE,WAAW,UAAU;OACnB,MAAM,eAAe;OACrB,aAAa;MACf;MAJF,UAAA,CAME,oBAACC,QAAD,EAAA,UACE,oBAACC,UAAD;OACE,SAAQ;OACR,UAAU,cAAc,aAAa,cAAc;OAEnD,UAAA,oBAAC,aAAD;QACE,gBAAe;QACf,IAAG;QACH,aAAY;OACb,CAAA;MACK,CAAA,EACU,CAAA,GAEpB,oBAAC,YAAD;OACE,SAAA;OACA,SAAS,cAAc;OACvB,MAAM,cAAc;OACpB,QAAQ;OAER,UAAA,oBAACA,UAAD;QAAQ,SAAQ;QAAc,MAAK;QACjC,UAAA,oBAAC,aAAD;SACE,gBAAe;SACf,IAAG;SACH,aAAY;QACb,CAAA;OACK,CAAA;MACE,CAAA,CACR;;IACF,CAAA;GACa;;CACL,CAAA;AAEtB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../../lib/constants.js";
|
|
2
|
+
import { clientGraphqlQueryKey } from "../../api/client-graphql/query-keys.js";
|
|
2
3
|
import { ApiError } from "../../api/errors.js";
|
|
3
|
-
import {
|
|
4
|
+
import { useClientGraphqlQuery } from "../../api/client-graphql/hooks.js";
|
|
4
5
|
import { useUsersManagementContext } from "./users-management-context.js";
|
|
5
6
|
import { keepPreviousData, useQueryClient } from "@tanstack/react-query";
|
|
6
7
|
//#region src/widgets/users-management/users-management-utils.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users-management-utils.js","names":[],"sources":["../../../../src/widgets/users-management/users-management-utils.ts"],"sourcesContent":["import type { UseQueryResult } from \"@tanstack/react-query\";\nimport { keepPreviousData, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n InviteUserMutation,\n ListOrganizationMembershipsQuery,\n ListRolesQuery,\n OrganizationMemberAction,\n OrganizationMemberFieldsFragment,\n RemoveMemberMutation,\n ResendInvitationMutation,\n RevokeInvitationMutation,\n RoleFieldsFragment,\n UpdateMemberRoleMutation,\n} from \"../../api/client-graphql/generated.js\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlQuery,\n} from \"../../api/client-graphql/hooks.js\";\nimport { ApiError } from \"../../api/errors.js\";\nimport type { ListMetadata } from \"../../lib/api/types.js\";\nimport { USER_ROW_LIMIT } from \"../../lib/constants.js\";\nimport { useUsersManagementContext } from \"./users-management-context.js\";\nimport type { UsersManagementContextValue } from \"./users-management-context.js\";\n\nexport type Role = RoleFieldsFragment;\n\nexport type RolesAndConfig = ListRolesQuery[\"roles\"];\n\nexport interface Member extends Omit<\n OrganizationMemberFieldsFragment,\n \"actions\"\n> {\n actions: MemberAction[];\n isLoggedInUser: boolean;\n}\n\nexport interface MembersQueryResult {\n data: Member[];\n listMetadata: ListMetadata;\n}\n\nconst MemberActions = {\n EditRole: \"edit-role\",\n ResendInvite: \"resend-invite\",\n RevokeInvite: \"revoke-invite\",\n RevokeMembership: \"revoke-membership\",\n} as const satisfies Record<OrganizationMemberAction, string>;\n\ntype MemberAction = (typeof MemberActions)[keyof typeof MemberActions];\n\nfunction toMember(member: OrganizationMemberFieldsFragment): Member {\n return {\n ...member,\n actions: member.actions.map((action) => MemberActions[action]),\n isLoggedInUser: member.isCurrentUser,\n };\n}\n\nfunction toMembersQueryResult(\n data: ListOrganizationMembershipsQuery,\n): MembersQueryResult {\n const { organizationMemberships } = data;\n return {\n data: organizationMemberships.data.map(toMember),\n listMetadata: {\n before: organizationMemberships.listMetadata.before ?? null,\n after: organizationMemberships.listMetadata.after ?? null,\n },\n };\n}\n\nexport const EMPTY_ROLES_AND_CONFIG: Readonly<ListRolesQuery> = {\n roles: { multipleRolesEnabled: false, roles: [] },\n};\n\nexport function useRolesAndConfig(): UseQueryResult<RolesAndConfig> {\n return useClientGraphqlQuery({\n queryKey: clientGraphqlQueryKey(\"ListRoles\"),\n queryFn: (sdk) => sdk.ListRoles(),\n select: (data) => data.roles,\n initialData: EMPTY_ROLES_AND_CONFIG,\n });\n}\n\nexport function useUsers(context?: UsersManagementContextValue) {\n const {\n state: { pagination, role, searchQuery },\n } = useUsersManagementContext(context);\n const { before, after } = pagination || {};\n const variables = {\n after: after ?? null,\n before: before ?? null,\n limit: USER_ROW_LIMIT,\n roleSlug: role ?? null,\n search: searchQuery ?? null,\n };\n\n return useClientGraphqlQuery({\n queryKey: clientGraphqlQueryKey(\"ListOrganizationMemberships\", variables),\n queryFn: (sdk) => sdk.ListOrganizationMemberships(variables),\n select: toMembersQueryResult,\n placeholderData: keepPreviousData,\n });\n}\n\nexport function useInvalidateMembers() {\n const queryClient = useQueryClient();\n return () =>\n queryClient.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"ListOrganizationMemberships\"),\n });\n}\n\nfunction mutationFailure(typename: string, message: string): ApiError {\n return new ApiError({\n message,\n status: 400,\n queryType: \"mutation\",\n context: { typename },\n });\n}\n\nexport function unwrapUpdateMemberRole(\n result: UpdateMemberRoleMutation[\"updateMemberRole\"],\n) {\n switch (result.__typename) {\n case \"MemberRoleUpdated\":\n return result.member;\n case \"MemberNotFound\":\n throw mutationFailure(result.__typename, \"Cannot edit role for user\");\n case \"RoleNotFound\":\n throw mutationFailure(result.__typename, \"Invalid role\");\n }\n}\n\nexport function unwrapInviteUser(result: InviteUserMutation[\"inviteUser\"]) {\n switch (result.__typename) {\n case \"UserInvited\":\n return result.invitation;\n case \"InviteeAlreadyMember\":\n throw mutationFailure(result.__typename, \"User already exists\");\n case \"InviteeAlreadyInvited\":\n throw mutationFailure(result.__typename, \"User already invited\");\n case \"InvalidInviteeEmail\":\n case \"InviteeEmailNotDeliverable\":\n throw mutationFailure(result.__typename, \"Invalid email\");\n case \"InvalidInviteeRole\":\n throw mutationFailure(result.__typename, \"Invalid role\");\n case \"InvalidInvitationExpiry\":\n throw mutationFailure(result.__typename, \"Invalid input\");\n case \"OrganizationBlockedFromSendingInvites\":\n throw mutationFailure(\n result.__typename,\n \"This organization is not allowed to send invites.\",\n );\n }\n}\n\nexport function unwrapRemoveMember(\n result: RemoveMemberMutation[\"removeMember\"],\n) {\n switch (result.__typename) {\n case \"MemberRemoved\":\n return result;\n case \"MemberNotFound\":\n case \"MemberIsManagedByDirectory\":\n throw mutationFailure(result.__typename, \"Cannot revoke membership\");\n }\n}\n\nexport function unwrapRevokeInvitation(\n result: RevokeInvitationMutation[\"revokeInvitation\"],\n) {\n switch (result.__typename) {\n case \"InvitationRevoked\":\n return result.invitation;\n case \"InvitationNotFound\":\n throw mutationFailure(result.__typename, \"User not invited\");\n case \"InvitationNotPending\":\n throw mutationFailure(result.__typename, \"User already accepted invite\");\n }\n}\n\nexport function unwrapResendInvitation(\n result: ResendInvitationMutation[\"resendInvitation\"],\n) {\n switch (result.__typename) {\n case \"InvitationResent\":\n return result.invitation;\n case \"ResendInvitationNotFound\":\n throw mutationFailure(result.__typename, \"User not invited\");\n case \"InviteeAlreadyAccepted\":\n throw mutationFailure(result.__typename, \"User already accepted invite\");\n case \"InviteeEmailNotDeliverable\":\n throw mutationFailure(result.__typename, \"Invalid email\");\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"users-management-utils.js","names":[],"sources":["../../../../src/widgets/users-management/users-management-utils.ts"],"sourcesContent":["import type { UseQueryResult } from \"@tanstack/react-query\";\nimport { keepPreviousData, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n InviteUserMutation,\n ListOrganizationMembershipsQuery,\n ListRolesQuery,\n OrganizationMemberAction,\n OrganizationMemberFieldsFragment,\n RemoveMemberMutation,\n ResendInvitationMutation,\n RevokeInvitationMutation,\n RoleFieldsFragment,\n UpdateMemberRoleMutation,\n} from \"../../api/client-graphql/generated.js\";\nimport {\n clientGraphqlQueryKey,\n useClientGraphqlQuery,\n} from \"../../api/client-graphql/hooks.js\";\nimport { ApiError } from \"../../api/errors.js\";\nimport type { ListMetadata } from \"../../lib/api/types.js\";\nimport { USER_ROW_LIMIT } from \"../../lib/constants.js\";\nimport { useUsersManagementContext } from \"./users-management-context.js\";\nimport type { UsersManagementContextValue } from \"./users-management-context.js\";\n\nexport type Role = RoleFieldsFragment;\n\nexport type RolesAndConfig = ListRolesQuery[\"roles\"];\n\nexport interface Member extends Omit<\n OrganizationMemberFieldsFragment,\n \"actions\"\n> {\n actions: MemberAction[];\n isLoggedInUser: boolean;\n}\n\nexport interface MembersQueryResult {\n data: Member[];\n listMetadata: ListMetadata;\n}\n\nconst MemberActions = {\n EditRole: \"edit-role\",\n ResendInvite: \"resend-invite\",\n RevokeInvite: \"revoke-invite\",\n RevokeMembership: \"revoke-membership\",\n} as const satisfies Record<OrganizationMemberAction, string>;\n\ntype MemberAction = (typeof MemberActions)[keyof typeof MemberActions];\n\nfunction toMember(member: OrganizationMemberFieldsFragment): Member {\n return {\n ...member,\n actions: member.actions.map((action) => MemberActions[action]),\n isLoggedInUser: member.isCurrentUser,\n };\n}\n\nfunction toMembersQueryResult(\n data: ListOrganizationMembershipsQuery,\n): MembersQueryResult {\n const { organizationMemberships } = data;\n return {\n data: organizationMemberships.data.map(toMember),\n listMetadata: {\n before: organizationMemberships.listMetadata.before ?? null,\n after: organizationMemberships.listMetadata.after ?? null,\n },\n };\n}\n\nexport const EMPTY_ROLES_AND_CONFIG: Readonly<ListRolesQuery> = {\n roles: { multipleRolesEnabled: false, roles: [] },\n};\n\nexport function useRolesAndConfig(): UseQueryResult<RolesAndConfig> {\n return useClientGraphqlQuery({\n queryKey: clientGraphqlQueryKey(\"ListRoles\"),\n queryFn: (sdk) => sdk.ListRoles(),\n select: (data) => data.roles,\n initialData: EMPTY_ROLES_AND_CONFIG,\n });\n}\n\nexport function useUsers(context?: UsersManagementContextValue) {\n const {\n state: { pagination, role, searchQuery },\n } = useUsersManagementContext(context);\n const { before, after } = pagination || {};\n const variables = {\n after: after ?? null,\n before: before ?? null,\n limit: USER_ROW_LIMIT,\n roleSlug: role ?? null,\n search: searchQuery ?? null,\n };\n\n return useClientGraphqlQuery({\n queryKey: clientGraphqlQueryKey(\"ListOrganizationMemberships\", variables),\n queryFn: (sdk) => sdk.ListOrganizationMemberships(variables),\n select: toMembersQueryResult,\n placeholderData: keepPreviousData,\n });\n}\n\nexport function useInvalidateMembers() {\n const queryClient = useQueryClient();\n return () =>\n queryClient.invalidateQueries({\n queryKey: clientGraphqlQueryKey(\"ListOrganizationMemberships\"),\n });\n}\n\nfunction mutationFailure(typename: string, message: string): ApiError {\n return new ApiError({\n message,\n status: 400,\n queryType: \"mutation\",\n context: { typename },\n });\n}\n\nexport function unwrapUpdateMemberRole(\n result: UpdateMemberRoleMutation[\"updateMemberRole\"],\n) {\n switch (result.__typename) {\n case \"MemberRoleUpdated\":\n return result.member;\n case \"MemberNotFound\":\n throw mutationFailure(result.__typename, \"Cannot edit role for user\");\n case \"RoleNotFound\":\n throw mutationFailure(result.__typename, \"Invalid role\");\n }\n}\n\nexport function unwrapInviteUser(result: InviteUserMutation[\"inviteUser\"]) {\n switch (result.__typename) {\n case \"UserInvited\":\n return result.invitation;\n case \"InviteeAlreadyMember\":\n throw mutationFailure(result.__typename, \"User already exists\");\n case \"InviteeAlreadyInvited\":\n throw mutationFailure(result.__typename, \"User already invited\");\n case \"InvalidInviteeEmail\":\n case \"InviteeEmailNotDeliverable\":\n throw mutationFailure(result.__typename, \"Invalid email\");\n case \"InvalidInviteeRole\":\n throw mutationFailure(result.__typename, \"Invalid role\");\n case \"InvalidInvitationExpiry\":\n throw mutationFailure(result.__typename, \"Invalid input\");\n case \"OrganizationBlockedFromSendingInvites\":\n throw mutationFailure(\n result.__typename,\n \"This organization is not allowed to send invites.\",\n );\n }\n}\n\nexport function unwrapRemoveMember(\n result: RemoveMemberMutation[\"removeMember\"],\n) {\n switch (result.__typename) {\n case \"MemberRemoved\":\n return result;\n case \"MemberNotFound\":\n case \"MemberIsManagedByDirectory\":\n throw mutationFailure(result.__typename, \"Cannot revoke membership\");\n }\n}\n\nexport function unwrapRevokeInvitation(\n result: RevokeInvitationMutation[\"revokeInvitation\"],\n) {\n switch (result.__typename) {\n case \"InvitationRevoked\":\n return result.invitation;\n case \"InvitationNotFound\":\n throw mutationFailure(result.__typename, \"User not invited\");\n case \"InvitationNotPending\":\n throw mutationFailure(result.__typename, \"User already accepted invite\");\n }\n}\n\nexport function unwrapResendInvitation(\n result: ResendInvitationMutation[\"resendInvitation\"],\n) {\n switch (result.__typename) {\n case \"InvitationResent\":\n return result.invitation;\n case \"ResendInvitationNotFound\":\n throw mutationFailure(result.__typename, \"User not invited\");\n case \"InviteeAlreadyAccepted\":\n throw mutationFailure(result.__typename, \"User already accepted invite\");\n case \"InviteeEmailNotDeliverable\":\n throw mutationFailure(result.__typename, \"Invalid email\");\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/api/api-provider.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { useIsHydrated } from "../lib/use-is-hydrated.js";
|
|
4
4
|
import { getClaims } from "./claims.js";
|
|
5
|
+
import type { Claims } from "./claims.js";
|
|
6
|
+
import { isClientGraphqlQueryKey } from "./client-graphql/query-keys.js";
|
|
5
7
|
|
|
6
8
|
type WidgetType =
|
|
7
9
|
| "admin-portal-audit-log-streaming"
|
|
@@ -69,6 +71,7 @@ const ApiProvider: React.FC<ApiProviderProps> = ({
|
|
|
69
71
|
widgetType,
|
|
70
72
|
}) => {
|
|
71
73
|
const queryClient = useQueryClient();
|
|
74
|
+
const queryKey = authTokenQueryKey(widgetType);
|
|
72
75
|
const authTokenQuery = useQuery<AuthTokenQueryData>({
|
|
73
76
|
initialData: () => {
|
|
74
77
|
if (typeof authToken !== "string") {
|
|
@@ -93,9 +96,40 @@ const ApiProvider: React.FC<ApiProviderProps> = ({
|
|
|
93
96
|
exp: claims.exp,
|
|
94
97
|
};
|
|
95
98
|
},
|
|
96
|
-
queryKey
|
|
99
|
+
queryKey,
|
|
97
100
|
});
|
|
98
101
|
|
|
102
|
+
// `initialData` only seeds the cache on first mount, so a new string token
|
|
103
|
+
// has to be written to the cache explicitly. A token for a different session,
|
|
104
|
+
// user or organization (e.g. after switching organizations) also invalidates
|
|
105
|
+
// data fetched with the previous token.
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
if (typeof authToken !== "string") {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const cached = queryClient.getQueryData<AuthTokenQueryData>(queryKey);
|
|
112
|
+
if (cached?.authToken === authToken) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Keep an in-flight fetch holding the previous token from overwriting the
|
|
117
|
+
// new one.
|
|
118
|
+
void queryClient.cancelQueries({ queryKey });
|
|
119
|
+
const claims = getClaims(authToken);
|
|
120
|
+
queryClient.setQueryData<AuthTokenQueryData>(queryKey, {
|
|
121
|
+
authToken,
|
|
122
|
+
permissions: claims.permissions,
|
|
123
|
+
exp: claims.exp,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (cached && !isSameSubject(getClaims(cached.authToken), claims)) {
|
|
127
|
+
void queryClient.invalidateQueries({
|
|
128
|
+
predicate: (query) => isClientGraphqlQueryKey(query.queryKey),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}, [authToken, queryClient, queryKey]);
|
|
132
|
+
|
|
99
133
|
// Keep the token "fresh" until ~30s before it expires, so window-focus
|
|
100
134
|
// re-fetches re-mint before data queries use a stale Bearer.
|
|
101
135
|
//
|
|
@@ -179,6 +213,10 @@ const ApiProvider: React.FC<ApiProviderProps> = ({
|
|
|
179
213
|
return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;
|
|
180
214
|
};
|
|
181
215
|
|
|
216
|
+
function isSameSubject(a: Claims, b: Claims) {
|
|
217
|
+
return a.sid === b.sid && a.sub === b.sub && a.org_id === b.org_id;
|
|
218
|
+
}
|
|
219
|
+
|
|
182
220
|
const useApi = () => {
|
|
183
221
|
const context = React.useContext(ApiContext);
|
|
184
222
|
|
package/src/api/claims.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
2
2
|
import type {
|
|
3
|
-
QueryKey,
|
|
4
3
|
UseMutationOptions,
|
|
5
4
|
UseQueryOptions,
|
|
6
5
|
} from "@tanstack/react-query";
|
|
@@ -8,32 +7,14 @@ import { useApiReady } from "../api-provider.js";
|
|
|
8
7
|
import { useClientGraphql } from "./client.js";
|
|
9
8
|
import type { Sdk } from "./generated.js";
|
|
10
9
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return queryKey[0] === CLIENT_GRAPHQL_KEY_PREFIX;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* React Query key for a Client GraphQL operation. Omit `variables` to address
|
|
24
|
-
* every cached page/variant of an operation, e.g. when invalidating after a
|
|
25
|
-
* mutation:
|
|
26
|
-
*
|
|
27
|
-
* queryClient.invalidateQueries({ queryKey: clientGraphqlQueryKey("Sessions") });
|
|
28
|
-
*/
|
|
29
|
-
export function clientGraphqlQueryKey<K extends ClientGraphqlOperation>(
|
|
30
|
-
operation: K,
|
|
31
|
-
variables?: ClientGraphqlVariables<K>,
|
|
32
|
-
): QueryKey {
|
|
33
|
-
return variables === undefined
|
|
34
|
-
? [CLIENT_GRAPHQL_KEY_PREFIX, operation]
|
|
35
|
-
: [CLIENT_GRAPHQL_KEY_PREFIX, operation, variables];
|
|
36
|
-
}
|
|
10
|
+
export {
|
|
11
|
+
clientGraphqlQueryKey,
|
|
12
|
+
isClientGraphqlQueryKey,
|
|
13
|
+
} from "./query-keys.js";
|
|
14
|
+
export type {
|
|
15
|
+
ClientGraphqlOperation,
|
|
16
|
+
ClientGraphqlVariables,
|
|
17
|
+
} from "./query-keys.js";
|
|
37
18
|
|
|
38
19
|
type ClientGraphqlQueryOptions<TResult, TData> = Omit<
|
|
39
20
|
UseQueryOptions<TResult, Error, TData>,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { QueryKey } from "@tanstack/react-query";
|
|
2
|
+
import type { Sdk } from "./generated.js";
|
|
3
|
+
|
|
4
|
+
export type ClientGraphqlOperation = keyof Sdk;
|
|
5
|
+
|
|
6
|
+
export type ClientGraphqlVariables<K extends ClientGraphqlOperation> =
|
|
7
|
+
Parameters<Sdk[K]>[0];
|
|
8
|
+
|
|
9
|
+
const CLIENT_GRAPHQL_KEY_PREFIX = "client-graphql";
|
|
10
|
+
|
|
11
|
+
export function isClientGraphqlQueryKey(queryKey: QueryKey): boolean {
|
|
12
|
+
return queryKey[0] === CLIENT_GRAPHQL_KEY_PREFIX;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* React Query key for a Client GraphQL operation. Omit `variables` to address
|
|
17
|
+
* every cached page/variant of an operation, e.g. when invalidating after a
|
|
18
|
+
* mutation:
|
|
19
|
+
*
|
|
20
|
+
* queryClient.invalidateQueries({ queryKey: clientGraphqlQueryKey("Sessions") });
|
|
21
|
+
*/
|
|
22
|
+
export function clientGraphqlQueryKey<K extends ClientGraphqlOperation>(
|
|
23
|
+
operation: K,
|
|
24
|
+
variables?: ClientGraphqlVariables<K>,
|
|
25
|
+
): QueryKey {
|
|
26
|
+
return variables === undefined
|
|
27
|
+
? [CLIENT_GRAPHQL_KEY_PREFIX, operation]
|
|
28
|
+
: [CLIENT_GRAPHQL_KEY_PREFIX, operation, variables];
|
|
29
|
+
}
|
|
@@ -56,7 +56,7 @@ const CopyText: React.FC<CopyTextProps> = ({ value, label }) => {
|
|
|
56
56
|
title={copyLabel}
|
|
57
57
|
variant="ghost"
|
|
58
58
|
size="1"
|
|
59
|
-
aria-
|
|
59
|
+
aria-label={copyLabel}
|
|
60
60
|
className={namespaceClassName("copy-text__copy")}
|
|
61
61
|
onClick={async (event) => {
|
|
62
62
|
const button = event.currentTarget;
|
|
@@ -68,9 +68,7 @@ const OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({
|
|
|
68
68
|
const invalidateAllWidgets = useWidgetsInvalidator();
|
|
69
69
|
const translate = useTranslation();
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
// to do in this case
|
|
73
|
-
if (!currentOrganization) {
|
|
71
|
+
if (organizations.length === 0) {
|
|
74
72
|
return (
|
|
75
73
|
<OrganizationSwitcherError
|
|
76
74
|
variant={variant}
|
|
@@ -79,12 +77,21 @@ const OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({
|
|
|
79
77
|
id: "XimGbo",
|
|
80
78
|
defaultMessage: "No organizations found",
|
|
81
79
|
description:
|
|
82
|
-
"
|
|
80
|
+
"Shown in the disabled organization switcher when the user belongs to no organizations",
|
|
83
81
|
})}
|
|
84
82
|
/>
|
|
85
83
|
);
|
|
86
84
|
}
|
|
87
85
|
|
|
86
|
+
const currentOrganizationName =
|
|
87
|
+
currentOrganization?.name ??
|
|
88
|
+
translate({
|
|
89
|
+
id: "aZ7eyn",
|
|
90
|
+
defaultMessage: "Select an organization",
|
|
91
|
+
description:
|
|
92
|
+
"Placeholder in the organization switcher trigger when the user has organizations but none is active",
|
|
93
|
+
});
|
|
94
|
+
|
|
88
95
|
return (
|
|
89
96
|
<DropdownMenu.Root>
|
|
90
97
|
<DropdownMenu.Trigger>
|
|
@@ -108,7 +115,7 @@ const OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({
|
|
|
108
115
|
minWidth="0"
|
|
109
116
|
>
|
|
110
117
|
<TruncatedOrganizationName
|
|
111
|
-
organizationName={
|
|
118
|
+
organizationName={currentOrganizationName}
|
|
112
119
|
truncateBehavior={truncateBehavior}
|
|
113
120
|
/>
|
|
114
121
|
<Flex asChild flexShrink="0">
|
|
@@ -134,7 +141,10 @@ const OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({
|
|
|
134
141
|
>
|
|
135
142
|
<DropdownMenu.Item
|
|
136
143
|
onClick={() => {
|
|
137
|
-
if (
|
|
144
|
+
if (
|
|
145
|
+
!currentOrganization ||
|
|
146
|
+
organization.id !== currentOrganization.id
|
|
147
|
+
) {
|
|
138
148
|
const result = switchToOrganization({
|
|
139
149
|
organizationId: organization.id,
|
|
140
150
|
});
|