@workos-inc/widgets 1.20.3-next.1793998900035 → 1.21.0
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 +3 -8
- package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs +2 -2
- package/dist/cjs/widgets/organization-switcher/organization-switcher.cjs.map +1 -1
- package/dist/cjs/widgets/pipes/pipes-connections.cjs +15 -15
- package/dist/cjs/widgets/pipes/pipes-connections.cjs.map +1 -1
- package/dist/cjs/widgets/pipes/pipes-credential-attribution.cjs +2 -2
- package/dist/cjs/widgets/pipes/pipes-credential-attribution.cjs.map +1 -1
- package/dist/cjs/widgets/pipes/pipes.cjs +6 -6
- package/dist/cjs/widgets/pipes/pipes.cjs.map +1 -1
- package/dist/cjs/widgets/pipes-admin/pipes-admin.cjs +15 -15
- package/dist/cjs/widgets/pipes-admin/pipes-admin.cjs.map +1 -1
- package/dist/cjs/widgets/user-sessions/user-sessions.cjs +1 -1
- package/dist/cjs/widgets/user-sessions/user-sessions.cjs.map +1 -1
- package/dist/esm/widgets/organization-switcher/organization-switcher.js +2 -2
- package/dist/esm/widgets/organization-switcher/organization-switcher.js.map +1 -1
- package/dist/esm/widgets/pipes/pipes-connections.js +15 -15
- package/dist/esm/widgets/pipes/pipes-connections.js.map +1 -1
- package/dist/esm/widgets/pipes/pipes-credential-attribution.js +2 -2
- package/dist/esm/widgets/pipes/pipes-credential-attribution.js.map +1 -1
- package/dist/esm/widgets/pipes/pipes.js +6 -6
- package/dist/esm/widgets/pipes/pipes.js.map +1 -1
- package/dist/esm/widgets/pipes-admin/pipes-admin.js +15 -15
- package/dist/esm/widgets/pipes-admin/pipes-admin.js.map +1 -1
- package/dist/esm/widgets/user-sessions/user-sessions.js +1 -1
- package/dist/esm/widgets/user-sessions/user-sessions.js.map +1 -1
- package/package.json +1 -1
- package/src/widgets/organization-switcher/organization-switcher.tsx +2 -2
- package/src/widgets/pipes/pipes-connections.tsx +15 -15
- package/src/widgets/pipes/pipes-credential-attribution.tsx +2 -2
- package/src/widgets/pipes/pipes.tsx +6 -6
- package/src/widgets/pipes-admin/pipes-admin.tsx +15 -15
- package/src/widgets/user-sessions/user-sessions.tsx +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-sessions.cjs","names":["React","Flex","Card","Box","Text","Translation","Button","LogoutDialog","LogoutAllSessionsDialog","Skeleton","IconPanel","GenericError","useTranslation","parseUserAgent","useComparativeReadableDate","MobileIcon","LaptopIcon","Badge","getWidgetDomProps"],"sources":["../../../../src/widgets/user-sessions/user-sessions.tsx"],"sourcesContent":["\"use client\";\n\nimport { LaptopIcon, MobileIcon } from \"@radix-ui/react-icons\";\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport * as React from \"react\";\nimport type { SessionFieldsFragment } from \"../../api/client-graphql/generated.js\";\nimport * as CardList from \"../../components/card-list.js\";\nimport { Badge, Button, Skeleton } from \"../../components/elements.js\";\nimport { GenericError } from \"../../components/generic-error.js\";\nimport { IconPanel } from \"../../components/icon-panel.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { parseUserAgent } from \"../../lib/parse-user-agent.js\";\nimport { useComparativeReadableDate } from \"../../lib/use-comparative-readable-date.js\";\nimport { getWidgetDomProps } from \"../../lib/widget-dom-props.js\";\nimport type {\n WidgetRootDomProps,\n WidgetRootState,\n} from \"../../lib/widget-types.js\";\nimport { LogoutAllSessionsDialog } from \"./logout-all-sessions-dialog.js\";\nimport { LogoutDialog } from \"./logout-dialog.js\";\n\ninterface UserSessionsProps extends WidgetRootDomProps {\n sessionsData: SessionFieldsFragment[];\n currentSessionId: string;\n}\n\nconst UserSessions: React.FC<UserSessionsProps> = ({\n sessionsData: sessions,\n currentSessionId,\n ...domProps\n}) => {\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: React.FC<UserSessionProps> = ({\n session,\n isCurrentSession = false,\n onSignOut,\n}) => {\n const translate = useTranslation();\n const userAgent = parseUserAgent(session.userAgent);\n const userLocation =\n getUserLocation(session.currentLocation, session.ipAddress) ||\n translate({\n defaultMessage: \"Unknown location\",\n description:\n \"Text for a session whose location could not be determined from its user agent\",\n id: \"4+P9Yz\",\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 &&\n session.lastActivityAt &&\n timeAgo.state === \"valid\" && (\n <>\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: timeAgo.formatted }}\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 getWidgetDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"user-sessions\",\n widgetState: state,\n });\n}\n\nfunction getUserLocation(\n location?: {\n cityName: string;\n countryISOCode: string;\n } | null,\n ipAddress?: string | null,\n): string | null {\n if (location) {\n return (\n [location.cityName, location.countryISOCode].filter(Boolean).join(\", \") ||\n null\n );\n }\n\n return ipAddress || null;\n}\n\nexport type { UserSessionsLoadingProps, UserSessionsErrorProps };\nexport { UserSessions, UserSessionsLoading, UserSessionsError };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"user-sessions.cjs","names":["React","Flex","Card","Box","Text","Translation","Button","LogoutDialog","LogoutAllSessionsDialog","Skeleton","IconPanel","GenericError","useTranslation","parseUserAgent","useComparativeReadableDate","MobileIcon","LaptopIcon","Badge","getWidgetDomProps"],"sources":["../../../../src/widgets/user-sessions/user-sessions.tsx"],"sourcesContent":["\"use client\";\n\nimport { LaptopIcon, MobileIcon } from \"@radix-ui/react-icons\";\nimport { Box, Card, Flex, Text } from \"@radix-ui/themes\";\nimport * as React from \"react\";\nimport type { SessionFieldsFragment } from \"../../api/client-graphql/generated.js\";\nimport * as CardList from \"../../components/card-list.js\";\nimport { Badge, Button, Skeleton } from \"../../components/elements.js\";\nimport { GenericError } from \"../../components/generic-error.js\";\nimport { IconPanel } from \"../../components/icon-panel.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { parseUserAgent } from \"../../lib/parse-user-agent.js\";\nimport { useComparativeReadableDate } from \"../../lib/use-comparative-readable-date.js\";\nimport { getWidgetDomProps } from \"../../lib/widget-dom-props.js\";\nimport type {\n WidgetRootDomProps,\n WidgetRootState,\n} from \"../../lib/widget-types.js\";\nimport { LogoutAllSessionsDialog } from \"./logout-all-sessions-dialog.js\";\nimport { LogoutDialog } from \"./logout-dialog.js\";\n\ninterface UserSessionsProps extends WidgetRootDomProps {\n sessionsData: SessionFieldsFragment[];\n currentSessionId: string;\n}\n\nconst UserSessions: React.FC<UserSessionsProps> = ({\n sessionsData: sessions,\n currentSessionId,\n ...domProps\n}) => {\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: React.FC<UserSessionProps> = ({\n session,\n isCurrentSession = false,\n onSignOut,\n}) => {\n const translate = useTranslation();\n const userAgent = parseUserAgent(session.userAgent);\n const userLocation =\n getUserLocation(session.currentLocation, session.ipAddress) ||\n translate({\n defaultMessage: \"Unknown location\",\n description:\n \"Text for a session whose location could not be determined from its user agent\",\n id: \"uMbGX5\",\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 &&\n session.lastActivityAt &&\n timeAgo.state === \"valid\" && (\n <>\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: timeAgo.formatted }}\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 getWidgetDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"user-sessions\",\n widgetState: state,\n });\n}\n\nfunction getUserLocation(\n location?: {\n cityName: string;\n countryISOCode: string;\n } | null,\n ipAddress?: string | null,\n): string | null {\n if (location) {\n return (\n [location.cityName, location.countryISOCode].filter(Boolean).join(\", \") ||\n null\n );\n }\n\n return ipAddress || null;\n}\n\nexport type { UserSessionsLoadingProps, UserSessionsErrorProps };\nexport { UserSessions, UserSessionsLoading, UserSessionsError };\n"],"mappings":"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"}
|
|
@@ -22,13 +22,13 @@ const OrganizationSwitcher = ({ organizations, switchToOrganization, variant, or
|
|
|
22
22
|
variant,
|
|
23
23
|
...domProps,
|
|
24
24
|
message: translate({
|
|
25
|
-
id: "
|
|
25
|
+
id: "aZ7eyn",
|
|
26
26
|
defaultMessage: "No organizations found",
|
|
27
27
|
description: "Shown in the disabled organization switcher when the user belongs to no organizations"
|
|
28
28
|
})
|
|
29
29
|
});
|
|
30
30
|
const currentOrganizationName = currentOrganization?.name ?? translate({
|
|
31
|
-
id: "
|
|
31
|
+
id: "OLi4Va",
|
|
32
32
|
defaultMessage: "Select an organization",
|
|
33
33
|
description: "Placeholder in the organization switcher trigger when the user has organizations but none is active"
|
|
34
34
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organization-switcher.js","names":["DropdownMenu.Root","DropdownMenu.Trigger","Button","DropdownMenu.TriggerIcon","DropdownMenu.Content","DropdownMenu.Group","DropdownMenu.Label","DropdownMenu.Item","Skeleton"],"sources":["../../../../src/widgets/organization-switcher/organization-switcher.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckIcon } from \"@radix-ui/react-icons\";\nimport {\n Box,\n ChevronDownIcon,\n Flex,\n Text,\n VisuallyHidden,\n} from \"@radix-ui/themes\";\nimport cx from \"clsx\";\nimport type { ListUserOrganizationsQuery } from \"../../api/client-graphql/generated.js\";\nimport { Button, DropdownMenu, Skeleton } from \"../../components/elements.js\";\nimport type { ButtonProps } from \"../../components/elements.js\";\nimport { getErrorMessage } from \"../../components/generic-error.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { useWidgetsInvalidator } from \"../../lib/use-widgets-invalidator.js\";\nimport { isPromiseLike } from \"../../lib/utils.js\";\nimport { getWidgetDomProps } from \"../../lib/widget-dom-props.js\";\nimport type {\n WidgetRootDomProps,\n WidgetRootState,\n} from \"../../lib/widget-types.js\";\n\ntype OrganizationSwitcherVariant = \"ghost\" | \"outline\";\n\n// Rename all uses of `org` to `organization`\ninterface OrganizationSwitcherPassthroughProps extends WidgetRootDomProps {\n switchToOrganization: ({\n organizationId,\n }: {\n organizationId: string;\n }) => void | Promise<void>;\n // Simple props to affect the overall style\n variant?: OrganizationSwitcherVariant;\n organizationLabel?: string | null;\n children?: React.ReactNode;\n /**\n * Choose where to truncate organization name in the trigger and dropdown\n * items.\n *\n * - `right`: Truncate the right side of the organization name\n * - `middle`: Truncate the middle of the organization name, trying to keep\n * words whole\n */\n truncateBehavior?: \"right\" | \"middle\";\n}\n\ntype UserOrganization = ListUserOrganizationsQuery[\"organizations\"][number];\n\ninterface OrganizationSwitcherProps extends OrganizationSwitcherPassthroughProps {\n organizations: UserOrganization[];\n}\n\nconst OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({\n organizations,\n switchToOrganization,\n variant,\n organizationLabel = \"Organizations\",\n truncateBehavior = \"right\",\n children,\n ...domProps\n}) => {\n const currentOrganization = organizations.find(\n (organization) => organization.isCurrent,\n );\n const invalidateAllWidgets = useWidgetsInvalidator();\n const translate = useTranslation();\n\n if (organizations.length === 0) {\n return (\n <OrganizationSwitcherError\n variant={variant}\n {...domProps}\n message={translate({\n id: \"XimGbo\",\n defaultMessage: \"No organizations found\",\n description:\n \"Shown in the disabled organization switcher when the user belongs to no organizations\",\n })}\n />\n );\n }\n\n const currentOrganizationName =\n currentOrganization?.name ??\n translate({\n id: \"aZ7eyn\",\n defaultMessage: \"Select an organization\",\n description:\n \"Placeholder in the organization switcher trigger when the user has organizations but none is active\",\n });\n\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger>\n <Button\n variant=\"secondary\"\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"resolved\", {\n ...domProps,\n // TODO(v2): Remove `OrganizationSwitcherTrigger` in the next major\n // version. This should follow conventions of all other widgets\n // using classnames in getWidgetRootDomProps.\n className: cx(domProps.className, \"OrganizationSwitcherTrigger\"),\n })}\n >\n <Flex\n align=\"center\"\n justify=\"between\"\n gap=\"2\"\n flexGrow=\"1\"\n overflow=\"hidden\"\n minWidth=\"0\"\n >\n <TruncatedOrganizationName\n organizationName={currentOrganizationName}\n truncateBehavior={truncateBehavior}\n />\n <Flex asChild flexShrink=\"0\">\n <DropdownMenu.TriggerIcon />\n </Flex>\n </Flex>\n </Button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Content>\n <DropdownMenu.Group>\n {organizationLabel ? (\n <DropdownMenu.Label>\n <Text>{organizationLabel}</Text>\n </DropdownMenu.Label>\n ) : null}\n {organizations.map((organization) => (\n <Flex\n key={organization.id}\n asChild\n pr=\"2\"\n maxWidth=\"280px\"\n minWidth=\"180px\"\n >\n <DropdownMenu.Item\n onClick={() => {\n if (\n !currentOrganization ||\n organization.id !== currentOrganization.id\n ) {\n const result = switchToOrganization({\n organizationId: organization.id,\n });\n if (isPromiseLike(result)) {\n result.then(invalidateAllWidgets);\n } else {\n invalidateAllWidgets();\n }\n }\n }}\n >\n <Flex\n justify=\"between\"\n align=\"center\"\n gap=\"4\"\n flexGrow=\"1\"\n overflow=\"hidden\"\n >\n <TruncatedOrganizationName\n organizationName={organization.name}\n truncateBehavior={truncateBehavior}\n />\n {organization.isCurrent && (\n <VisuallyHidden>\n <Translation\n defaultMessage=\"(current)\"\n id=\"daFXTP\"\n description=\"Label for screen readers indicating current organization\"\n />\n </VisuallyHidden>\n )}\n <Flex\n aria-hidden\n align=\"center\"\n justify=\"center\"\n flexShrink=\"0\"\n >\n {organization.isCurrent ? (\n <CheckIcon width=\"18px\" height=\"18px\" />\n ) : (\n // make the extra space for\n <Box width=\"18px\" height=\"18px\" />\n )}\n </Flex>\n </Flex>\n </DropdownMenu.Item>\n </Flex>\n ))}\n </DropdownMenu.Group>\n {children}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n};\n\ninterface OrganizationSwitcherLoadingProps extends WidgetRootDomProps {\n variant?: OrganizationSwitcherVariant;\n}\n\nconst OrganizationSwitcherLoading: React.FC<\n OrganizationSwitcherLoadingProps\n> = ({ variant, ...props }: OrganizationSwitcherLoadingProps) => {\n return (\n // Always need DropdownMenu.Root to wrap children than may include\n <Button\n variant=\"secondary\"\n disabled\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"loading\", props)}\n >\n <Flex align=\"center\" gap=\"2\">\n <Skeleton>\n <Text>\n <Translation\n defaultMessage=\"Loading...\"\n id=\"MPkIXK\"\n description=\"Placeholder text while organizations load\"\n />\n </Text>\n </Skeleton>\n <ChevronDownIcon />\n </Flex>\n </Button>\n );\n};\n\ninterface OrganizationSwitcherErrorProps extends WidgetRootDomProps {\n error?: unknown;\n message?: string;\n variant?: OrganizationSwitcherVariant;\n}\n\nconst OrganizationSwitcherError: React.FC<OrganizationSwitcherErrorProps> = ({\n error,\n variant,\n message,\n ...domProps\n}) => {\n const translate = useTranslation();\n const heading = message || getErrorMessage(error, translate).heading;\n\n return (\n <Button\n variant=\"secondary\"\n disabled\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"error\", domProps)}\n >\n <Flex align=\"center\" gap=\"2\">\n <Text>{heading}</Text>\n <ChevronDownIcon />\n </Flex>\n </Button>\n );\n};\n\nconst MAX_TRUNCATED_SUFFIX_LENGTH = 10;\nconst MIN_TRUNCATED_SUFFIX_LENGTH = 3;\n\nconst WHITE_SPACE_REGEX = /\\s/;\n\nconst getSplitPosition = (organizationName: string) => {\n if (organizationName.length <= MAX_TRUNCATED_SUFFIX_LENGTH) {\n return organizationName.length;\n }\n\n for (\n let i = organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n i < organizationName.length - MIN_TRUNCATED_SUFFIX_LENGTH;\n i++\n ) {\n if (WHITE_SPACE_REGEX.test(organizationName[i - 1])) {\n return i;\n }\n }\n\n return organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n};\n\nconst splitOrganizationName = (organizationName: string) => {\n const splitPosition = getSplitPosition(organizationName);\n return [\n organizationName.slice(0, splitPosition),\n organizationName.slice(splitPosition),\n ];\n};\n\ninterface TruncatedOrganizationNameProps {\n organizationName: string;\n truncateBehavior: \"right\" | \"middle\";\n}\n\nconst TruncatedOrganizationName: React.FC<TruncatedOrganizationNameProps> = ({\n organizationName,\n truncateBehavior,\n}) => {\n if (truncateBehavior === \"right\") {\n return <Text truncate>{organizationName}</Text>;\n }\n\n const [organizationNameLeft, organizationNameRight] =\n splitOrganizationName(organizationName);\n\n return (\n <Flex overflow=\"hidden\">\n <VisuallyHidden>\n <Text>{organizationName}</Text>\n </VisuallyHidden>\n <Text aria-hidden truncate style={{ whiteSpace: \"pre\" }}>\n {organizationNameLeft}\n </Text>\n <Text aria-hidden>{organizationNameRight}</Text>\n </Flex>\n );\n};\n\nfunction mapVariantProps(variant?: OrganizationSwitcherVariant): ButtonProps {\n const variantProps: ButtonProps = {};\n // Passing `undefined` to the variant prop will result in overrides to props we\n // set internally. This should be addressed in Radix Themes, but we can\n // explicitly set it only when it's provided and _not_ undefined.\n if (variant) {\n variantProps.unsafe_radixVariant = variant;\n }\n return variantProps;\n}\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getWidgetDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"organization-switcher\",\n widgetState: state,\n });\n}\nexport type {\n OrganizationSwitcherLoadingProps,\n OrganizationSwitcherErrorProps,\n OrganizationSwitcherPassthroughProps,\n UserOrganization,\n};\nexport {\n OrganizationSwitcher,\n OrganizationSwitcherLoading,\n OrganizationSwitcherError,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"organization-switcher.js","names":["DropdownMenu.Root","DropdownMenu.Trigger","Button","DropdownMenu.TriggerIcon","DropdownMenu.Content","DropdownMenu.Group","DropdownMenu.Label","DropdownMenu.Item","Skeleton"],"sources":["../../../../src/widgets/organization-switcher/organization-switcher.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckIcon } from \"@radix-ui/react-icons\";\nimport {\n Box,\n ChevronDownIcon,\n Flex,\n Text,\n VisuallyHidden,\n} from \"@radix-ui/themes\";\nimport cx from \"clsx\";\nimport type { ListUserOrganizationsQuery } from \"../../api/client-graphql/generated.js\";\nimport { Button, DropdownMenu, Skeleton } from \"../../components/elements.js\";\nimport type { ButtonProps } from \"../../components/elements.js\";\nimport { getErrorMessage } from \"../../components/generic-error.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport { useWidgetsInvalidator } from \"../../lib/use-widgets-invalidator.js\";\nimport { isPromiseLike } from \"../../lib/utils.js\";\nimport { getWidgetDomProps } from \"../../lib/widget-dom-props.js\";\nimport type {\n WidgetRootDomProps,\n WidgetRootState,\n} from \"../../lib/widget-types.js\";\n\ntype OrganizationSwitcherVariant = \"ghost\" | \"outline\";\n\n// Rename all uses of `org` to `organization`\ninterface OrganizationSwitcherPassthroughProps extends WidgetRootDomProps {\n switchToOrganization: ({\n organizationId,\n }: {\n organizationId: string;\n }) => void | Promise<void>;\n // Simple props to affect the overall style\n variant?: OrganizationSwitcherVariant;\n organizationLabel?: string | null;\n children?: React.ReactNode;\n /**\n * Choose where to truncate organization name in the trigger and dropdown\n * items.\n *\n * - `right`: Truncate the right side of the organization name\n * - `middle`: Truncate the middle of the organization name, trying to keep\n * words whole\n */\n truncateBehavior?: \"right\" | \"middle\";\n}\n\ntype UserOrganization = ListUserOrganizationsQuery[\"organizations\"][number];\n\ninterface OrganizationSwitcherProps extends OrganizationSwitcherPassthroughProps {\n organizations: UserOrganization[];\n}\n\nconst OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({\n organizations,\n switchToOrganization,\n variant,\n organizationLabel = \"Organizations\",\n truncateBehavior = \"right\",\n children,\n ...domProps\n}) => {\n const currentOrganization = organizations.find(\n (organization) => organization.isCurrent,\n );\n const invalidateAllWidgets = useWidgetsInvalidator();\n const translate = useTranslation();\n\n if (organizations.length === 0) {\n return (\n <OrganizationSwitcherError\n variant={variant}\n {...domProps}\n message={translate({\n id: \"aZ7eyn\",\n defaultMessage: \"No organizations found\",\n description:\n \"Shown in the disabled organization switcher when the user belongs to no organizations\",\n })}\n />\n );\n }\n\n const currentOrganizationName =\n currentOrganization?.name ??\n translate({\n id: \"OLi4Va\",\n defaultMessage: \"Select an organization\",\n description:\n \"Placeholder in the organization switcher trigger when the user has organizations but none is active\",\n });\n\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger>\n <Button\n variant=\"secondary\"\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"resolved\", {\n ...domProps,\n // TODO(v2): Remove `OrganizationSwitcherTrigger` in the next major\n // version. This should follow conventions of all other widgets\n // using classnames in getWidgetRootDomProps.\n className: cx(domProps.className, \"OrganizationSwitcherTrigger\"),\n })}\n >\n <Flex\n align=\"center\"\n justify=\"between\"\n gap=\"2\"\n flexGrow=\"1\"\n overflow=\"hidden\"\n minWidth=\"0\"\n >\n <TruncatedOrganizationName\n organizationName={currentOrganizationName}\n truncateBehavior={truncateBehavior}\n />\n <Flex asChild flexShrink=\"0\">\n <DropdownMenu.TriggerIcon />\n </Flex>\n </Flex>\n </Button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Content>\n <DropdownMenu.Group>\n {organizationLabel ? (\n <DropdownMenu.Label>\n <Text>{organizationLabel}</Text>\n </DropdownMenu.Label>\n ) : null}\n {organizations.map((organization) => (\n <Flex\n key={organization.id}\n asChild\n pr=\"2\"\n maxWidth=\"280px\"\n minWidth=\"180px\"\n >\n <DropdownMenu.Item\n onClick={() => {\n if (\n !currentOrganization ||\n organization.id !== currentOrganization.id\n ) {\n const result = switchToOrganization({\n organizationId: organization.id,\n });\n if (isPromiseLike(result)) {\n result.then(invalidateAllWidgets);\n } else {\n invalidateAllWidgets();\n }\n }\n }}\n >\n <Flex\n justify=\"between\"\n align=\"center\"\n gap=\"4\"\n flexGrow=\"1\"\n overflow=\"hidden\"\n >\n <TruncatedOrganizationName\n organizationName={organization.name}\n truncateBehavior={truncateBehavior}\n />\n {organization.isCurrent && (\n <VisuallyHidden>\n <Translation\n defaultMessage=\"(current)\"\n id=\"daFXTP\"\n description=\"Label for screen readers indicating current organization\"\n />\n </VisuallyHidden>\n )}\n <Flex\n aria-hidden\n align=\"center\"\n justify=\"center\"\n flexShrink=\"0\"\n >\n {organization.isCurrent ? (\n <CheckIcon width=\"18px\" height=\"18px\" />\n ) : (\n // make the extra space for\n <Box width=\"18px\" height=\"18px\" />\n )}\n </Flex>\n </Flex>\n </DropdownMenu.Item>\n </Flex>\n ))}\n </DropdownMenu.Group>\n {children}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n};\n\ninterface OrganizationSwitcherLoadingProps extends WidgetRootDomProps {\n variant?: OrganizationSwitcherVariant;\n}\n\nconst OrganizationSwitcherLoading: React.FC<\n OrganizationSwitcherLoadingProps\n> = ({ variant, ...props }: OrganizationSwitcherLoadingProps) => {\n return (\n // Always need DropdownMenu.Root to wrap children than may include\n <Button\n variant=\"secondary\"\n disabled\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"loading\", props)}\n >\n <Flex align=\"center\" gap=\"2\">\n <Skeleton>\n <Text>\n <Translation\n defaultMessage=\"Loading...\"\n id=\"MPkIXK\"\n description=\"Placeholder text while organizations load\"\n />\n </Text>\n </Skeleton>\n <ChevronDownIcon />\n </Flex>\n </Button>\n );\n};\n\ninterface OrganizationSwitcherErrorProps extends WidgetRootDomProps {\n error?: unknown;\n message?: string;\n variant?: OrganizationSwitcherVariant;\n}\n\nconst OrganizationSwitcherError: React.FC<OrganizationSwitcherErrorProps> = ({\n error,\n variant,\n message,\n ...domProps\n}) => {\n const translate = useTranslation();\n const heading = message || getErrorMessage(error, translate).heading;\n\n return (\n <Button\n variant=\"secondary\"\n disabled\n {...mapVariantProps(variant)}\n {...getWidgetRootDomProps(\"error\", domProps)}\n >\n <Flex align=\"center\" gap=\"2\">\n <Text>{heading}</Text>\n <ChevronDownIcon />\n </Flex>\n </Button>\n );\n};\n\nconst MAX_TRUNCATED_SUFFIX_LENGTH = 10;\nconst MIN_TRUNCATED_SUFFIX_LENGTH = 3;\n\nconst WHITE_SPACE_REGEX = /\\s/;\n\nconst getSplitPosition = (organizationName: string) => {\n if (organizationName.length <= MAX_TRUNCATED_SUFFIX_LENGTH) {\n return organizationName.length;\n }\n\n for (\n let i = organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n i < organizationName.length - MIN_TRUNCATED_SUFFIX_LENGTH;\n i++\n ) {\n if (WHITE_SPACE_REGEX.test(organizationName[i - 1])) {\n return i;\n }\n }\n\n return organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n};\n\nconst splitOrganizationName = (organizationName: string) => {\n const splitPosition = getSplitPosition(organizationName);\n return [\n organizationName.slice(0, splitPosition),\n organizationName.slice(splitPosition),\n ];\n};\n\ninterface TruncatedOrganizationNameProps {\n organizationName: string;\n truncateBehavior: \"right\" | \"middle\";\n}\n\nconst TruncatedOrganizationName: React.FC<TruncatedOrganizationNameProps> = ({\n organizationName,\n truncateBehavior,\n}) => {\n if (truncateBehavior === \"right\") {\n return <Text truncate>{organizationName}</Text>;\n }\n\n const [organizationNameLeft, organizationNameRight] =\n splitOrganizationName(organizationName);\n\n return (\n <Flex overflow=\"hidden\">\n <VisuallyHidden>\n <Text>{organizationName}</Text>\n </VisuallyHidden>\n <Text aria-hidden truncate style={{ whiteSpace: \"pre\" }}>\n {organizationNameLeft}\n </Text>\n <Text aria-hidden>{organizationNameRight}</Text>\n </Flex>\n );\n};\n\nfunction mapVariantProps(variant?: OrganizationSwitcherVariant): ButtonProps {\n const variantProps: ButtonProps = {};\n // Passing `undefined` to the variant prop will result in overrides to props we\n // set internally. This should be addressed in Radix Themes, but we can\n // explicitly set it only when it's provided and _not_ undefined.\n if (variant) {\n variantProps.unsafe_radixVariant = variant;\n }\n return variantProps;\n}\n\nfunction getWidgetRootDomProps(\n state: WidgetRootState,\n domProps: WidgetRootDomProps,\n) {\n return getWidgetDomProps({\n ...domProps,\n isWidgetRoot: true,\n widgetId: \"organization-switcher\",\n widgetState: state,\n });\n}\nexport type {\n OrganizationSwitcherLoadingProps,\n OrganizationSwitcherErrorProps,\n OrganizationSwitcherPassthroughProps,\n UserOrganization,\n};\nexport {\n OrganizationSwitcher,\n OrganizationSwitcherLoading,\n OrganizationSwitcherError,\n};\n"],"mappings":"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"}
|
|
@@ -136,7 +136,7 @@ function toAccountDisplayNameInput(name) {
|
|
|
136
136
|
const ConnectionNameField = ({ id, value, onChange }) => {
|
|
137
137
|
const placeholder = useTranslation()({
|
|
138
138
|
defaultMessage: "e.g. Marketing",
|
|
139
|
-
id: "
|
|
139
|
+
id: "pWC98E",
|
|
140
140
|
description: "Placeholder for the connection name input"
|
|
141
141
|
});
|
|
142
142
|
return /* @__PURE__ */ jsxs(Flex, {
|
|
@@ -149,7 +149,7 @@ const ConnectionNameField = ({ id, value, onChange }) => {
|
|
|
149
149
|
htmlFor: id,
|
|
150
150
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
151
151
|
defaultMessage: "Name",
|
|
152
|
-
id: "
|
|
152
|
+
id: "KdgU9T",
|
|
153
153
|
description: "Label for the connection name input"
|
|
154
154
|
})
|
|
155
155
|
}), /* @__PURE__ */ jsx(Text, {
|
|
@@ -157,7 +157,7 @@ const ConnectionNameField = ({ id, value, onChange }) => {
|
|
|
157
157
|
color: "gray",
|
|
158
158
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
159
159
|
defaultMessage: "Optional",
|
|
160
|
-
id: "
|
|
160
|
+
id: "2Yy441",
|
|
161
161
|
description: "Hint beside the connection name input saying it can be left empty"
|
|
162
162
|
})
|
|
163
163
|
})]
|
|
@@ -189,7 +189,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
189
189
|
const errorMessage = error instanceof ApiError && error.message ? error.message : null;
|
|
190
190
|
const placeholder = translate({
|
|
191
191
|
defaultMessage: "e.g. Marketing",
|
|
192
|
-
id: "
|
|
192
|
+
id: "pWC98E",
|
|
193
193
|
description: "Placeholder for the connection name input"
|
|
194
194
|
});
|
|
195
195
|
return /* @__PURE__ */ jsx(Root, {
|
|
@@ -203,7 +203,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
203
203
|
weight: "bold",
|
|
204
204
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
205
205
|
defaultMessage: "Rename {integrationName} connection",
|
|
206
|
-
id: "
|
|
206
|
+
id: "c1jT5Q",
|
|
207
207
|
description: "Dialog title for renaming one connection to a provider",
|
|
208
208
|
values: { integrationName }
|
|
209
209
|
})
|
|
@@ -213,7 +213,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
213
213
|
mt: "1",
|
|
214
214
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
215
215
|
defaultMessage: "Give this connection a name to tell it apart from your other {integrationName} connections.",
|
|
216
|
-
id: "
|
|
216
|
+
id: "Ptf2r1",
|
|
217
217
|
description: "Explains what a connection name is for",
|
|
218
218
|
values: { integrationName }
|
|
219
219
|
})
|
|
@@ -224,7 +224,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
224
224
|
role: "alert",
|
|
225
225
|
children: /* @__PURE__ */ jsx(Callout.Text, { children: errorMessage ?? /* @__PURE__ */ jsx(Translation, {
|
|
226
226
|
defaultMessage: "An error occurred while renaming this connection. Please try again.",
|
|
227
|
-
id: "
|
|
227
|
+
id: "0grPwn",
|
|
228
228
|
description: "Error message when renaming a connection fails"
|
|
229
229
|
}) })
|
|
230
230
|
}),
|
|
@@ -234,7 +234,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
234
234
|
role: "status",
|
|
235
235
|
children: /* @__PURE__ */ jsx(Callout.Text, { children: warning.code === DUPLICATE_LABEL_WARNING ? /* @__PURE__ */ jsx(Translation, {
|
|
236
236
|
defaultMessage: "Saved. Another {integrationName} connection already uses this name, so you may want to pick one that tells them apart.",
|
|
237
|
-
id: "
|
|
237
|
+
id: "eB4eyQ",
|
|
238
238
|
description: "Notice after renaming a connection to a name another connection to the same provider already has",
|
|
239
239
|
values: { integrationName }
|
|
240
240
|
}) : warning.message })
|
|
@@ -258,7 +258,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
258
258
|
htmlFor: `rename-connection-${installation.id}`,
|
|
259
259
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
260
260
|
defaultMessage: "Name",
|
|
261
|
-
id: "
|
|
261
|
+
id: "KdgU9T",
|
|
262
262
|
description: "Label for the connection name input"
|
|
263
263
|
})
|
|
264
264
|
}), /* @__PURE__ */ jsx(TextFieldRoot, {
|
|
@@ -278,7 +278,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
278
278
|
variant: "secondary",
|
|
279
279
|
children: warnings.length > 0 ? /* @__PURE__ */ jsx(Translation, {
|
|
280
280
|
defaultMessage: "Done",
|
|
281
|
-
id: "
|
|
281
|
+
id: "YlARFG",
|
|
282
282
|
description: "Button to close the rename dialog after a name was saved with a warning"
|
|
283
283
|
}) : /* @__PURE__ */ jsx(Translation, {
|
|
284
284
|
defaultMessage: "Cancel",
|
|
@@ -291,7 +291,7 @@ const RenameConnectionDialog = ({ integrationName, installation, owner = "user",
|
|
|
291
291
|
loading: isPending,
|
|
292
292
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
293
293
|
defaultMessage: "Save",
|
|
294
|
-
id: "
|
|
294
|
+
id: "JmvID+",
|
|
295
295
|
description: "Button to save a connection's new name"
|
|
296
296
|
})
|
|
297
297
|
})]
|
|
@@ -311,7 +311,7 @@ const ConnectionCountStatus = ({ count }) => {
|
|
|
311
311
|
state: "neutral",
|
|
312
312
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
313
313
|
defaultMessage: "{count} connections",
|
|
314
|
-
id: "
|
|
314
|
+
id: "ZarniP",
|
|
315
315
|
description: "Status for a provider grouping two or more connections, giving how many",
|
|
316
316
|
values: { count }
|
|
317
317
|
})
|
|
@@ -320,21 +320,21 @@ const ConnectionCountStatus = ({ count }) => {
|
|
|
320
320
|
const RenameMenuLabel = () => {
|
|
321
321
|
return /* @__PURE__ */ jsx(Translation, {
|
|
322
322
|
defaultMessage: "Rename",
|
|
323
|
-
id: "
|
|
323
|
+
id: "iiQbUM",
|
|
324
324
|
description: "Menu option to rename one connection"
|
|
325
325
|
});
|
|
326
326
|
};
|
|
327
327
|
const ConnectAnotherAccountMenuLabel = () => {
|
|
328
328
|
return /* @__PURE__ */ jsx(Translation, {
|
|
329
329
|
defaultMessage: "Connect another account",
|
|
330
|
-
id: "
|
|
330
|
+
id: "SNK0to",
|
|
331
331
|
description: "Menu option to add another connection to the same provider"
|
|
332
332
|
});
|
|
333
333
|
};
|
|
334
334
|
const AddConnectionButtonLabel = () => {
|
|
335
335
|
return /* @__PURE__ */ jsx(Translation, {
|
|
336
336
|
defaultMessage: "Add connection",
|
|
337
|
-
id: "
|
|
337
|
+
id: "DFzo7c",
|
|
338
338
|
description: "Button to add another connection to a provider that already has several"
|
|
339
339
|
});
|
|
340
340
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipes-connections.js","names":["TextField.Root","Dialog.Root","Dialog.Content","Dialog.Title","Dialog.Description","Dialog.Close","Button"],"sources":["../../../../src/widgets/pipes/pipes-connections.tsx"],"sourcesContent":["\"use client\";\nimport { Callout, Flex, Text } from \"@radix-ui/themes\";\nimport { useEffect, useState } from \"react\";\nimport type {\n ConnectionIntent,\n DataProviderConnectionLabelResultFieldsFragment,\n UpdateDataProviderConnectionLabelInput,\n} from \"../../api/client-graphql/generated.js\";\nimport { useClientGraphqlMutation } from \"../../api/client-graphql/hooks.js\";\nimport { ApiError } from \"../../api/errors.js\";\nimport { Button, Dialog, Label, TextField } from \"../../components/elements.js\";\nimport { Status } from \"../../components/status.js\";\nimport { useInvalidateConnections } from \"../../lib/api/pipes.js\";\nimport type { DataInstallation } from \"../../lib/api/types.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport type { ConnectionOwner } from \"./pipes.js\";\n\n/**\n * Multiple connections per provider, shared by the member and administrator\n * widgets: naming a connection, targeting the connect flows at one exact\n * connection (or at a new one), and renaming.\n */\n\n/**\n * How one connection is named within its provider's group: the label someone\n * gave it, then the provider account it disclosed, then the provider itself.\n */\nfunction getConnectionLabel(\n integrationName: string,\n installation: DataInstallation,\n): string {\n return (\n installation.account_display_name?.trim() ||\n installation.account_identifier ||\n integrationName\n );\n}\n\n/**\n * Which connection a connect flow writes. Left unset, the flows keep the API's\n * compatibility behavior: they write the owner's single compatibility\n * connection, creating it if there is none.\n */\ntype ConnectionTarget =\n /**\n * Create another connection alongside the owner's existing ones.\n * `idempotencyKey` identifies one logical Add, so a retry after an\n * ambiguous failure returns the connection the first attempt created\n * instead of creating a second (see {@link createAddConnectionTarget}).\n */\n | { intent: \"Add\"; idempotencyKey?: string }\n /** Re-authorize, or replace the credentials of, one exact connection. */\n | { intent: \"Reauthorize\"; connectedAccountId: string };\n\n/** Requests an Add; the flow that runs it mints the idempotency key. */\nconst ADD_CONNECTION: ConnectionTarget = { intent: \"Add\" };\n\n/**\n * A random key for one logical Add. `crypto.randomUUID` needs a secure\n * context, so plain-HTTP hosts fall back to `getRandomValues`, which doesn't.\n */\nfunction createIdempotencyKey(): string {\n if (typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n}\n\n/**\n * The target for one logical Add, carrying a fresh idempotency key. Mint it\n * once when the Add starts and reuse it for every retry of that Add; a\n * deliberate second Add gets its own. The key is only honored with the Add\n * intent, and within 24 hours of the first attempt.\n */\nfunction createAddConnectionTarget(): ConnectionTarget {\n return { intent: \"Add\", idempotencyKey: createIdempotencyKey() };\n}\n\n/**\n * The target for writing an existing connection's credentials. Standard\n * connections are only reachable by naming them; the compatibility connection\n * is what an untargeted write already means, and leaving the intent off keeps\n * working where the environment has multiple connections turned off (the API\n * refuses explicit intents there).\n */\nfunction getConnectionTarget(\n installation: DataInstallation | null | undefined,\n): ConnectionTarget | undefined {\n return installation?.connection_role === \"standard\"\n ? { intent: \"Reauthorize\", connectedAccountId: installation.id }\n : undefined;\n}\n\n/**\n * Whether a provider's connections can be renamed: only once it can hold more\n * than one, since a lone connection's name is never shown beside another's.\n * The API reports no setting for this, so it's inferred: either an add is\n * available, or a standard connection exists, which only multiple connections\n * create (and which any provider with several connections has, at the cap\n * too).\n */\nfunction canRenameConnections(integration: {\n capabilities: { canAddConnection: boolean };\n connected_accounts: DataInstallation[];\n}): boolean {\n return (\n integration.capabilities.canAddConnection ||\n integration.connected_accounts.some(\n (connection) => connection.connection_role === \"standard\",\n )\n );\n}\n\n/**\n * The `connectionIntent` / `connectedAccountId` fields of a connect input.\n * Every connect input takes these, including OAuth authorization, which has\n * no idempotency key — see {@link toCreationTargetInput} for the inputs that do.\n */\nfunction toConnectionTargetInput(target: ConnectionTarget | undefined): {\n connectionIntent?: ConnectionIntent;\n connectedAccountId?: string;\n} {\n if (!target) {\n return {};\n }\n return target.intent === \"Add\"\n ? { connectionIntent: \"Add\" }\n : {\n connectionIntent: \"Reauthorize\",\n connectedAccountId: target.connectedAccountId,\n };\n}\n\n/**\n * {@link toConnectionTargetInput} plus the Add's `idempotencyKey`, for the\n * synchronous creation inputs (API key and client credentials) that accept it.\n * Never spread this into an authorization input: the API rejects the field.\n */\nfunction toCreationTargetInput(target: ConnectionTarget | undefined): {\n connectionIntent?: ConnectionIntent;\n connectedAccountId?: string;\n idempotencyKey?: string;\n} {\n const input = toConnectionTargetInput(target);\n return target?.intent === \"Add\" && target.idempotencyKey\n ? { ...input, idempotencyKey: target.idempotencyKey }\n : input;\n}\n\n/**\n * Saves a connection's label through the owner's mutation (`null` clears it\n * back to the computed fallback) and refreshes the owner's lists.\n */\nfunction useRenameConnection(owner: ConnectionOwner) {\n const invalidateConnections = useInvalidateConnections(owner);\n return useClientGraphqlMutation({\n mutationFn: async (\n sdk,\n input: UpdateDataProviderConnectionLabelInput,\n ): Promise<DataProviderConnectionLabelResultFieldsFragment> => {\n if (owner === \"organization\") {\n const { updateOrganizationDataProviderConnectionLabel } =\n await sdk.UpdateOrganizationDataProviderConnectionLabel({ input });\n return updateOrganizationDataProviderConnectionLabel;\n }\n const { updateDataProviderConnectionLabel } =\n await sdk.UpdateDataProviderConnectionLabel({ input });\n return updateDataProviderConnectionLabel;\n },\n onSuccess: invalidateConnections,\n });\n}\n\n/** The label code the API warns with when a sibling already uses the name. */\nconst DUPLICATE_LABEL_WARNING = \"duplicate_account_display_name\";\n\n/** Maximum label length the API accepts (PVIT-904). */\nconst MAX_LABEL_LENGTH = 255;\n\n/**\n * The `accountDisplayName` field of a creation input: the trimmed name when\n * one was entered, otherwise nothing, so an untouched field never sends an\n * empty label.\n */\nfunction toAccountDisplayNameInput(name: string | undefined): {\n accountDisplayName?: string;\n} {\n const trimmed = name?.trim();\n return trimmed ? { accountDisplayName: trimmed } : {};\n}\n\ninterface ConnectionNameFieldProps {\n id: string;\n value: string;\n onChange: (value: string) => void;\n}\n\n/**\n * The optional name a connect dialog collects for the connection it creates,\n * so it can be told apart from the owner's other connections to the provider.\n * Only the sectioned experiences ask for it, and only when creating.\n */\nconst ConnectionNameField: React.FC<ConnectionNameFieldProps> = ({\n id,\n value,\n onChange,\n}) => {\n const translate = useTranslation();\n const placeholder = translate({\n defaultMessage: \"e.g. Marketing\",\n id: \"Rn4cLp\",\n description: \"Placeholder for the connection name input\",\n });\n\n return (\n <Flex direction=\"column\" gap=\"1\">\n <Flex align=\"baseline\" gap=\"2\">\n <Label htmlFor={id}>\n <Translation\n defaultMessage=\"Name\"\n id=\"Rn1lNm\"\n description=\"Label for the connection name input\"\n />\n </Label>\n <Text size=\"1\" color=\"gray\">\n <Translation\n defaultMessage=\"Optional\"\n id=\"Rn0oPt\"\n description=\"Hint beside the connection name input saying it can be left empty\"\n />\n </Text>\n </Flex>\n <TextField.Root\n id={id}\n value={value}\n maxLength={MAX_LABEL_LENGTH}\n autoComplete=\"off\"\n placeholder={placeholder}\n onChange={(event) => onChange(event.target.value)}\n />\n </Flex>\n );\n};\n\ninterface RenameConnectionDialogProps {\n integrationName: string;\n installation: DataInstallation;\n owner?: ConnectionOwner;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * Renames one connection. A duplicate name never blocks the save; when the API\n * warns about one, the dialog stays open on the saved name to say so, and the\n * field stays editable so a distinct name can be saved right away.\n */\nconst RenameConnectionDialog: React.FC<RenameConnectionDialogProps> = ({\n integrationName,\n installation,\n owner = \"user\",\n open,\n onOpenChange,\n}) => {\n const [label, setLabel] = useState(installation.account_display_name ?? \"\");\n const translate = useTranslation();\n const { mutate, data, error, reset, isPending } = useRenameConnection(owner);\n\n // Mounted only while open, so the field starts from the saved label.\n useEffect(() => {\n reset();\n }, [reset]);\n\n const trimmed = label.trim();\n // After a save that warned, the name to compare against is the one just\n // saved, so the warning can be answered with another name straight away.\n const savedLabel = data\n ? (data.connection.accountDisplayName ?? \"\")\n : (installation.account_display_name ?? \"\");\n const unchanged = trimmed === savedLabel;\n // A save's warnings describe the name it saved, so they only stand while\n // the field still shows that name. Once it's edited they (and \"Done\") give\n // way to Cancel, so closing can't be mistaken for keeping the new text.\n const warnings = unchanged ? (data?.warnings ?? []) : [];\n const errorMessage =\n error instanceof ApiError && error.message ? error.message : null;\n\n const placeholder = translate({\n defaultMessage: \"e.g. Marketing\",\n id: \"Rn4cLp\",\n description: \"Placeholder for the connection name input\",\n });\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content maxWidth=\"430px\">\n <Dialog.Title size=\"3\" weight=\"bold\">\n <Translation\n defaultMessage=\"Rename {integrationName} connection\"\n id=\"Rn7tTl\"\n description=\"Dialog title for renaming one connection to a provider\"\n values={{ integrationName }}\n />\n </Dialog.Title>\n <Dialog.Description color=\"gray\" mt=\"1\">\n <Translation\n defaultMessage=\"Give this connection a name to tell it apart from your other {integrationName} connections.\"\n id=\"Rn2dSc\"\n description=\"Explains what a connection name is for\"\n values={{ integrationName }}\n />\n </Dialog.Description>\n\n {error && (\n <Callout.Root my=\"3\" color=\"red\" role=\"alert\">\n <Callout.Text>\n {errorMessage ?? (\n <Translation\n defaultMessage=\"An error occurred while renaming this connection. Please try again.\"\n id=\"Rn9eRr\"\n description=\"Error message when renaming a connection fails\"\n />\n )}\n </Callout.Text>\n </Callout.Root>\n )}\n\n {warnings.map((warning) => (\n <Callout.Root key={warning.code} my=\"3\" color=\"amber\" role=\"status\">\n <Callout.Text>\n {warning.code === DUPLICATE_LABEL_WARNING ? (\n <Translation\n defaultMessage=\"Saved. Another {integrationName} connection already uses this name, so you may want to pick one that tells them apart.\"\n id=\"Rn2wDp\"\n description=\"Notice after renaming a connection to a name another connection to the same provider already has\"\n values={{ integrationName }}\n />\n ) : (\n warning.message\n )}\n </Callout.Text>\n </Callout.Root>\n ))}\n\n <form\n onSubmit={(event) => {\n event.preventDefault();\n if (unchanged) {\n return;\n }\n mutate(\n { id: installation.id, accountDisplayName: trimmed || null },\n {\n onSuccess: (result) => {\n if (result.warnings.length === 0) {\n onOpenChange(false);\n }\n },\n },\n );\n }}\n >\n <Flex direction=\"column\" gap=\"1\" mt=\"4\">\n <Label htmlFor={`rename-connection-${installation.id}`}>\n <Translation\n defaultMessage=\"Name\"\n id=\"Rn1lNm\"\n description=\"Label for the connection name input\"\n />\n </Label>\n <TextField.Root\n id={`rename-connection-${installation.id}`}\n value={label}\n maxLength={MAX_LABEL_LENGTH}\n autoComplete=\"off\"\n placeholder={placeholder}\n onChange={(event) => setLabel(event.target.value)}\n />\n </Flex>\n\n <Flex justify=\"end\" gap=\"3\" mt=\"5\">\n <Dialog.Close>\n <Button type=\"button\" variant=\"secondary\">\n {/* With a saved name shown (and warned about), closing keeps it. */}\n {warnings.length > 0 ? (\n <Translation\n defaultMessage=\"Done\"\n id=\"Rn6dDn\"\n description=\"Button to close the rename dialog after a name was saved with a warning\"\n />\n ) : (\n <Translation\n defaultMessage=\"Cancel\"\n id=\"hHNj31\"\n description=\"Cancel button text\"\n />\n )}\n </Button>\n </Dialog.Close>\n <Button\n type=\"submit\"\n disabled={isPending || unchanged}\n loading={isPending}\n >\n <Translation\n defaultMessage=\"Save\"\n id=\"Rn5sVe\"\n description=\"Button to save a connection's new name\"\n />\n </Button>\n </Flex>\n </form>\n </Dialog.Content>\n </Dialog.Root>\n );\n};\n\ninterface ConnectionCountStatusProps {\n count: number;\n}\n\n/**\n * A provider row's status once it groups its connections, which it only does\n * for two or more — hence no singular form (plurals also need the optional\n * I18nProvider to format).\n */\nconst ConnectionCountStatus: React.FC<ConnectionCountStatusProps> = ({\n count,\n}) => {\n return (\n <Status state=\"neutral\">\n <Translation\n defaultMessage=\"{count} connections\"\n id=\"Rn3cCt\"\n description=\"Status for a provider grouping two or more connections, giving how many\"\n values={{ count }}\n />\n </Status>\n );\n};\n\nconst RenameMenuLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Rename\"\n id=\"Rn6mRn\"\n description=\"Menu option to rename one connection\"\n />\n );\n};\n\nconst ConnectAnotherAccountMenuLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Connect another account\"\n id=\"Rn8mCa\"\n description=\"Menu option to add another connection to the same provider\"\n />\n );\n};\n\nconst AddConnectionButtonLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Add connection\"\n id=\"Rn0bAc\"\n description=\"Button to add another connection to a provider that already has several\"\n />\n );\n};\nexport type { ConnectionTarget };\nexport {\n getConnectionLabel,\n ADD_CONNECTION,\n createAddConnectionTarget,\n getConnectionTarget,\n canRenameConnections,\n toConnectionTargetInput,\n toCreationTargetInput,\n toAccountDisplayNameInput,\n ConnectionNameField,\n RenameConnectionDialog,\n ConnectionCountStatus,\n RenameMenuLabel,\n ConnectAnotherAccountMenuLabel,\n AddConnectionButtonLabel,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipes-connections.js","names":["TextField.Root","Dialog.Root","Dialog.Content","Dialog.Title","Dialog.Description","Dialog.Close","Button"],"sources":["../../../../src/widgets/pipes/pipes-connections.tsx"],"sourcesContent":["\"use client\";\nimport { Callout, Flex, Text } from \"@radix-ui/themes\";\nimport { useEffect, useState } from \"react\";\nimport type {\n ConnectionIntent,\n DataProviderConnectionLabelResultFieldsFragment,\n UpdateDataProviderConnectionLabelInput,\n} from \"../../api/client-graphql/generated.js\";\nimport { useClientGraphqlMutation } from \"../../api/client-graphql/hooks.js\";\nimport { ApiError } from \"../../api/errors.js\";\nimport { Button, Dialog, Label, TextField } from \"../../components/elements.js\";\nimport { Status } from \"../../components/status.js\";\nimport { useInvalidateConnections } from \"../../lib/api/pipes.js\";\nimport type { DataInstallation } from \"../../lib/api/types.js\";\nimport { Translation } from \"../../lib/i18n/translation.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\nimport type { ConnectionOwner } from \"./pipes.js\";\n\n/**\n * Multiple connections per provider, shared by the member and administrator\n * widgets: naming a connection, targeting the connect flows at one exact\n * connection (or at a new one), and renaming.\n */\n\n/**\n * How one connection is named within its provider's group: the label someone\n * gave it, then the provider account it disclosed, then the provider itself.\n */\nfunction getConnectionLabel(\n integrationName: string,\n installation: DataInstallation,\n): string {\n return (\n installation.account_display_name?.trim() ||\n installation.account_identifier ||\n integrationName\n );\n}\n\n/**\n * Which connection a connect flow writes. Left unset, the flows keep the API's\n * compatibility behavior: they write the owner's single compatibility\n * connection, creating it if there is none.\n */\ntype ConnectionTarget =\n /**\n * Create another connection alongside the owner's existing ones.\n * `idempotencyKey` identifies one logical Add, so a retry after an\n * ambiguous failure returns the connection the first attempt created\n * instead of creating a second (see {@link createAddConnectionTarget}).\n */\n | { intent: \"Add\"; idempotencyKey?: string }\n /** Re-authorize, or replace the credentials of, one exact connection. */\n | { intent: \"Reauthorize\"; connectedAccountId: string };\n\n/** Requests an Add; the flow that runs it mints the idempotency key. */\nconst ADD_CONNECTION: ConnectionTarget = { intent: \"Add\" };\n\n/**\n * A random key for one logical Add. `crypto.randomUUID` needs a secure\n * context, so plain-HTTP hosts fall back to `getRandomValues`, which doesn't.\n */\nfunction createIdempotencyKey(): string {\n if (typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n}\n\n/**\n * The target for one logical Add, carrying a fresh idempotency key. Mint it\n * once when the Add starts and reuse it for every retry of that Add; a\n * deliberate second Add gets its own. The key is only honored with the Add\n * intent, and within 24 hours of the first attempt.\n */\nfunction createAddConnectionTarget(): ConnectionTarget {\n return { intent: \"Add\", idempotencyKey: createIdempotencyKey() };\n}\n\n/**\n * The target for writing an existing connection's credentials. Standard\n * connections are only reachable by naming them; the compatibility connection\n * is what an untargeted write already means, and leaving the intent off keeps\n * working where the environment has multiple connections turned off (the API\n * refuses explicit intents there).\n */\nfunction getConnectionTarget(\n installation: DataInstallation | null | undefined,\n): ConnectionTarget | undefined {\n return installation?.connection_role === \"standard\"\n ? { intent: \"Reauthorize\", connectedAccountId: installation.id }\n : undefined;\n}\n\n/**\n * Whether a provider's connections can be renamed: only once it can hold more\n * than one, since a lone connection's name is never shown beside another's.\n * The API reports no setting for this, so it's inferred: either an add is\n * available, or a standard connection exists, which only multiple connections\n * create (and which any provider with several connections has, at the cap\n * too).\n */\nfunction canRenameConnections(integration: {\n capabilities: { canAddConnection: boolean };\n connected_accounts: DataInstallation[];\n}): boolean {\n return (\n integration.capabilities.canAddConnection ||\n integration.connected_accounts.some(\n (connection) => connection.connection_role === \"standard\",\n )\n );\n}\n\n/**\n * The `connectionIntent` / `connectedAccountId` fields of a connect input.\n * Every connect input takes these, including OAuth authorization, which has\n * no idempotency key — see {@link toCreationTargetInput} for the inputs that do.\n */\nfunction toConnectionTargetInput(target: ConnectionTarget | undefined): {\n connectionIntent?: ConnectionIntent;\n connectedAccountId?: string;\n} {\n if (!target) {\n return {};\n }\n return target.intent === \"Add\"\n ? { connectionIntent: \"Add\" }\n : {\n connectionIntent: \"Reauthorize\",\n connectedAccountId: target.connectedAccountId,\n };\n}\n\n/**\n * {@link toConnectionTargetInput} plus the Add's `idempotencyKey`, for the\n * synchronous creation inputs (API key and client credentials) that accept it.\n * Never spread this into an authorization input: the API rejects the field.\n */\nfunction toCreationTargetInput(target: ConnectionTarget | undefined): {\n connectionIntent?: ConnectionIntent;\n connectedAccountId?: string;\n idempotencyKey?: string;\n} {\n const input = toConnectionTargetInput(target);\n return target?.intent === \"Add\" && target.idempotencyKey\n ? { ...input, idempotencyKey: target.idempotencyKey }\n : input;\n}\n\n/**\n * Saves a connection's label through the owner's mutation (`null` clears it\n * back to the computed fallback) and refreshes the owner's lists.\n */\nfunction useRenameConnection(owner: ConnectionOwner) {\n const invalidateConnections = useInvalidateConnections(owner);\n return useClientGraphqlMutation({\n mutationFn: async (\n sdk,\n input: UpdateDataProviderConnectionLabelInput,\n ): Promise<DataProviderConnectionLabelResultFieldsFragment> => {\n if (owner === \"organization\") {\n const { updateOrganizationDataProviderConnectionLabel } =\n await sdk.UpdateOrganizationDataProviderConnectionLabel({ input });\n return updateOrganizationDataProviderConnectionLabel;\n }\n const { updateDataProviderConnectionLabel } =\n await sdk.UpdateDataProviderConnectionLabel({ input });\n return updateDataProviderConnectionLabel;\n },\n onSuccess: invalidateConnections,\n });\n}\n\n/** The label code the API warns with when a sibling already uses the name. */\nconst DUPLICATE_LABEL_WARNING = \"duplicate_account_display_name\";\n\n/** Maximum label length the API accepts (PVIT-904). */\nconst MAX_LABEL_LENGTH = 255;\n\n/**\n * The `accountDisplayName` field of a creation input: the trimmed name when\n * one was entered, otherwise nothing, so an untouched field never sends an\n * empty label.\n */\nfunction toAccountDisplayNameInput(name: string | undefined): {\n accountDisplayName?: string;\n} {\n const trimmed = name?.trim();\n return trimmed ? { accountDisplayName: trimmed } : {};\n}\n\ninterface ConnectionNameFieldProps {\n id: string;\n value: string;\n onChange: (value: string) => void;\n}\n\n/**\n * The optional name a connect dialog collects for the connection it creates,\n * so it can be told apart from the owner's other connections to the provider.\n * Only the sectioned experiences ask for it, and only when creating.\n */\nconst ConnectionNameField: React.FC<ConnectionNameFieldProps> = ({\n id,\n value,\n onChange,\n}) => {\n const translate = useTranslation();\n const placeholder = translate({\n defaultMessage: \"e.g. Marketing\",\n id: \"pWC98E\",\n description: \"Placeholder for the connection name input\",\n });\n\n return (\n <Flex direction=\"column\" gap=\"1\">\n <Flex align=\"baseline\" gap=\"2\">\n <Label htmlFor={id}>\n <Translation\n defaultMessage=\"Name\"\n id=\"KdgU9T\"\n description=\"Label for the connection name input\"\n />\n </Label>\n <Text size=\"1\" color=\"gray\">\n <Translation\n defaultMessage=\"Optional\"\n id=\"2Yy441\"\n description=\"Hint beside the connection name input saying it can be left empty\"\n />\n </Text>\n </Flex>\n <TextField.Root\n id={id}\n value={value}\n maxLength={MAX_LABEL_LENGTH}\n autoComplete=\"off\"\n placeholder={placeholder}\n onChange={(event) => onChange(event.target.value)}\n />\n </Flex>\n );\n};\n\ninterface RenameConnectionDialogProps {\n integrationName: string;\n installation: DataInstallation;\n owner?: ConnectionOwner;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * Renames one connection. A duplicate name never blocks the save; when the API\n * warns about one, the dialog stays open on the saved name to say so, and the\n * field stays editable so a distinct name can be saved right away.\n */\nconst RenameConnectionDialog: React.FC<RenameConnectionDialogProps> = ({\n integrationName,\n installation,\n owner = \"user\",\n open,\n onOpenChange,\n}) => {\n const [label, setLabel] = useState(installation.account_display_name ?? \"\");\n const translate = useTranslation();\n const { mutate, data, error, reset, isPending } = useRenameConnection(owner);\n\n // Mounted only while open, so the field starts from the saved label.\n useEffect(() => {\n reset();\n }, [reset]);\n\n const trimmed = label.trim();\n // After a save that warned, the name to compare against is the one just\n // saved, so the warning can be answered with another name straight away.\n const savedLabel = data\n ? (data.connection.accountDisplayName ?? \"\")\n : (installation.account_display_name ?? \"\");\n const unchanged = trimmed === savedLabel;\n // A save's warnings describe the name it saved, so they only stand while\n // the field still shows that name. Once it's edited they (and \"Done\") give\n // way to Cancel, so closing can't be mistaken for keeping the new text.\n const warnings = unchanged ? (data?.warnings ?? []) : [];\n const errorMessage =\n error instanceof ApiError && error.message ? error.message : null;\n\n const placeholder = translate({\n defaultMessage: \"e.g. Marketing\",\n id: \"pWC98E\",\n description: \"Placeholder for the connection name input\",\n });\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content maxWidth=\"430px\">\n <Dialog.Title size=\"3\" weight=\"bold\">\n <Translation\n defaultMessage=\"Rename {integrationName} connection\"\n id=\"c1jT5Q\"\n description=\"Dialog title for renaming one connection to a provider\"\n values={{ integrationName }}\n />\n </Dialog.Title>\n <Dialog.Description color=\"gray\" mt=\"1\">\n <Translation\n defaultMessage=\"Give this connection a name to tell it apart from your other {integrationName} connections.\"\n id=\"Ptf2r1\"\n description=\"Explains what a connection name is for\"\n values={{ integrationName }}\n />\n </Dialog.Description>\n\n {error && (\n <Callout.Root my=\"3\" color=\"red\" role=\"alert\">\n <Callout.Text>\n {errorMessage ?? (\n <Translation\n defaultMessage=\"An error occurred while renaming this connection. Please try again.\"\n id=\"0grPwn\"\n description=\"Error message when renaming a connection fails\"\n />\n )}\n </Callout.Text>\n </Callout.Root>\n )}\n\n {warnings.map((warning) => (\n <Callout.Root key={warning.code} my=\"3\" color=\"amber\" role=\"status\">\n <Callout.Text>\n {warning.code === DUPLICATE_LABEL_WARNING ? (\n <Translation\n defaultMessage=\"Saved. Another {integrationName} connection already uses this name, so you may want to pick one that tells them apart.\"\n id=\"eB4eyQ\"\n description=\"Notice after renaming a connection to a name another connection to the same provider already has\"\n values={{ integrationName }}\n />\n ) : (\n warning.message\n )}\n </Callout.Text>\n </Callout.Root>\n ))}\n\n <form\n onSubmit={(event) => {\n event.preventDefault();\n if (unchanged) {\n return;\n }\n mutate(\n { id: installation.id, accountDisplayName: trimmed || null },\n {\n onSuccess: (result) => {\n if (result.warnings.length === 0) {\n onOpenChange(false);\n }\n },\n },\n );\n }}\n >\n <Flex direction=\"column\" gap=\"1\" mt=\"4\">\n <Label htmlFor={`rename-connection-${installation.id}`}>\n <Translation\n defaultMessage=\"Name\"\n id=\"KdgU9T\"\n description=\"Label for the connection name input\"\n />\n </Label>\n <TextField.Root\n id={`rename-connection-${installation.id}`}\n value={label}\n maxLength={MAX_LABEL_LENGTH}\n autoComplete=\"off\"\n placeholder={placeholder}\n onChange={(event) => setLabel(event.target.value)}\n />\n </Flex>\n\n <Flex justify=\"end\" gap=\"3\" mt=\"5\">\n <Dialog.Close>\n <Button type=\"button\" variant=\"secondary\">\n {/* With a saved name shown (and warned about), closing keeps it. */}\n {warnings.length > 0 ? (\n <Translation\n defaultMessage=\"Done\"\n id=\"YlARFG\"\n description=\"Button to close the rename dialog after a name was saved with a warning\"\n />\n ) : (\n <Translation\n defaultMessage=\"Cancel\"\n id=\"hHNj31\"\n description=\"Cancel button text\"\n />\n )}\n </Button>\n </Dialog.Close>\n <Button\n type=\"submit\"\n disabled={isPending || unchanged}\n loading={isPending}\n >\n <Translation\n defaultMessage=\"Save\"\n id=\"JmvID+\"\n description=\"Button to save a connection's new name\"\n />\n </Button>\n </Flex>\n </form>\n </Dialog.Content>\n </Dialog.Root>\n );\n};\n\ninterface ConnectionCountStatusProps {\n count: number;\n}\n\n/**\n * A provider row's status once it groups its connections, which it only does\n * for two or more — hence no singular form (plurals also need the optional\n * I18nProvider to format).\n */\nconst ConnectionCountStatus: React.FC<ConnectionCountStatusProps> = ({\n count,\n}) => {\n return (\n <Status state=\"neutral\">\n <Translation\n defaultMessage=\"{count} connections\"\n id=\"ZarniP\"\n description=\"Status for a provider grouping two or more connections, giving how many\"\n values={{ count }}\n />\n </Status>\n );\n};\n\nconst RenameMenuLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Rename\"\n id=\"iiQbUM\"\n description=\"Menu option to rename one connection\"\n />\n );\n};\n\nconst ConnectAnotherAccountMenuLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Connect another account\"\n id=\"SNK0to\"\n description=\"Menu option to add another connection to the same provider\"\n />\n );\n};\n\nconst AddConnectionButtonLabel: React.FC = () => {\n return (\n <Translation\n defaultMessage=\"Add connection\"\n id=\"DFzo7c\"\n description=\"Button to add another connection to a provider that already has several\"\n />\n );\n};\nexport type { ConnectionTarget };\nexport {\n getConnectionLabel,\n ADD_CONNECTION,\n createAddConnectionTarget,\n getConnectionTarget,\n canRenameConnections,\n toConnectionTargetInput,\n toCreationTargetInput,\n toAccountDisplayNameInput,\n ConnectionNameField,\n RenameConnectionDialog,\n ConnectionCountStatus,\n RenameMenuLabel,\n ConnectAnotherAccountMenuLabel,\n AddConnectionButtonLabel,\n};\n"],"mappings":"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"}
|
|
@@ -18,11 +18,11 @@ function usePipesCredentialAttribution() {
|
|
|
18
18
|
error: sessionUserId && hostUserId && hostUserId !== sessionUserId ? translate({
|
|
19
19
|
defaultMessage: "Shared credentials must be attributed to the signed-in user. The application must omit PipesAdmin's userId prop or pass the signed-in user's WorkOS user ID.",
|
|
20
20
|
description: "Configuration error when the host-supplied PipesAdmin user ID differs from the authenticated user",
|
|
21
|
-
id: "
|
|
21
|
+
id: "jhlRXY"
|
|
22
22
|
}) : !userId ? translate({
|
|
23
23
|
defaultMessage: "Shared credentials require an acting WorkOS user. The application must use a user-backed session or supply the acting person's WorkOS user ID through PipesAdmin's userId prop when using an application credential token.",
|
|
24
24
|
description: "Configuration guidance for shared credential writes without an acting user; no user ID entry is requested",
|
|
25
|
-
id: "
|
|
25
|
+
id: "LXD0s/"
|
|
26
26
|
}) : void 0
|
|
27
27
|
};
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipes-credential-attribution.js","names":[],"sources":["../../../../src/widgets/pipes/pipes-credential-attribution.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { useApi } from \"../../api/api-provider.js\";\nimport { getAuthenticatedUserId, getClaims } from \"../../api/claims.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\n\nconst PipesCredentialUserIdContext = createContext<string | undefined>(\n undefined,\n);\nPipesCredentialUserIdContext.displayName = \"PipesCredentialUserIdContext\";\n\nfunction usePipesCredentialAttribution() {\n const hostUserId = useContext(PipesCredentialUserIdContext)?.trim();\n const { authToken } = useApi();\n const translate = useTranslation();\n const claims = authToken ? getClaims(authToken) : undefined;\n const sessionUserId = claims && getAuthenticatedUserId(claims);\n const userId =\n sessionUserId ??\n (claims?.sub_type === \"client_api_credential\" &&\n hostUserId?.startsWith(\"user_\")\n ? hostUserId\n : undefined);\n\n const error =\n sessionUserId && hostUserId && hostUserId !== sessionUserId\n ? translate({\n defaultMessage:\n \"Shared credentials must be attributed to the signed-in user. The application must omit PipesAdmin's userId prop or pass the signed-in user's WorkOS user ID.\",\n description:\n \"Configuration error when the host-supplied PipesAdmin user ID differs from the authenticated user\",\n id: \"
|
|
1
|
+
{"version":3,"file":"pipes-credential-attribution.js","names":[],"sources":["../../../../src/widgets/pipes/pipes-credential-attribution.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { useApi } from \"../../api/api-provider.js\";\nimport { getAuthenticatedUserId, getClaims } from \"../../api/claims.js\";\nimport { useTranslation } from \"../../lib/i18n/use-translation.js\";\n\nconst PipesCredentialUserIdContext = createContext<string | undefined>(\n undefined,\n);\nPipesCredentialUserIdContext.displayName = \"PipesCredentialUserIdContext\";\n\nfunction usePipesCredentialAttribution() {\n const hostUserId = useContext(PipesCredentialUserIdContext)?.trim();\n const { authToken } = useApi();\n const translate = useTranslation();\n const claims = authToken ? getClaims(authToken) : undefined;\n const sessionUserId = claims && getAuthenticatedUserId(claims);\n const userId =\n sessionUserId ??\n (claims?.sub_type === \"client_api_credential\" &&\n hostUserId?.startsWith(\"user_\")\n ? hostUserId\n : undefined);\n\n const error =\n sessionUserId && hostUserId && hostUserId !== sessionUserId\n ? translate({\n defaultMessage:\n \"Shared credentials must be attributed to the signed-in user. The application must omit PipesAdmin's userId prop or pass the signed-in user's WorkOS user ID.\",\n description:\n \"Configuration error when the host-supplied PipesAdmin user ID differs from the authenticated user\",\n id: \"jhlRXY\",\n })\n : !userId\n ? translate({\n defaultMessage:\n \"Shared credentials require an acting WorkOS user. The application must use a user-backed session or supply the acting person's WorkOS user ID through PipesAdmin's userId prop when using an application credential token.\",\n description:\n \"Configuration guidance for shared credential writes without an acting user; no user ID entry is requested\",\n id: \"LXD0s/\",\n })\n : undefined;\n\n return { userId, error };\n}\nexport { PipesCredentialUserIdContext, usePipesCredentialAttribution };\n"],"mappings":";;;;;;AAOA,MAAM,+BAA+B,cACnC,MACF;AACA,6BAA6B,cAAc;AAE3C,SAAS,gCAAgC;CACvC,MAAM,aAAa,WAAW,4BAA4B,CAAC,EAAE,KAAK;CAClE,MAAM,EAAE,cAAc,OAAO;CAC7B,MAAM,YAAY,eAAe;CACjC,MAAM,SAAS,YAAY,UAAU,SAAS,IAAI;CAClD,MAAM,gBAAgB,UAAU,uBAAuB,MAAM;CAC7D,MAAM,SACJ,kBACC,QAAQ,aAAa,2BACtB,YAAY,WAAW,OAAO,IAC1B,aACA;CAqBN,OAAO;EAAE;EAAQ,OAlBf,iBAAiB,cAAc,eAAe,gBAC1C,UAAU;GACR,gBACE;GACF,aACE;GACF,IAAI;EACN,CAAC,IACD,CAAC,SACC,UAAU;GACR,gBACE;GACF,aACE;GACF,IAAI;EACN,CAAC,IACD;CAEe;AACzB"}
|
|
@@ -1718,7 +1718,7 @@ const SharedConnectionStatus = ({ connection }) => {
|
|
|
1718
1718
|
state: "success",
|
|
1719
1719
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
1720
1720
|
defaultMessage: "Available",
|
|
1721
|
-
id: "
|
|
1721
|
+
id: "/jQjSu",
|
|
1722
1722
|
description: "Status text for a shared connection the member can use"
|
|
1723
1723
|
})
|
|
1724
1724
|
}) : /* @__PURE__ */ jsx(Status, {
|
|
@@ -1812,7 +1812,7 @@ const MemberConnectionStatus = ({ installation, connectionError }) => {
|
|
|
1812
1812
|
description: "Status text for a connection established with client credentials"
|
|
1813
1813
|
}) : /* @__PURE__ */ jsx(Translation, {
|
|
1814
1814
|
defaultMessage: "Connected via OAuth",
|
|
1815
|
-
id: "
|
|
1815
|
+
id: "pJcK/M",
|
|
1816
1816
|
description: "Status text for a connection established with OAuth"
|
|
1817
1817
|
})
|
|
1818
1818
|
});
|
|
@@ -1820,7 +1820,7 @@ const MemberConnectionStatus = ({ installation, connectionError }) => {
|
|
|
1820
1820
|
state: "warning",
|
|
1821
1821
|
children: installation.auth_method === "api_key" ? /* @__PURE__ */ jsx(Translation, {
|
|
1822
1822
|
defaultMessage: "Update your API key",
|
|
1823
|
-
id: "
|
|
1823
|
+
id: "+YLPTk",
|
|
1824
1824
|
description: "Status text for a connection whose API key stopped working"
|
|
1825
1825
|
}) : /* @__PURE__ */ jsx(Translation, {
|
|
1826
1826
|
defaultMessage: "Requires reauthorization",
|
|
@@ -1919,7 +1919,7 @@ const MemberConnectionActions = ({ integration, installation, label, onConnect,
|
|
|
1919
1919
|
onClick: openApiKey,
|
|
1920
1920
|
children: /* @__PURE__ */ jsx(Translation, {
|
|
1921
1921
|
defaultMessage: "Update key",
|
|
1922
|
-
id: "MP
|
|
1922
|
+
id: "MP+aOE",
|
|
1923
1923
|
description: "Button to replace the API key of a connection that stopped working"
|
|
1924
1924
|
})
|
|
1925
1925
|
}) : isClientCredentialsInstallation ? /* @__PURE__ */ jsx(Button$1, {
|
|
@@ -2127,7 +2127,7 @@ const PipesSections = ({ integrations, hasUnfilteredIntegrations, sharedConnecti
|
|
|
2127
2127
|
children: [(isSharedLoading || sharedConnections.length > 0 || hasUnfilteredSharedConnections) && /* @__PURE__ */ jsx(PipesSection, {
|
|
2128
2128
|
title: /* @__PURE__ */ jsx(Translation, {
|
|
2129
2129
|
defaultMessage: "Shared connections",
|
|
2130
|
-
id: "Fcd
|
|
2130
|
+
id: "Fcd+bK",
|
|
2131
2131
|
description: "Heading for the section listing connections the organization owns"
|
|
2132
2132
|
}),
|
|
2133
2133
|
description: organizationName ? /* @__PURE__ */ jsx(Translation, {
|
|
@@ -2144,7 +2144,7 @@ const PipesSections = ({ integrations, hasUnfilteredIntegrations, sharedConnecti
|
|
|
2144
2144
|
}), /* @__PURE__ */ jsx(PipesSection, {
|
|
2145
2145
|
title: /* @__PURE__ */ jsx(Translation, {
|
|
2146
2146
|
defaultMessage: "Your connections",
|
|
2147
|
-
id: "
|
|
2147
|
+
id: "+VDI3G",
|
|
2148
2148
|
description: "Heading for the section listing the member's own connections"
|
|
2149
2149
|
}),
|
|
2150
2150
|
description: /* @__PURE__ */ jsx(Translation, {
|