@shipfox/client-integrations 14.0.0 → 14.0.1
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/components/callback-status-shell.js +3 -3
- package/dist/components/callback-status-shell.js.map +1 -1
- package/dist/components/connection-picker.js +3 -3
- package/dist/components/connection-picker.js.map +1 -1
- package/dist/components/installed-integrations-section.js +9 -9
- package/dist/components/installed-integrations-section.js.map +1 -1
- package/dist/components/integration-delete-confirm-modal.js +1 -1
- package/dist/components/integration-delete-confirm-modal.js.map +1 -1
- package/dist/components/integration-gallery-for-workspace.js +3 -3
- package/dist/components/integration-gallery-for-workspace.js.map +1 -1
- package/dist/components/integration-usage-modal.js +6 -6
- package/dist/components/integration-usage-modal.js.map +1 -1
- package/dist/components/provider-grid.d.ts +1 -1
- package/dist/components/provider-grid.d.ts.map +1 -1
- package/dist/components/provider-grid.js +10 -10
- package/dist/components/provider-grid.js.map +1 -1
- package/dist/components/redirect-install-page.js +1 -1
- package/dist/components/redirect-install-page.js.map +1 -1
- package/dist/components/repository-picker.js +4 -4
- package/dist/components/repository-picker.js.map +1 -1
- package/dist/components/webhook/copyable-value.js +3 -3
- package/dist/components/webhook/copyable-value.js.map +1 -1
- package/dist/components/webhook/webhook-create-modal.js +1 -1
- package/dist/components/webhook/webhook-create-modal.js.map +1 -1
- package/dist/components/webhook/webhook-usage-details.js +2 -2
- package/dist/components/webhook/webhook-usage-details.js.map +1 -1
- package/dist/pages/gitea-install-page.js +4 -4
- package/dist/pages/gitea-install-page.js.map +1 -1
- package/dist/pages/jira-callback-page.js +7 -7
- package/dist/pages/jira-callback-page.js.map +1 -1
- package/dist/pages/sentry-callback-page.js +7 -7
- package/dist/pages/sentry-callback-page.js.map +1 -1
- package/dist/pages/source-control-onboarding-page.js +2 -2
- package/dist/pages/source-control-onboarding-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/components/callback-status-shell.tsx +3 -3
- package/src/components/connection-picker.tsx +3 -3
- package/src/components/installed-integrations-section.tsx +9 -9
- package/src/components/integration-delete-confirm-modal.tsx +1 -1
- package/src/components/integration-gallery-for-workspace.tsx +3 -3
- package/src/components/integration-usage-modal.tsx +6 -6
- package/src/components/provider-grid.tsx +10 -10
- package/src/components/redirect-install-page.tsx +1 -1
- package/src/components/repository-picker.test.tsx +2 -2
- package/src/components/repository-picker.tsx +6 -4
- package/src/components/webhook/copyable-value.tsx +3 -3
- package/src/components/webhook/webhook-create-modal.tsx +1 -1
- package/src/components/webhook/webhook-usage-details.tsx +2 -2
- package/src/pages/gitea-install-page.tsx +4 -4
- package/src/pages/jira-callback-page.tsx +7 -7
- package/src/pages/sentry-callback-page.tsx +7 -7
- package/src/pages/source-control-onboarding-page.tsx +2 -2
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/provider-grid.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {IntegrationProvider} from '#core/models.js';\nimport {PROVIDER_CATALOG} from '#provider-catalog.js';\n\nexport interface ProviderGridProps {\n workspaceSlug: string;\n providers: IntegrationProvider[];\n isPending: boolean;\n isFetching?: boolean;\n error?: Error | null | undefined;\n onRetry?: () => void;\n emptyMessage: string;\n loadingLabel?: string;\n errorSubject?: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}\n\nexport const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-
|
|
1
|
+
{"version":3,"sources":["../../src/components/provider-grid.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {IntegrationProvider} from '#core/models.js';\nimport {PROVIDER_CATALOG} from '#provider-catalog.js';\n\nexport interface ProviderGridProps {\n workspaceSlug: string;\n providers: IntegrationProvider[];\n isPending: boolean;\n isFetching?: boolean;\n error?: Error | null | undefined;\n onRetry?: () => void;\n emptyMessage: string;\n loadingLabel?: string;\n errorSubject?: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}\n\nexport const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1';\n\nexport const PROVIDER_SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function ProviderGrid({\n workspaceSlug,\n providers,\n isPending,\n isFetching = false,\n error,\n onRetry,\n emptyMessage,\n loadingLabel = 'Loading providers',\n errorSubject = 'available integrations',\n onOpenProvider,\n}: ProviderGridProps) {\n const installableProviders = providers.filter((provider) => PROVIDER_CATALOG[provider.provider]);\n\n if (isPending) return <ProviderGridSkeleton label={loadingLabel} />;\n\n if (error) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-row')}>\n <QueryLoadError\n query={{\n isError: true,\n isFetching,\n data: undefined,\n error,\n refetch: onRetry ?? (() => undefined),\n }}\n subject={errorSubject}\n />\n </div>\n );\n }\n\n if (installableProviders.length === 0) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-row')}>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations available\"\n description={emptyMessage}\n />\n </div>\n );\n }\n\n return (\n <ul className={PROVIDER_GRID_CLASS}>\n {installableProviders.map((provider) => (\n <li key={provider.provider}>\n <ProviderCard\n provider={provider}\n workspaceSlug={workspaceSlug}\n onOpenProvider={onOpenProvider}\n />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction ProviderCard({\n provider,\n workspaceSlug,\n onOpenProvider,\n}: {\n provider: IntegrationProvider;\n workspaceSlug: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}) {\n const catalog = PROVIDER_CATALOG[provider.provider];\n if (!catalog) return null;\n\n if (catalog.kind === 'modal-connect') {\n return (\n <button\n type=\"button\"\n aria-label={`Add ${provider.displayName}`}\n onClick={() => onOpenProvider?.(provider.provider)}\n className=\"group flex h-full w-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Add\" />\n </button>\n );\n }\n\n return (\n <Link\n to={catalog.setupPath}\n params={{workspaceSlug}}\n aria-label={`Install ${provider.displayName}`}\n className=\"group flex h-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Install\" />\n </Link>\n );\n}\n\nfunction ProviderCardContent({\n provider,\n action,\n}: {\n provider: IntegrationProvider;\n action: 'Add' | 'Install';\n}) {\n return (\n <>\n <span className=\"flex min-w-0 items-center gap-cluster\">\n <IntegrationIcon\n source={provider.provider}\n aria-hidden\n className=\"size-24 shrink-0 text-foreground-neutral-base\"\n />\n <Text as=\"span\" size=\"md\" bold className=\"truncate\">\n {provider.displayName}\n </Text>\n </span>\n <span className=\"flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive\">\n <Text as=\"span\" size=\"sm\">\n {action}\n </Text>\n <Icon name=\"chevronRight\" className=\"size-16\" />\n </span>\n </>\n );\n}\n\nfunction ProviderGridSkeleton({label}: {label: string}) {\n return (\n <ul role=\"status\" aria-label={label} className={PROVIDER_GRID_CLASS}>\n {[0, 1, 2, 3].map((tile) => (\n <li key={tile}>\n <Card className=\"h-full p-panel-compact\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-cluster\">\n <Skeleton className=\"size-24 shrink-0\" />\n <Skeleton className=\"h-16 w-100\" />\n </div>\n <Skeleton className=\"h-16 w-64 shrink-0\" />\n </div>\n </Card>\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IntegrationIcon","Card","EmptyState","Icon","Skeleton","Text","cn","Link","PROVIDER_CATALOG","PROVIDER_GRID_CLASS","PROVIDER_SURFACE_CLASS","ProviderGrid","workspaceSlug","providers","isPending","isFetching","error","onRetry","emptyMessage","loadingLabel","errorSubject","onOpenProvider","installableProviders","filter","provider","ProviderGridSkeleton","label","div","className","query","isError","data","undefined","refetch","subject","length","icon","title","description","ul","map","li","ProviderCard","catalog","kind","button","type","aria-label","displayName","onClick","ProviderCardContent","action","to","setupPath","params","span","source","aria-hidden","as","size","bold","name","role","tile"],"mappings":"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"}
|
|
@@ -37,7 +37,7 @@ export function RedirectInstallPage({ installRequest, errorFallbackMessage, load
|
|
|
37
37
|
]);
|
|
38
38
|
if (errorMessage) {
|
|
39
39
|
return /*#__PURE__*/ _jsxs("div", {
|
|
40
|
-
className: "mx-auto flex w-full max-w-[480px] flex-col gap-
|
|
40
|
+
className: "mx-auto flex w-full max-w-[480px] flex-col gap-group",
|
|
41
41
|
children: [
|
|
42
42
|
/*#__PURE__*/ _jsx(Callout, {
|
|
43
43
|
role: "alert",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/redirect-install-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef, useState} from 'react';\nimport type {InstallRedirect} from '#core/models.js';\n\ninterface RedirectInstallPageProps {\n installRequest: (body: {workspace_id: string}) => Promise<InstallRedirect>;\n errorFallbackMessage: string;\n loadingLabel?: string;\n /**\n * Runs before leaving the app (e.g. to persist the workspace id for a\n * state-less provider callback. It must not throw. A failed side effect\n * should never block the redirect.\n */\n beforeRedirect?: (workspaceId: string) => void;\n /** Injectable for tests: jsdom's window.location cannot be stubbed. */\n assignLocation?: (url: string) => void;\n}\n\nexport function RedirectInstallPage({\n installRequest,\n errorFallbackMessage,\n loadingLabel = 'Starting installation',\n beforeRedirect,\n assignLocation = (url) => window.location.assign(url),\n}: RedirectInstallPageProps) {\n const workspace = useActiveWorkspace();\n const startedRef = useRef(false);\n const [errorMessage, setErrorMessage] = useState<string | undefined>();\n\n useEffect(() => {\n if (startedRef.current) return;\n startedRef.current = true;\n // The side effect is best-effort persistence; a throw here must never\n // block the install redirect, so swallow it and continue.\n try {\n beforeRedirect?.(workspace.id);\n } catch {\n // ignore\n }\n installRequest({workspace_id: workspace.id})\n .then((response) => {\n assignLocation(response.installUrl);\n })\n .catch((error: unknown) => {\n setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);\n });\n }, [workspace, installRequest, beforeRedirect, errorFallbackMessage, assignLocation]);\n\n if (errorMessage) {\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-
|
|
1
|
+
{"version":3,"sources":["../../src/components/redirect-install-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef, useState} from 'react';\nimport type {InstallRedirect} from '#core/models.js';\n\ninterface RedirectInstallPageProps {\n installRequest: (body: {workspace_id: string}) => Promise<InstallRedirect>;\n errorFallbackMessage: string;\n loadingLabel?: string;\n /**\n * Runs before leaving the app (e.g. to persist the workspace id for a\n * state-less provider callback. It must not throw. A failed side effect\n * should never block the redirect.\n */\n beforeRedirect?: (workspaceId: string) => void;\n /** Injectable for tests: jsdom's window.location cannot be stubbed. */\n assignLocation?: (url: string) => void;\n}\n\nexport function RedirectInstallPage({\n installRequest,\n errorFallbackMessage,\n loadingLabel = 'Starting installation',\n beforeRedirect,\n assignLocation = (url) => window.location.assign(url),\n}: RedirectInstallPageProps) {\n const workspace = useActiveWorkspace();\n const startedRef = useRef(false);\n const [errorMessage, setErrorMessage] = useState<string | undefined>();\n\n useEffect(() => {\n if (startedRef.current) return;\n startedRef.current = true;\n // The side effect is best-effort persistence; a throw here must never\n // block the install redirect, so swallow it and continue.\n try {\n beforeRedirect?.(workspace.id);\n } catch {\n // ignore\n }\n installRequest({workspace_id: workspace.id})\n .then((response) => {\n assignLocation(response.installUrl);\n })\n .catch((error: unknown) => {\n setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);\n });\n }, [workspace, installRequest, beforeRedirect, errorFallbackMessage, assignLocation]);\n\n if (errorMessage) {\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-group\">\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{errorMessage}</Text>\n </Callout>\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/w/$workspaceSlug/integrations\" params={{workspaceSlug: workspace.slug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n </div>\n );\n }\n\n return <FullPageLoader aria-label={loadingLabel} />;\n}\n"],"names":["ApiError","useActiveWorkspace","ButtonLink","Callout","FullPageLoader","Text","Link","useEffect","useRef","useState","RedirectInstallPage","installRequest","errorFallbackMessage","loadingLabel","beforeRedirect","assignLocation","url","window","location","assign","workspace","startedRef","errorMessage","setErrorMessage","current","id","workspace_id","then","response","installUrl","catch","error","message","div","className","role","type","size","asChild","variant","to","params","workspaceSlug","slug","aria-label"],"mappings":";AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAiBlD,OAAO,SAASC,oBAAoB,EAClCC,cAAc,EACdC,oBAAoB,EACpBC,eAAe,uBAAuB,EACtCC,cAAc,EACdC,iBAAiB,CAACC,MAAQC,OAAOC,QAAQ,CAACC,MAAM,CAACH,IAAI,EAC5B;IACzB,MAAMI,YAAYnB;IAClB,MAAMoB,aAAab,OAAO;IAC1B,MAAM,CAACc,cAAcC,gBAAgB,GAAGd;IAExCF,UAAU;QACR,IAAIc,WAAWG,OAAO,EAAE;QACxBH,WAAWG,OAAO,GAAG;QACrB,sEAAsE;QACtE,0DAA0D;QAC1D,IAAI;YACFV,iBAAiBM,UAAUK,EAAE;QAC/B,EAAE,OAAM;QACN,SAAS;QACX;QACAd,eAAe;YAACe,cAAcN,UAAUK,EAAE;QAAA,GACvCE,IAAI,CAAC,CAACC;YACLb,eAAea,SAASC,UAAU;QACpC,GACCC,KAAK,CAAC,CAACC;YACNR,gBAAgBQ,iBAAiB/B,WAAW+B,MAAMC,OAAO,GAAGpB;QAC9D;IACJ,GAAG;QAACQ;QAAWT;QAAgBG;QAAgBF;QAAsBG;KAAe;IAEpF,IAAIO,cAAc;QAChB,qBACE,MAACW;YAAIC,WAAU;;8BACb,KAAC/B;oBAAQgC,MAAK;oBAAQC,MAAK;8BACzB,cAAA,KAAC/B;wBAAKgC,MAAK;kCAAMf;;;8BAEnB,KAACpB;oBAAWoC,OAAO;oBAACC,SAAQ;oBAAQL,WAAU;8BAC5C,cAAA,KAAC5B;wBAAKkC,IAAG;wBAAiCC,QAAQ;4BAACC,eAAetB,UAAUuB,IAAI;wBAAA;kCAAG;;;;;IAM3F;IAEA,qBAAO,KAACvC;QAAewC,cAAY/B;;AACrC"}
|
|
@@ -8,7 +8,7 @@ import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
|
|
9
9
|
import { Text } from '@shipfox/react-ui/typography';
|
|
10
10
|
import { useId } from 'react';
|
|
11
|
-
const REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-
|
|
11
|
+
const REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-inline max-[760px]:grid-cols-1';
|
|
12
12
|
const REPOSITORY_SKELETON_WIDTHS = [
|
|
13
13
|
'w-64',
|
|
14
14
|
'w-96',
|
|
@@ -19,7 +19,7 @@ export function RepositoryPicker({ repositories, selectedRepositoryId, onSelect,
|
|
|
19
19
|
const labelId = useId();
|
|
20
20
|
const showSearch = onSearchChange !== undefined;
|
|
21
21
|
return /*#__PURE__*/ _jsxs("div", {
|
|
22
|
-
className: "flex flex-col gap-
|
|
22
|
+
className: "flex flex-col gap-inline",
|
|
23
23
|
children: [
|
|
24
24
|
/*#__PURE__*/ _jsx(Label, {
|
|
25
25
|
id: labelId,
|
|
@@ -36,7 +36,7 @@ export function RepositoryPicker({ repositories, selectedRepositoryId, onSelect,
|
|
|
36
36
|
}) : null,
|
|
37
37
|
isLoading ? /*#__PURE__*/ _jsx(RepositoryLoadingState, {}) : null,
|
|
38
38
|
!isLoading && repositories.length === 0 ? /*#__PURE__*/ _jsx("div", {
|
|
39
|
-
className: "rounded-8 border border-border-neutral-base bg-background-subtle-base p-
|
|
39
|
+
className: "rounded-8 border border-border-neutral-base bg-background-subtle-base p-panel-compact",
|
|
40
40
|
children: /*#__PURE__*/ _jsx(Text, {
|
|
41
41
|
size: "sm",
|
|
42
42
|
children: emptyMessage
|
|
@@ -101,7 +101,7 @@ function RepositoryLoadingState() {
|
|
|
101
101
|
"aria-hidden": "true",
|
|
102
102
|
className: REPOSITORY_GRID_CLASS_NAME,
|
|
103
103
|
children: REPOSITORY_SKELETON_WIDTHS.map((width)=>/*#__PURE__*/ _jsx("div", {
|
|
104
|
-
className: "h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-
|
|
104
|
+
className: "h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-[14px]",
|
|
105
105
|
children: /*#__PURE__*/ _jsx(Skeleton, {
|
|
106
106
|
className: `h-20 ${width}`
|
|
107
107
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/repository-picker.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {Repository} from '#core/models.js';\n\nconst REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-
|
|
1
|
+
{"version":3,"sources":["../../src/components/repository-picker.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {Repository} from '#core/models.js';\n\nconst REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-inline max-[760px]:grid-cols-1';\nconst REPOSITORY_SKELETON_WIDTHS = ['w-64', 'w-96', 'w-80', 'w-112'] as const;\n\nexport function RepositoryPicker({\n repositories,\n selectedRepositoryId,\n onSelect,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n onLoadMore,\n emptyMessage = 'No repositories found.',\n searchValue,\n onSearchChange,\n searchDisabled,\n}: {\n repositories: Repository[];\n selectedRepositoryId: string | undefined;\n onSelect: (repositoryId: string) => void;\n isLoading: boolean;\n isFetchingNextPage?: boolean;\n hasNextPage?: boolean;\n onLoadMore?: () => void;\n emptyMessage?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n searchDisabled?: boolean;\n}) {\n const labelId = useId();\n const showSearch = onSearchChange !== undefined;\n\n return (\n <div className=\"flex flex-col gap-inline\">\n <Label id={labelId} className=\"sr-only\">\n Repository\n </Label>\n\n {showSearch ? (\n <Input\n type=\"search\"\n placeholder=\"Search repositories…\"\n aria-label=\"Search repositories\"\n value={searchValue ?? ''}\n onChange={(event) => onSearchChange?.(event.target.value)}\n disabled={searchDisabled}\n />\n ) : null}\n\n {isLoading ? <RepositoryLoadingState /> : null}\n\n {!isLoading && repositories.length === 0 ? (\n <div className=\"rounded-8 border border-border-neutral-base bg-background-subtle-base p-panel-compact\">\n <Text size=\"sm\">{emptyMessage}</Text>\n </div>\n ) : null}\n\n {repositories.length > 0 ? (\n <RadioGroup\n aria-labelledby={labelId}\n value={selectedRepositoryId ?? ''}\n onValueChange={onSelect}\n className={REPOSITORY_GRID_CLASS_NAME}\n >\n {repositories.map((repository) => (\n <RepositoryCard key={repository.externalRepositoryId} repository={repository} />\n ))}\n </RadioGroup>\n ) : null}\n\n {hasNextPage && onLoadMore ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n isLoading={isFetchingNextPage ?? false}\n onClick={onLoadMore}\n >\n Load more\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction RepositoryCard({repository}: {repository: Repository}) {\n const {ref: nameRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(repository.name);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <RadioGroupItem value={repository.externalRepositoryId} className=\"min-w-0\">\n <span ref={nameRef} className=\"block min-w-0 truncate\">\n <Text as=\"span\" size=\"sm\" bold>\n {repository.name}\n </Text>\n </span>\n </RadioGroupItem>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{repository.name}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction RepositoryLoadingState() {\n return (\n <>\n <div role=\"status\" className=\"sr-only\">\n Loading repositories.\n </div>\n <div aria-hidden=\"true\" className={REPOSITORY_GRID_CLASS_NAME}>\n {/* The skeleton mirrors RadioGroupItem's own 14px padding contract so the\n placeholder and the loaded card stay the same height. */}\n {REPOSITORY_SKELETON_WIDTHS.map((width) => (\n <div\n key={width}\n className=\"h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-[14px]\"\n >\n <Skeleton className={`h-20 ${width}`} />\n </div>\n ))}\n </div>\n </>\n );\n}\n"],"names":["Button","useIsTextTruncated","Input","Label","RadioGroup","RadioGroupItem","Skeleton","Tooltip","TooltipContent","TooltipTrigger","Text","useId","REPOSITORY_GRID_CLASS_NAME","REPOSITORY_SKELETON_WIDTHS","RepositoryPicker","repositories","selectedRepositoryId","onSelect","isLoading","isFetchingNextPage","hasNextPage","onLoadMore","emptyMessage","searchValue","onSearchChange","searchDisabled","labelId","showSearch","undefined","div","className","id","type","placeholder","aria-label","value","onChange","event","target","disabled","RepositoryLoadingState","length","size","aria-labelledby","onValueChange","map","repository","RepositoryCard","externalRepositoryId","variant","onClick","ref","nameRef","isTruncated","name","asChild","span","as","bold","role","aria-hidden","width"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAG5B,MAAMC,6BAA6B;AACnC,MAAMC,6BAA6B;IAAC;IAAQ;IAAQ;IAAQ;CAAQ;AAEpE,OAAO,SAASC,iBAAiB,EAC/BC,YAAY,EACZC,oBAAoB,EACpBC,QAAQ,EACRC,SAAS,EACTC,kBAAkB,EAClBC,WAAW,EACXC,UAAU,EACVC,eAAe,wBAAwB,EACvCC,WAAW,EACXC,cAAc,EACdC,cAAc,EAaf;IACC,MAAMC,UAAUf;IAChB,MAAMgB,aAAaH,mBAAmBI;IAEtC,qBACE,MAACC;QAAIC,WAAU;;0BACb,KAAC3B;gBAAM4B,IAAIL;gBAASI,WAAU;0BAAU;;YAIvCH,2BACC,KAACzB;gBACC8B,MAAK;gBACLC,aAAY;gBACZC,cAAW;gBACXC,OAAOZ,eAAe;gBACtBa,UAAU,CAACC,QAAUb,iBAAiBa,MAAMC,MAAM,CAACH,KAAK;gBACxDI,UAAUd;iBAEV;YAEHP,0BAAY,KAACsB,8BAA4B;YAEzC,CAACtB,aAAaH,aAAa0B,MAAM,KAAK,kBACrC,KAACZ;gBAAIC,WAAU;0BACb,cAAA,KAACpB;oBAAKgC,MAAK;8BAAMpB;;iBAEjB;YAEHP,aAAa0B,MAAM,GAAG,kBACrB,KAACrC;gBACCuC,mBAAiBjB;gBACjBS,OAAOnB,wBAAwB;gBAC/B4B,eAAe3B;gBACfa,WAAWlB;0BAEVG,aAAa8B,GAAG,CAAC,CAACC,2BACjB,KAACC;wBAAqDD,YAAYA;uBAA7CA,WAAWE,oBAAoB;iBAGtD;YAEH5B,eAAeC,2BACd,KAACrB;gBACCgC,MAAK;gBACLiB,SAAQ;gBACRP,MAAK;gBACLxB,WAAWC,sBAAsB;gBACjC+B,SAAS7B;0BACV;iBAGC;;;AAGV;AAEA,SAAS0B,eAAe,EAACD,UAAU,EAA2B;IAC5D,MAAM,EAACK,KAAKC,OAAO,EAAEC,WAAW,EAAC,GAAGpD,mBAAoC6C,WAAWQ,IAAI;IAEvF,qBACE,MAAC/C;;0BACC,KAACE;gBAAe8C,OAAO;0BACrB,cAAA,KAAClD;oBAAe8B,OAAOW,WAAWE,oBAAoB;oBAAElB,WAAU;8BAChE,cAAA,KAAC0B;wBAAKL,KAAKC;wBAAStB,WAAU;kCAC5B,cAAA,KAACpB;4BAAK+C,IAAG;4BAAOf,MAAK;4BAAKgB,IAAI;sCAC3BZ,WAAWQ,IAAI;;;;;YAKvBD,4BAAc,KAAC7C;0BAAgBsC,WAAWQ,IAAI;iBAAqB;;;AAG1E;AAEA,SAASd;IACP,qBACE;;0BACE,KAACX;gBAAI8B,MAAK;gBAAS7B,WAAU;0BAAU;;0BAGvC,KAACD;gBAAI+B,eAAY;gBAAO9B,WAAWlB;0BAGhCC,2BAA2BgC,GAAG,CAAC,CAACgB,sBAC/B,KAAChC;wBAECC,WAAU;kCAEV,cAAA,KAACxB;4BAASwB,WAAW,CAAC,KAAK,EAAE+B,OAAO;;uBAH/BA;;;;AASjB"}
|
|
@@ -29,15 +29,15 @@ export function CopyableValue({ label, value, note, className }) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return /*#__PURE__*/ _jsxs("div", {
|
|
32
|
-
className: cn('flex w-full min-w-0 flex-col gap-
|
|
32
|
+
className: cn('flex w-full min-w-0 flex-col gap-inline', className),
|
|
33
33
|
children: [
|
|
34
34
|
/*#__PURE__*/ _jsxs("div", {
|
|
35
|
-
className: "flex min-w-0 items-center gap-
|
|
35
|
+
className: "flex min-w-0 items-center gap-inline",
|
|
36
36
|
children: [
|
|
37
37
|
/*#__PURE__*/ _jsx(Code, {
|
|
38
38
|
as: "span",
|
|
39
39
|
variant: "paragraph",
|
|
40
|
-
className: "min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-
|
|
40
|
+
className: "min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-tight py-[6px] text-foreground-neutral-base",
|
|
41
41
|
children: value
|
|
42
42
|
}),
|
|
43
43
|
/*#__PURE__*/ _jsx(Button, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/webhook/copyable-value.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useCopyToClipboard} from '@shipfox/react-ui/hooks';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface CopyableValueProps {\n label: string;\n value: string;\n note?: ReactNode;\n className?: string;\n}\n\nexport function CopyableValue({label, value, note, className}: CopyableValueProps) {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const {copy} = useCopyToClipboard({\n text: value,\n onCopy: () => {\n setCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n },\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n async function handleCopy() {\n try {\n await copy();\n } catch {\n toast.error('Could not copy.');\n }\n }\n\n return (\n <div className={cn('flex w-full min-w-0 flex-col gap-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/webhook/copyable-value.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useCopyToClipboard} from '@shipfox/react-ui/hooks';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface CopyableValueProps {\n label: string;\n value: string;\n note?: ReactNode;\n className?: string;\n}\n\nexport function CopyableValue({label, value, note, className}: CopyableValueProps) {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const {copy} = useCopyToClipboard({\n text: value,\n onCopy: () => {\n setCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n },\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n async function handleCopy() {\n try {\n await copy();\n } catch {\n toast.error('Could not copy.');\n }\n }\n\n return (\n <div className={cn('flex w-full min-w-0 flex-col gap-inline', className)}>\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Code\n as=\"span\"\n variant=\"paragraph\"\n className=\"min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-tight py-[6px] text-foreground-neutral-base\"\n >\n {value}\n </Code>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n iconLeft={copied ? 'check' : 'copy'}\n aria-label={`Copy ${label}`}\n onClick={() => {\n void handleCopy();\n }}\n >\n Copy\n </Button>\n </div>\n {note ? (\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {note}\n </Text>\n ) : null}\n </div>\n );\n}\n"],"names":["Button","useCopyToClipboard","toast","Code","Text","cn","useEffect","useRef","useState","CopyableValue","label","value","note","className","copied","setCopied","timeoutRef","undefined","copy","text","onCopy","current","clearTimeout","setTimeout","handleCopy","error","div","as","variant","type","size","iconLeft","aria-label","onClick"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,IAAI,EAAEC,IAAI,QAAO,+BAA+B;AACxD,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AASlD,OAAO,SAASC,cAAc,EAACC,KAAK,EAAEC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAqB;IAC/E,MAAM,CAACC,QAAQC,UAAU,GAAGP,SAAS;IACrC,MAAMQ,aAAaT,OAAkDU;IACrE,MAAM,EAACC,IAAI,EAAC,GAAGjB,mBAAmB;QAChCkB,MAAMR;QACNS,QAAQ;YACNL,UAAU;YACV,IAAIC,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;YACvDL,WAAWK,OAAO,GAAGE,WAAW,IAAMR,UAAU,QAAQ;QAC1D;IACF;IAEAT,UAAU;QACR,OAAO;YACL,IAAIU,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;QACzD;IACF,GAAG,EAAE;IAEL,eAAeG;QACb,IAAI;YACF,MAAMN;QACR,EAAE,OAAM;YACNhB,MAAMuB,KAAK,CAAC;QACd;IACF;IAEA,qBACE,MAACC;QAAIb,WAAWR,GAAG,2CAA2CQ;;0BAC5D,MAACa;gBAAIb,WAAU;;kCACb,KAACV;wBACCwB,IAAG;wBACHC,SAAQ;wBACRf,WAAU;kCAETF;;kCAEH,KAACX;wBACC6B,MAAK;wBACLD,SAAQ;wBACRE,MAAK;wBACLC,UAAUjB,SAAS,UAAU;wBAC7BkB,cAAY,CAAC,KAAK,EAAEtB,OAAO;wBAC3BuB,SAAS;4BACP,KAAKT;wBACP;kCACD;;;;YAIFZ,qBACC,KAACR;gBAAK0B,MAAK;gBAAKjB,WAAU;0BACvBD;iBAED;;;AAGV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/webhook/webhook-create-modal.tsx"],"sourcesContent":["import {\n createWebhookConnectionBodySchema,\n WEBHOOK_RESERVED_SLUGS,\n webhookSlugSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport type {IntegrationConnection, WebhookConnection} from '#core/models.js';\nimport {useCreateWebhookConnectionMutation} from '#hooks/api/webhook-connections.js';\nimport {webhookCreateErrorToFormError} from './webhook-form-errors.js';\n\ninterface WebhookCreateModalProps {\n workspaceId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCreated: (connection: IntegrationConnection) => void;\n}\n\nexport function WebhookCreateModal({\n workspaceId,\n open,\n onOpenChange,\n onCreated,\n}: WebhookCreateModalProps) {\n const createWebhook = useCreateWebhookConnectionMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [hasManualSlug, setHasManualSlug] = useState(false);\n const nameInputRef = useRef<HTMLInputElement | null>(null);\n\n const form = useForm({\n defaultValues: {name: '', slug: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = createWebhookConnectionBodySchema.parse({\n workspace_id: workspaceId,\n name: value.name.trim(),\n slug: value.slug.trim(),\n });\n const connection = await createWebhook.mutateAsync(body);\n onCreated(webhookConnectionToIntegrationConnection(connection));\n toast.success('Webhook created.');\n onOpenChange(false);\n } catch (error) {\n const mapped = webhookCreateErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n useEffect(() => {\n if (!open) {\n setFormError(undefined);\n setHasManualSlug(false);\n form.reset();\n return;\n }\n requestAnimationFrame(() => nameInputRef.current?.focus());\n }, [form, open]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (createWebhook.isPending && !nextOpen) return;\n onOpenChange(nextOpen);\n };\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Add webhook</ModalTitle>\n <ModalHeader title=\"Add webhook\" showClose={!createWebhook.isPending} />\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <ModalBody className=\"gap-16\">\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Create a named inbound webhook URL for this workspace.\n </Text>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => webhookNameFieldError(value),\n onSubmit: ({value}) => webhookNameFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"webhook-name\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n ref={nameInputRef}\n name=\"name\"\n value={field.state.value}\n onChange={(event) => {\n const nextName = event.target.value;\n field.handleChange(nextName);\n if (!hasManualSlug) {\n form.setFieldValue(\n 'slug',\n nextName.trim() ? suggestWebhookSlug(nextName) : '',\n );\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"Stripe production\"\n disabled={createWebhook.isPending}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"slug\"\n validators={{\n onBlur: ({value}) => webhookSlugFieldError(value),\n onSubmit: ({value}) => webhookSlugFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Slug\"\n id=\"webhook-slug\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n name=\"slug\"\n value={field.state.value}\n onChange={(event) => {\n const nextSlug = event.target.value;\n setHasManualSlug(nextSlug.trim().length > 0);\n field.handleChange(nextSlug);\n if (nextSlug.trim().length === 0 && form.state.values.name.trim()) {\n form.setFieldValue('slug', suggestWebhookSlug(form.state.values.name));\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"webhook-stripe-production\"\n disabled={createWebhook.isPending}\n />\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference in workflows with{' '}\n <Code as=\"span\">\n source: {field.state.value || 'webhook-stripe-production'}\n </Code>\n .\n </Text>\n </FormField>\n )}\n </form.Field>\n </ModalBody>\n <ModalFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={createWebhook.isPending}\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button type=\"submit\" isLoading={createWebhook.isPending}>\n Create\n </Button>\n </ModalFooter>\n </form>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction webhookNameFieldError(value: string): string | undefined {\n return displayNameFieldError(\n value.trim(),\n 'Webhook name',\n createWebhookConnectionBodySchema.shape.name,\n );\n}\n\nfunction webhookSlugFieldError(value: string): string | undefined {\n const trimmed = value.trim();\n if (trimmed.length === 0) return 'Slug is required.';\n if (WEBHOOK_RESERVED_SLUGS.includes(trimmed as (typeof WEBHOOK_RESERVED_SLUGS)[number])) {\n return 'That slug is reserved.';\n }\n if (webhookSlugSchema.safeParse(trimmed).success) return undefined;\n return 'Use lowercase letters, numbers, hyphens, or underscores.';\n}\n\nfunction suggestWebhookSlug(name: string): string {\n const prefix = 'webhook';\n const suffix = name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '')\n .slice(0, 100 - prefix.length - 1)\n .replaceAll(/-+$/g, '');\n\n return suffix ? `${prefix}-${suffix}` : prefix;\n}\n\nfunction webhookConnectionToIntegrationConnection(\n connection: WebhookConnection,\n): IntegrationConnection {\n return {\n id: connection.id,\n workspaceId: connection.workspaceId,\n provider: 'webhook',\n externalAccountId: connection.slug,\n slug: connection.slug,\n displayName: connection.name,\n lifecycleStatus: connection.lifecycleStatus,\n capabilities: [],\n createdAt: connection.createdAt,\n updatedAt: connection.updatedAt,\n };\n}\n"],"names":["createWebhookConnectionBodySchema","WEBHOOK_RESERVED_SLUGS","webhookSlugSchema","displayNameFieldError","Button","Callout","FormField","FormFieldInput","fieldError","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","toast","Code","Text","useForm","useEffect","useRef","useState","useCreateWebhookConnectionMutation","webhookCreateErrorToFormError","WebhookCreateModal","workspaceId","open","onOpenChange","onCreated","createWebhook","formError","setFormError","hasManualSlug","setHasManualSlug","nameInputRef","form","defaultValues","name","slug","onSubmit","value","undefined","body","parse","workspace_id","trim","connection","mutateAsync","webhookConnectionToIntegrationConnection","success","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","reset","requestAnimationFrame","current","focus","handleOpenChange","nextOpen","isPending","aria-describedby","className","title","showClose","noValidate","event","preventDefault","stopPropagation","handleSubmit","size","role","type","Field","validators","onBlur","webhookNameFieldError","label","id","ref","state","onChange","nextName","target","handleChange","setFieldValue","suggestWebhookSlug","handleBlur","placeholder","disabled","webhookSlugFieldError","nextSlug","length","values","as","variant","onClick","isLoading","shape","trimmed","includes","safeParse","prefix","suffix","toLowerCase","replaceAll","slice","provider","externalAccountId","displayName","lifecycleStatus","capabilities","createdAt","updatedAt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/webhook/webhook-create-modal.tsx"],"sourcesContent":["import {\n createWebhookConnectionBodySchema,\n WEBHOOK_RESERVED_SLUGS,\n webhookSlugSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport type {IntegrationConnection, WebhookConnection} from '#core/models.js';\nimport {useCreateWebhookConnectionMutation} from '#hooks/api/webhook-connections.js';\nimport {webhookCreateErrorToFormError} from './webhook-form-errors.js';\n\ninterface WebhookCreateModalProps {\n workspaceId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCreated: (connection: IntegrationConnection) => void;\n}\n\nexport function WebhookCreateModal({\n workspaceId,\n open,\n onOpenChange,\n onCreated,\n}: WebhookCreateModalProps) {\n const createWebhook = useCreateWebhookConnectionMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [hasManualSlug, setHasManualSlug] = useState(false);\n const nameInputRef = useRef<HTMLInputElement | null>(null);\n\n const form = useForm({\n defaultValues: {name: '', slug: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = createWebhookConnectionBodySchema.parse({\n workspace_id: workspaceId,\n name: value.name.trim(),\n slug: value.slug.trim(),\n });\n const connection = await createWebhook.mutateAsync(body);\n onCreated(webhookConnectionToIntegrationConnection(connection));\n toast.success('Webhook created.');\n onOpenChange(false);\n } catch (error) {\n const mapped = webhookCreateErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n useEffect(() => {\n if (!open) {\n setFormError(undefined);\n setHasManualSlug(false);\n form.reset();\n return;\n }\n requestAnimationFrame(() => nameInputRef.current?.focus());\n }, [form, open]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (createWebhook.isPending && !nextOpen) return;\n onOpenChange(nextOpen);\n };\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Add webhook</ModalTitle>\n <ModalHeader title=\"Add webhook\" showClose={!createWebhook.isPending} />\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <ModalBody className=\"gap-group\">\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Create a named inbound webhook URL for this workspace.\n </Text>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => webhookNameFieldError(value),\n onSubmit: ({value}) => webhookNameFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"webhook-name\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n ref={nameInputRef}\n name=\"name\"\n value={field.state.value}\n onChange={(event) => {\n const nextName = event.target.value;\n field.handleChange(nextName);\n if (!hasManualSlug) {\n form.setFieldValue(\n 'slug',\n nextName.trim() ? suggestWebhookSlug(nextName) : '',\n );\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"Stripe production\"\n disabled={createWebhook.isPending}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"slug\"\n validators={{\n onBlur: ({value}) => webhookSlugFieldError(value),\n onSubmit: ({value}) => webhookSlugFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Slug\"\n id=\"webhook-slug\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n name=\"slug\"\n value={field.state.value}\n onChange={(event) => {\n const nextSlug = event.target.value;\n setHasManualSlug(nextSlug.trim().length > 0);\n field.handleChange(nextSlug);\n if (nextSlug.trim().length === 0 && form.state.values.name.trim()) {\n form.setFieldValue('slug', suggestWebhookSlug(form.state.values.name));\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"webhook-stripe-production\"\n disabled={createWebhook.isPending}\n />\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference in workflows with{' '}\n <Code as=\"span\">\n source: {field.state.value || 'webhook-stripe-production'}\n </Code>\n .\n </Text>\n </FormField>\n )}\n </form.Field>\n </ModalBody>\n <ModalFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={createWebhook.isPending}\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button type=\"submit\" isLoading={createWebhook.isPending}>\n Create\n </Button>\n </ModalFooter>\n </form>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction webhookNameFieldError(value: string): string | undefined {\n return displayNameFieldError(\n value.trim(),\n 'Webhook name',\n createWebhookConnectionBodySchema.shape.name,\n );\n}\n\nfunction webhookSlugFieldError(value: string): string | undefined {\n const trimmed = value.trim();\n if (trimmed.length === 0) return 'Slug is required.';\n if (WEBHOOK_RESERVED_SLUGS.includes(trimmed as (typeof WEBHOOK_RESERVED_SLUGS)[number])) {\n return 'That slug is reserved.';\n }\n if (webhookSlugSchema.safeParse(trimmed).success) return undefined;\n return 'Use lowercase letters, numbers, hyphens, or underscores.';\n}\n\nfunction suggestWebhookSlug(name: string): string {\n const prefix = 'webhook';\n const suffix = name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '')\n .slice(0, 100 - prefix.length - 1)\n .replaceAll(/-+$/g, '');\n\n return suffix ? `${prefix}-${suffix}` : prefix;\n}\n\nfunction webhookConnectionToIntegrationConnection(\n connection: WebhookConnection,\n): IntegrationConnection {\n return {\n id: connection.id,\n workspaceId: connection.workspaceId,\n provider: 'webhook',\n externalAccountId: connection.slug,\n slug: connection.slug,\n displayName: connection.name,\n lifecycleStatus: connection.lifecycleStatus,\n capabilities: [],\n createdAt: connection.createdAt,\n updatedAt: connection.updatedAt,\n };\n}\n"],"names":["createWebhookConnectionBodySchema","WEBHOOK_RESERVED_SLUGS","webhookSlugSchema","displayNameFieldError","Button","Callout","FormField","FormFieldInput","fieldError","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","toast","Code","Text","useForm","useEffect","useRef","useState","useCreateWebhookConnectionMutation","webhookCreateErrorToFormError","WebhookCreateModal","workspaceId","open","onOpenChange","onCreated","createWebhook","formError","setFormError","hasManualSlug","setHasManualSlug","nameInputRef","form","defaultValues","name","slug","onSubmit","value","undefined","body","parse","workspace_id","trim","connection","mutateAsync","webhookConnectionToIntegrationConnection","success","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","reset","requestAnimationFrame","current","focus","handleOpenChange","nextOpen","isPending","aria-describedby","className","title","showClose","noValidate","event","preventDefault","stopPropagation","handleSubmit","size","role","type","Field","validators","onBlur","webhookNameFieldError","label","id","ref","state","onChange","nextName","target","handleChange","setFieldValue","suggestWebhookSlug","handleBlur","placeholder","disabled","webhookSlugFieldError","nextSlug","length","values","as","variant","onClick","isLoading","shape","trimmed","includes","safeParse","prefix","suffix","toLowerCase","replaceAll","slice","provider","externalAccountId","displayName","lifecycleStatus","capabilities","createdAt","updatedAt"],"mappings":"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"}
|
|
@@ -27,9 +27,9 @@ export function WebhookUsageDetails({ workspaceId, connectionId }) {
|
|
|
27
27
|
}
|
|
28
28
|
if (!connection) return null;
|
|
29
29
|
return /*#__PURE__*/ _jsx("div", {
|
|
30
|
-
className: "flex w-full flex-col gap-
|
|
30
|
+
className: "flex w-full flex-col gap-group",
|
|
31
31
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
32
|
-
className: "flex w-full min-w-0 flex-col gap-
|
|
32
|
+
className: "flex w-full min-w-0 flex-col gap-inline",
|
|
33
33
|
children: [
|
|
34
34
|
/*#__PURE__*/ _jsx(Text, {
|
|
35
35
|
size: "sm",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/webhook/webhook-usage-details.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {ShipfoxLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useWebhookConnectionsQuery} from '#hooks/api/webhook-connections.js';\nimport {CopyableValue} from './copyable-value.js';\nimport {WebhookPublicEndpointAlert} from './webhook-public-endpoint-alert.js';\n\ninterface WebhookUsageDetailsProps {\n workspaceId: string;\n connectionId: string;\n}\n\nexport function WebhookUsageDetails({workspaceId, connectionId}: WebhookUsageDetailsProps) {\n const connectionsQuery = useWebhookConnectionsQuery(workspaceId);\n const connection = connectionsQuery.data?.find((candidate) => candidate.id === connectionId);\n\n if (connectionsQuery.isPending) {\n return (\n <div className=\"flex min-h-80 items-center justify-center\">\n <ShipfoxLoader size={32} animation=\"circular\" color=\"orange\" background=\"light\" />\n </div>\n );\n }\n\n if (connectionsQuery.isError && connectionsQuery.data === undefined) {\n return <QueryLoadError query={connectionsQuery} subject=\"webhook details\" />;\n }\n\n if (!connection) return null;\n\n return (\n <div className=\"flex w-full flex-col gap-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/webhook/webhook-usage-details.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {ShipfoxLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useWebhookConnectionsQuery} from '#hooks/api/webhook-connections.js';\nimport {CopyableValue} from './copyable-value.js';\nimport {WebhookPublicEndpointAlert} from './webhook-public-endpoint-alert.js';\n\ninterface WebhookUsageDetailsProps {\n workspaceId: string;\n connectionId: string;\n}\n\nexport function WebhookUsageDetails({workspaceId, connectionId}: WebhookUsageDetailsProps) {\n const connectionsQuery = useWebhookConnectionsQuery(workspaceId);\n const connection = connectionsQuery.data?.find((candidate) => candidate.id === connectionId);\n\n if (connectionsQuery.isPending) {\n return (\n <div className=\"flex min-h-80 items-center justify-center\">\n <ShipfoxLoader size={32} animation=\"circular\" color=\"orange\" background=\"light\" />\n </div>\n );\n }\n\n if (connectionsQuery.isError && connectionsQuery.data === undefined) {\n return <QueryLoadError query={connectionsQuery} subject=\"webhook details\" />;\n }\n\n if (!connection) return null;\n\n return (\n <div className=\"flex w-full flex-col gap-group\">\n <div className=\"flex w-full min-w-0 flex-col gap-inline\">\n <Text size=\"sm\" bold>\n Inbound URL\n </Text>\n <CopyableValue label=\"inbound URL\" value={connection.inboundUrl} />\n <WebhookPublicEndpointAlert />\n </div>\n </div>\n );\n}\n"],"names":["QueryLoadError","ShipfoxLoader","Text","useWebhookConnectionsQuery","CopyableValue","WebhookPublicEndpointAlert","WebhookUsageDetails","workspaceId","connectionId","connectionsQuery","connection","data","find","candidate","id","isPending","div","className","size","animation","color","background","isError","undefined","query","subject","bold","label","value","inboundUrl"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,aAAa,QAAO,2BAA2B;AACvD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,aAAa,QAAO,sBAAsB;AAClD,SAAQC,0BAA0B,QAAO,qCAAqC;AAO9E,OAAO,SAASC,oBAAoB,EAACC,WAAW,EAAEC,YAAY,EAA2B;IACvF,MAAMC,mBAAmBN,2BAA2BI;IACpD,MAAMG,aAAaD,iBAAiBE,IAAI,EAAEC,KAAK,CAACC,YAAcA,UAAUC,EAAE,KAAKN;IAE/E,IAAIC,iBAAiBM,SAAS,EAAE;QAC9B,qBACE,KAACC;YAAIC,WAAU;sBACb,cAAA,KAAChB;gBAAciB,MAAM;gBAAIC,WAAU;gBAAWC,OAAM;gBAASC,YAAW;;;IAG9E;IAEA,IAAIZ,iBAAiBa,OAAO,IAAIb,iBAAiBE,IAAI,KAAKY,WAAW;QACnE,qBAAO,KAACvB;YAAewB,OAAOf;YAAkBgB,SAAQ;;IAC1D;IAEA,IAAI,CAACf,YAAY,OAAO;IAExB,qBACE,KAACM;QAAIC,WAAU;kBACb,cAAA,MAACD;YAAIC,WAAU;;8BACb,KAACf;oBAAKgB,MAAK;oBAAKQ,IAAI;8BAAC;;8BAGrB,KAACtB;oBAAcuB,OAAM;oBAAcC,OAAOlB,WAAWmB,UAAU;;8BAC/D,KAACxB;;;;AAIT"}
|
|
@@ -51,10 +51,10 @@ export function GiteaInstallPage() {
|
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
53
|
return /*#__PURE__*/ _jsxs("div", {
|
|
54
|
-
className: "mx-auto flex w-full max-w-[480px] flex-col gap-
|
|
54
|
+
className: "mx-auto flex w-full max-w-[480px] flex-col gap-section",
|
|
55
55
|
children: [
|
|
56
56
|
/*#__PURE__*/ _jsxs("header", {
|
|
57
|
-
className: "flex flex-col gap-
|
|
57
|
+
className: "flex flex-col gap-inline",
|
|
58
58
|
children: [
|
|
59
59
|
/*#__PURE__*/ _jsx(Header, {
|
|
60
60
|
variant: "h1",
|
|
@@ -68,7 +68,7 @@ export function GiteaInstallPage() {
|
|
|
68
68
|
]
|
|
69
69
|
}),
|
|
70
70
|
/*#__PURE__*/ _jsxs("form", {
|
|
71
|
-
className: "flex flex-col gap-
|
|
71
|
+
className: "flex flex-col gap-group",
|
|
72
72
|
noValidate: true,
|
|
73
73
|
onSubmit: (event)=>{
|
|
74
74
|
event.preventDefault();
|
|
@@ -101,7 +101,7 @@ export function GiteaInstallPage() {
|
|
|
101
101
|
})
|
|
102
102
|
}),
|
|
103
103
|
/*#__PURE__*/ _jsxs("div", {
|
|
104
|
-
className: "flex items-center gap-
|
|
104
|
+
className: "flex items-center gap-cluster",
|
|
105
105
|
children: [
|
|
106
106
|
/*#__PURE__*/ _jsx(Button, {
|
|
107
107
|
type: "submit",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/gitea-install-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {useState} from 'react';\nimport {useCreateGiteaConnectionMutation} from '#hooks/api/integrations.js';\nimport {giteaConnectErrorToFormError} from './gitea-form-errors.js';\n\nexport function GiteaInstallPage() {\n const workspace = useActiveWorkspace();\n const connect = useCreateGiteaConnectionMutation();\n const navigate = useNavigate();\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {org: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n const workspaceId = workspace.id;\n try {\n await connect.mutateAsync({workspace_id: workspaceId, org: value.org.trim()});\n toast.success('Gitea organization installed.');\n await navigate({to: '/w/$workspaceSlug', params: {workspaceSlug: workspace.slug}});\n } catch (error) {\n const mapped = giteaConnectErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-
|
|
1
|
+
{"version":3,"sources":["../../src/pages/gitea-install-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {useState} from 'react';\nimport {useCreateGiteaConnectionMutation} from '#hooks/api/integrations.js';\nimport {giteaConnectErrorToFormError} from './gitea-form-errors.js';\n\nexport function GiteaInstallPage() {\n const workspace = useActiveWorkspace();\n const connect = useCreateGiteaConnectionMutation();\n const navigate = useNavigate();\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {org: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n const workspaceId = workspace.id;\n try {\n await connect.mutateAsync({workspace_id: workspaceId, org: value.org.trim()});\n toast.success('Gitea organization installed.');\n await navigate({to: '/w/$workspaceSlug', params: {workspaceSlug: workspace.slug}});\n } catch (error) {\n const mapped = giteaConnectErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-section\">\n <header className=\"flex flex-col gap-inline\">\n <Header variant=\"h1\">Install Gitea</Header>\n <Text size=\"md\" className=\"text-foreground-neutral-muted\">\n Enter the Gitea organization to install in this workspace.\n </Text>\n </header>\n\n <form\n className=\"flex flex-col gap-group\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"org\"\n validators={{\n onBlur: ({value}) =>\n value.trim().length > 0 ? undefined : 'Enter your Gitea organization.',\n onSubmit: ({value}) =>\n value.trim().length > 0 ? undefined : 'Enter your Gitea organization.',\n }}\n >\n {(field) => (\n <FormField label=\"Organization\" id=\"gitea-org\" error={fieldError(field)}>\n <FormFieldInput\n placeholder=\"my-org\"\n autoComplete=\"off\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <div className=\"flex items-center gap-cluster\">\n <Button type=\"submit\" isLoading={connect.isPending}>\n Install\n </Button>\n <ButtonLink asChild variant=\"muted\">\n <Link to=\"/w/$workspaceSlug/integrations\" params={{workspaceSlug: workspace.slug}}>\n Cancel\n </Link>\n </ButtonLink>\n </div>\n </form>\n </div>\n );\n}\n"],"names":["useActiveWorkspace","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Header","Text","useForm","Link","useNavigate","useState","useCreateGiteaConnectionMutation","giteaConnectErrorToFormError","GiteaInstallPage","workspace","connect","navigate","formError","setFormError","form","defaultValues","org","onSubmit","value","undefined","workspaceId","id","mutateAsync","workspace_id","trim","success","to","params","workspaceSlug","slug","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","div","className","header","variant","size","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","name","validators","onBlur","length","label","placeholder","autoComplete","state","onChange","handleChange","target","handleBlur","isLoading","isPending","asChild"],"mappings":"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"}
|
|
@@ -235,12 +235,12 @@ function JiraCallbackFailurePage({ failure, workspaceSlug }) {
|
|
|
235
235
|
}
|
|
236
236
|
function JiraSiteSelectionPage({ sites, selectedCloudId, failure, workspaceSlug, onSelect }) {
|
|
237
237
|
return /*#__PURE__*/ _jsx("main", {
|
|
238
|
-
className: "flex min-h-screen bg-background-subtle-base px-
|
|
238
|
+
className: "flex min-h-screen bg-background-subtle-base px-row py-frame",
|
|
239
239
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
240
|
-
className: "mx-auto flex w-full max-w-[480px] flex-col justify-center gap-
|
|
240
|
+
className: "mx-auto flex w-full max-w-[480px] flex-col justify-center gap-section",
|
|
241
241
|
children: [
|
|
242
242
|
/*#__PURE__*/ _jsxs("header", {
|
|
243
|
-
className: "flex flex-col gap-
|
|
243
|
+
className: "flex flex-col gap-inline",
|
|
244
244
|
children: [
|
|
245
245
|
/*#__PURE__*/ _jsx(Header, {
|
|
246
246
|
variant: "h2",
|
|
@@ -262,12 +262,12 @@ function JiraSiteSelectionPage({ sites, selectedCloudId, failure, workspaceSlug,
|
|
|
262
262
|
})
|
|
263
263
|
}) : null,
|
|
264
264
|
/*#__PURE__*/ _jsx("section", {
|
|
265
|
-
className: "flex flex-col gap-
|
|
265
|
+
className: "flex flex-col gap-inline",
|
|
266
266
|
"aria-label": "Choose a Jira site",
|
|
267
267
|
children: sites.map((site)=>/*#__PURE__*/ _jsx(Card, {
|
|
268
|
-
className: "p-
|
|
268
|
+
className: "p-panel-compact",
|
|
269
269
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
270
|
-
className: "flex items-center justify-between gap-
|
|
270
|
+
className: "flex items-center justify-between gap-cluster",
|
|
271
271
|
children: [
|
|
272
272
|
/*#__PURE__*/ _jsxs("div", {
|
|
273
273
|
className: "min-w-0",
|
|
@@ -297,7 +297,7 @@ function JiraSiteSelectionPage({ sites, selectedCloudId, failure, workspaceSlug,
|
|
|
297
297
|
}, site.cloudId))
|
|
298
298
|
}),
|
|
299
299
|
/*#__PURE__*/ _jsxs("div", {
|
|
300
|
-
className: "flex flex-col gap-
|
|
300
|
+
className: "flex flex-col gap-inline sm:flex-row sm:items-center",
|
|
301
301
|
children: [
|
|
302
302
|
failure?.startOver && workspaceSlug ? /*#__PURE__*/ _jsx(ButtonLink, {
|
|
303
303
|
asChild: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/jira-callback-page.tsx"],"sourcesContent":["import {useAuthState, useRefreshAuth} from '@shipfox/client-auth';\nimport {useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Card} from '@shipfox/react-ui/card';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useQueryClient} from '@tanstack/react-query';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {\n useCompleteIntegrationCallback,\n useCompleteIntegrationCallbackResult,\n} from '#application/complete-integration-callback.js';\nimport {CallbackStatusShell} from '#components/callback-status-shell.js';\nimport type {IntegrationConnection, JiraSite} from '#core/models.js';\nimport {\n useCompleteJiraCallbackMutation,\n useCompleteJiraSiteSelectionMutation,\n} from '#hooks/api/integrations.js';\nimport {\n clearJiraInstallWorkspace,\n parseJiraCallbackQuery,\n readJiraInstallWorkspace,\n serializeJiraCallbackQuery,\n} from '#jira-callback.js';\nimport {\n JIRA_CALLBACK_CACHE_SIZE,\n jiraCallbackRequests,\n jiraCompletedConnections,\n jiraSiteSelectionRequests,\n jiraToastedCallbacks,\n} from '#jira-callback-state.js';\nimport {classifyJiraCallbackError, type JiraCallbackFailure} from '#jira-form-errors.js';\nimport {rememberCallbackKey, resolveWorkspaceSlug} from '#workspace-navigation.js';\n\nexport function JiraCallbackPage() {\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const refreshAuth = useRefreshAuth();\n const {workspaces, isLoading} = useAuthState();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n const completeIntegrationCallbackResult = useCompleteIntegrationCallbackResult();\n const {mutateAsync: completeJiraCallback} = useCompleteJiraCallbackMutation();\n const {mutateAsync: completeJiraSiteSelection} = useCompleteJiraSiteSelectionMutation();\n const params = useRouteSearch(parseJiraCallbackQuery);\n const callbackKey = params ? serializeJiraCallbackQuery(params) : undefined;\n const workspaceId = useMemo(() => readJiraInstallWorkspace(sessionStorageOrUndefined()), []);\n const [sites, setSites] = useState<JiraSite[] | undefined>();\n const [failure, setFailure] = useState<JiraCallbackFailure | undefined>();\n const [selectedCloudId, setSelectedCloudId] = useState<string | undefined>();\n const [completedWorkspace, setCompletedWorkspace] = useState<{\n slug?: string | undefined;\n }>();\n const disposedRef = useRef(false);\n const callbackKeyRef = useRef(callbackKey);\n callbackKeyRef.current = callbackKey;\n const [callbackStateKey, setCallbackStateKey] = useState(callbackKey);\n\n useEffect(() => {\n disposedRef.current = false;\n return () => {\n disposedRef.current = true;\n };\n }, []);\n\n const completeConnection = useCallback(\n async (\n connection: IntegrationConnection,\n callbackKey: string,\n isActive: () => boolean = () => true,\n ) => {\n if (disposedRef.current || !isActive()) return;\n const completedConnection = jiraCompletedConnections.get(callbackKey);\n if (completedConnection) {\n const workspaceSlug = await resolveWorkspaceSlug({\n workspaceId: completedConnection.workspaceId,\n fallbackWorkspaces: workspaces,\n queryClient,\n });\n if (!disposedRef.current && isActive())\n setCompletedWorkspace(workspaceSlug ? {slug: workspaceSlug} : {});\n return;\n }\n rememberCompletedConnection(callbackKey, connection);\n try {\n clearJiraInstallWorkspace(sessionStorageOrUndefined());\n } catch {\n // The successful API response remains the source of truth for navigation.\n }\n if (disposedRef.current || !isActive()) return;\n if (!jiraToastedCallbacks.has(callbackKey)) {\n rememberCallbackKey(jiraToastedCallbacks, callbackKey);\n toast.success('Jira installed.');\n }\n let workspaceSlug: string | undefined;\n try {\n workspaceSlug = await resolveWorkspaceSlug({\n workspaceId: connection.workspaceId,\n fallbackWorkspaces: workspaces,\n queryClient,\n });\n if (disposedRef.current || !isActive()) return;\n if (!workspaceSlug) {\n setCompletedWorkspace({});\n return;\n }\n setCompletedWorkspace({slug: workspaceSlug});\n await navigate({\n to: '/w/$workspaceSlug/settings/integrations',\n params: {workspaceSlug},\n replace: true,\n });\n } catch {\n if (!disposedRef.current && isActive()) setCompletedWorkspace({slug: workspaceSlug});\n }\n },\n [navigate, queryClient, workspaces],\n );\n\n useEffect(() => {\n if (callbackKey === undefined) return;\n setCallbackStateKey(callbackKey);\n setSites(undefined);\n setSelectedCloudId(undefined);\n setFailure(undefined);\n setCompletedWorkspace(undefined);\n }, [callbackKey]);\n\n useEffect(() => {\n if (!params || !callbackKey || isLoading) return;\n\n let active = true;\n\n const completedConnection = jiraCompletedConnections.get(callbackKey);\n if (completedConnection) {\n void completeConnection(completedConnection, callbackKey, () => active);\n return () => {\n active = false;\n };\n }\n\n const request = jiraCallbackRequests.run(callbackKey, async () => {\n return await completeIntegrationCallbackResult({\n input: params,\n refreshAuth,\n complete: async (query, token) => await completeJiraCallback({query, token}),\n getConnection: (result) => ('sites' in result ? undefined : result),\n });\n });\n\n request.then(\n async (result) => {\n if (disposedRef.current || !active) return;\n if ('sites' in result) {\n setFailure(undefined);\n setSites(result.sites);\n return;\n }\n await completeConnection(result, callbackKey, () => active);\n },\n (error: unknown) => {\n if (!disposedRef.current && active) setFailure(classifyJiraCallbackError(error));\n },\n );\n\n return () => {\n active = false;\n };\n }, [\n callbackKey,\n completeIntegrationCallbackResult,\n completeJiraCallback,\n completeConnection,\n isLoading,\n params,\n refreshAuth,\n ]);\n\n function selectSite(site: JiraSite) {\n if (!params || !callbackKey || selectedCloudId) return;\n setSelectedCloudId(site.cloudId);\n setFailure(undefined);\n const selectionKey = callbackKey;\n const request = jiraSiteSelectionRequests.run(params.state, async () => {\n return await completeIntegrationCallback({\n input: {cloud_id: site.cloudId, state: params.state},\n refreshAuth,\n complete: async (body, token) => await completeJiraSiteSelection({body, token}),\n });\n });\n request.then(\n async (connection) => {\n if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;\n jiraCallbackRequests.clear(selectionKey);\n await completeConnection(\n connection,\n selectionKey,\n () => callbackKeyRef.current === selectionKey,\n );\n },\n (error: unknown) => {\n if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;\n setSelectedCloudId(undefined);\n setFailure(classifyJiraCallbackError(error));\n },\n );\n }\n\n if (!params) {\n return (\n <JiraCallbackFailurePage\n failure={classifyJiraCallbackError(new Error('invalid callback'))}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n />\n );\n }\n\n if (isLoading) return <FullPageLoader aria-label=\"Connecting Jira\" />;\n\n if (callbackStateKey === callbackKey && completedWorkspace)\n return (\n <CallbackStatusShell\n title=\"Jira connected\"\n status=\"success\"\n message={\n completedWorkspace.slug\n ? 'Jira is connected. Continue in integrations settings.'\n : 'Jira is connected. Return to Shipfox to continue.'\n }\n workspaceSlug={completedWorkspace.slug}\n installPath=\"/w/$workspaceSlug/integrations/jira\"\n />\n );\n\n if (callbackStateKey === callbackKey && sites)\n return (\n <JiraSiteSelectionPage\n sites={sites}\n selectedCloudId={selectedCloudId}\n failure={failure}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n onSelect={selectSite}\n />\n );\n\n if (callbackStateKey === callbackKey && failure)\n return (\n <JiraCallbackFailurePage\n failure={failure}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n />\n );\n\n return <FullPageLoader aria-label=\"Connecting Jira\" />;\n}\n\nfunction rememberCompletedConnection(key: string, connection: IntegrationConnection): void {\n jiraCompletedConnections.delete(key);\n jiraCompletedConnections.set(key, connection);\n while (jiraCompletedConnections.size > JIRA_CALLBACK_CACHE_SIZE) {\n const oldestKey = jiraCompletedConnections.keys().next().value;\n if (oldestKey === undefined) return;\n jiraCompletedConnections.delete(oldestKey);\n }\n}\n\nfunction JiraCallbackFailurePage({\n failure,\n workspaceSlug,\n}: {\n failure: JiraCallbackFailure;\n workspaceSlug: string | undefined;\n}) {\n return (\n <CallbackStatusShell\n title={failure.title}\n message={failure.message}\n startOver={failure.startOver}\n switchAccount={failure.signIn}\n workspaceSlug={workspaceSlug}\n installPath=\"/w/$workspaceSlug/integrations/jira\"\n />\n );\n}\n\nfunction JiraSiteSelectionPage({\n sites,\n selectedCloudId,\n failure,\n workspaceSlug,\n onSelect,\n}: {\n sites: JiraSite[];\n selectedCloudId: string | undefined;\n failure: JiraCallbackFailure | undefined;\n workspaceSlug: string | undefined;\n onSelect: (site: JiraSite) => void;\n}) {\n return (\n <main className=\"flex min-h-screen bg-background-subtle-base px-16 py-32\">\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col justify-center gap-20\">\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h2\">Choose a Jira site</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Select the Jira site to connect to this Shipfox workspace.\n </Text>\n </header>\n\n {failure ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{failure.message}</Text>\n </Callout>\n ) : null}\n\n <section className=\"flex flex-col gap-8\" aria-label=\"Choose a Jira site\">\n {sites.map((site) => (\n <Card key={site.cloudId} className=\"p-16\">\n <div className=\"flex items-center justify-between gap-12\">\n <div className=\"min-w-0\">\n <Text size=\"md\" bold className=\"truncate\">\n {site.name}\n </Text>\n <Text size=\"sm\" className=\"truncate text-foreground-neutral-muted\">\n {site.url}\n </Text>\n </div>\n <Button\n variant=\"secondary\"\n disabled={selectedCloudId !== undefined}\n isLoading={selectedCloudId === site.cloudId}\n onClick={() => onSelect(site)}\n >\n Connect\n </Button>\n </div>\n </Card>\n ))}\n </section>\n\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n {failure?.startOver && workspaceSlug ? (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/integrations/jira\" params={{workspaceSlug}}>\n Start over\n </Link>\n </ButtonLink>\n ) : null}\n {workspaceSlug ? (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/settings/integrations\" params={{workspaceSlug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n ) : (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n )}\n </div>\n </div>\n </main>\n );\n}\n"],"names":["useAuthState","useRefreshAuth","useRouteSearch","sessionStorageOrUndefined","Button","ButtonLink","Callout","Card","FullPageLoader","toast","Header","Text","useQueryClient","Link","useNavigate","useCallback","useEffect","useMemo","useRef","useState","useCompleteIntegrationCallback","useCompleteIntegrationCallbackResult","CallbackStatusShell","useCompleteJiraCallbackMutation","useCompleteJiraSiteSelectionMutation","clearJiraInstallWorkspace","parseJiraCallbackQuery","readJiraInstallWorkspace","serializeJiraCallbackQuery","JIRA_CALLBACK_CACHE_SIZE","jiraCallbackRequests","jiraCompletedConnections","jiraSiteSelectionRequests","jiraToastedCallbacks","classifyJiraCallbackError","rememberCallbackKey","resolveWorkspaceSlug","JiraCallbackPage","navigate","queryClient","refreshAuth","workspaces","isLoading","completeIntegrationCallback","completeIntegrationCallbackResult","mutateAsync","completeJiraCallback","completeJiraSiteSelection","params","callbackKey","undefined","workspaceId","sites","setSites","failure","setFailure","selectedCloudId","setSelectedCloudId","completedWorkspace","setCompletedWorkspace","disposedRef","callbackKeyRef","current","callbackStateKey","setCallbackStateKey","completeConnection","connection","isActive","completedConnection","get","workspaceSlug","fallbackWorkspaces","slug","rememberCompletedConnection","has","success","to","replace","active","request","run","input","complete","query","token","getConnection","result","then","error","selectSite","site","cloudId","selectionKey","state","cloud_id","body","clear","JiraCallbackFailurePage","Error","find","id","aria-label","title","status","message","installPath","JiraSiteSelectionPage","onSelect","key","delete","set","size","oldestKey","keys","next","value","startOver","switchAccount","signIn","main","className","div","header","variant","role","type","section","map","bold","name","url","disabled","onClick","asChild"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/jira-callback-page.tsx"],"sourcesContent":["import {useAuthState, useRefreshAuth} from '@shipfox/client-auth';\nimport {useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Card} from '@shipfox/react-ui/card';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useQueryClient} from '@tanstack/react-query';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {\n useCompleteIntegrationCallback,\n useCompleteIntegrationCallbackResult,\n} from '#application/complete-integration-callback.js';\nimport {CallbackStatusShell} from '#components/callback-status-shell.js';\nimport type {IntegrationConnection, JiraSite} from '#core/models.js';\nimport {\n useCompleteJiraCallbackMutation,\n useCompleteJiraSiteSelectionMutation,\n} from '#hooks/api/integrations.js';\nimport {\n clearJiraInstallWorkspace,\n parseJiraCallbackQuery,\n readJiraInstallWorkspace,\n serializeJiraCallbackQuery,\n} from '#jira-callback.js';\nimport {\n JIRA_CALLBACK_CACHE_SIZE,\n jiraCallbackRequests,\n jiraCompletedConnections,\n jiraSiteSelectionRequests,\n jiraToastedCallbacks,\n} from '#jira-callback-state.js';\nimport {classifyJiraCallbackError, type JiraCallbackFailure} from '#jira-form-errors.js';\nimport {rememberCallbackKey, resolveWorkspaceSlug} from '#workspace-navigation.js';\n\nexport function JiraCallbackPage() {\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const refreshAuth = useRefreshAuth();\n const {workspaces, isLoading} = useAuthState();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n const completeIntegrationCallbackResult = useCompleteIntegrationCallbackResult();\n const {mutateAsync: completeJiraCallback} = useCompleteJiraCallbackMutation();\n const {mutateAsync: completeJiraSiteSelection} = useCompleteJiraSiteSelectionMutation();\n const params = useRouteSearch(parseJiraCallbackQuery);\n const callbackKey = params ? serializeJiraCallbackQuery(params) : undefined;\n const workspaceId = useMemo(() => readJiraInstallWorkspace(sessionStorageOrUndefined()), []);\n const [sites, setSites] = useState<JiraSite[] | undefined>();\n const [failure, setFailure] = useState<JiraCallbackFailure | undefined>();\n const [selectedCloudId, setSelectedCloudId] = useState<string | undefined>();\n const [completedWorkspace, setCompletedWorkspace] = useState<{\n slug?: string | undefined;\n }>();\n const disposedRef = useRef(false);\n const callbackKeyRef = useRef(callbackKey);\n callbackKeyRef.current = callbackKey;\n const [callbackStateKey, setCallbackStateKey] = useState(callbackKey);\n\n useEffect(() => {\n disposedRef.current = false;\n return () => {\n disposedRef.current = true;\n };\n }, []);\n\n const completeConnection = useCallback(\n async (\n connection: IntegrationConnection,\n callbackKey: string,\n isActive: () => boolean = () => true,\n ) => {\n if (disposedRef.current || !isActive()) return;\n const completedConnection = jiraCompletedConnections.get(callbackKey);\n if (completedConnection) {\n const workspaceSlug = await resolveWorkspaceSlug({\n workspaceId: completedConnection.workspaceId,\n fallbackWorkspaces: workspaces,\n queryClient,\n });\n if (!disposedRef.current && isActive())\n setCompletedWorkspace(workspaceSlug ? {slug: workspaceSlug} : {});\n return;\n }\n rememberCompletedConnection(callbackKey, connection);\n try {\n clearJiraInstallWorkspace(sessionStorageOrUndefined());\n } catch {\n // The successful API response remains the source of truth for navigation.\n }\n if (disposedRef.current || !isActive()) return;\n if (!jiraToastedCallbacks.has(callbackKey)) {\n rememberCallbackKey(jiraToastedCallbacks, callbackKey);\n toast.success('Jira installed.');\n }\n let workspaceSlug: string | undefined;\n try {\n workspaceSlug = await resolveWorkspaceSlug({\n workspaceId: connection.workspaceId,\n fallbackWorkspaces: workspaces,\n queryClient,\n });\n if (disposedRef.current || !isActive()) return;\n if (!workspaceSlug) {\n setCompletedWorkspace({});\n return;\n }\n setCompletedWorkspace({slug: workspaceSlug});\n await navigate({\n to: '/w/$workspaceSlug/settings/integrations',\n params: {workspaceSlug},\n replace: true,\n });\n } catch {\n if (!disposedRef.current && isActive()) setCompletedWorkspace({slug: workspaceSlug});\n }\n },\n [navigate, queryClient, workspaces],\n );\n\n useEffect(() => {\n if (callbackKey === undefined) return;\n setCallbackStateKey(callbackKey);\n setSites(undefined);\n setSelectedCloudId(undefined);\n setFailure(undefined);\n setCompletedWorkspace(undefined);\n }, [callbackKey]);\n\n useEffect(() => {\n if (!params || !callbackKey || isLoading) return;\n\n let active = true;\n\n const completedConnection = jiraCompletedConnections.get(callbackKey);\n if (completedConnection) {\n void completeConnection(completedConnection, callbackKey, () => active);\n return () => {\n active = false;\n };\n }\n\n const request = jiraCallbackRequests.run(callbackKey, async () => {\n return await completeIntegrationCallbackResult({\n input: params,\n refreshAuth,\n complete: async (query, token) => await completeJiraCallback({query, token}),\n getConnection: (result) => ('sites' in result ? undefined : result),\n });\n });\n\n request.then(\n async (result) => {\n if (disposedRef.current || !active) return;\n if ('sites' in result) {\n setFailure(undefined);\n setSites(result.sites);\n return;\n }\n await completeConnection(result, callbackKey, () => active);\n },\n (error: unknown) => {\n if (!disposedRef.current && active) setFailure(classifyJiraCallbackError(error));\n },\n );\n\n return () => {\n active = false;\n };\n }, [\n callbackKey,\n completeIntegrationCallbackResult,\n completeJiraCallback,\n completeConnection,\n isLoading,\n params,\n refreshAuth,\n ]);\n\n function selectSite(site: JiraSite) {\n if (!params || !callbackKey || selectedCloudId) return;\n setSelectedCloudId(site.cloudId);\n setFailure(undefined);\n const selectionKey = callbackKey;\n const request = jiraSiteSelectionRequests.run(params.state, async () => {\n return await completeIntegrationCallback({\n input: {cloud_id: site.cloudId, state: params.state},\n refreshAuth,\n complete: async (body, token) => await completeJiraSiteSelection({body, token}),\n });\n });\n request.then(\n async (connection) => {\n if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;\n jiraCallbackRequests.clear(selectionKey);\n await completeConnection(\n connection,\n selectionKey,\n () => callbackKeyRef.current === selectionKey,\n );\n },\n (error: unknown) => {\n if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;\n setSelectedCloudId(undefined);\n setFailure(classifyJiraCallbackError(error));\n },\n );\n }\n\n if (!params) {\n return (\n <JiraCallbackFailurePage\n failure={classifyJiraCallbackError(new Error('invalid callback'))}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n />\n );\n }\n\n if (isLoading) return <FullPageLoader aria-label=\"Connecting Jira\" />;\n\n if (callbackStateKey === callbackKey && completedWorkspace)\n return (\n <CallbackStatusShell\n title=\"Jira connected\"\n status=\"success\"\n message={\n completedWorkspace.slug\n ? 'Jira is connected. Continue in integrations settings.'\n : 'Jira is connected. Return to Shipfox to continue.'\n }\n workspaceSlug={completedWorkspace.slug}\n installPath=\"/w/$workspaceSlug/integrations/jira\"\n />\n );\n\n if (callbackStateKey === callbackKey && sites)\n return (\n <JiraSiteSelectionPage\n sites={sites}\n selectedCloudId={selectedCloudId}\n failure={failure}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n onSelect={selectSite}\n />\n );\n\n if (callbackStateKey === callbackKey && failure)\n return (\n <JiraCallbackFailurePage\n failure={failure}\n workspaceSlug={workspaces.find(({id}) => id === workspaceId)?.slug}\n />\n );\n\n return <FullPageLoader aria-label=\"Connecting Jira\" />;\n}\n\nfunction rememberCompletedConnection(key: string, connection: IntegrationConnection): void {\n jiraCompletedConnections.delete(key);\n jiraCompletedConnections.set(key, connection);\n while (jiraCompletedConnections.size > JIRA_CALLBACK_CACHE_SIZE) {\n const oldestKey = jiraCompletedConnections.keys().next().value;\n if (oldestKey === undefined) return;\n jiraCompletedConnections.delete(oldestKey);\n }\n}\n\nfunction JiraCallbackFailurePage({\n failure,\n workspaceSlug,\n}: {\n failure: JiraCallbackFailure;\n workspaceSlug: string | undefined;\n}) {\n return (\n <CallbackStatusShell\n title={failure.title}\n message={failure.message}\n startOver={failure.startOver}\n switchAccount={failure.signIn}\n workspaceSlug={workspaceSlug}\n installPath=\"/w/$workspaceSlug/integrations/jira\"\n />\n );\n}\n\nfunction JiraSiteSelectionPage({\n sites,\n selectedCloudId,\n failure,\n workspaceSlug,\n onSelect,\n}: {\n sites: JiraSite[];\n selectedCloudId: string | undefined;\n failure: JiraCallbackFailure | undefined;\n workspaceSlug: string | undefined;\n onSelect: (site: JiraSite) => void;\n}) {\n return (\n <main className=\"flex min-h-screen bg-background-subtle-base px-row py-frame\">\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col justify-center gap-section\">\n <header className=\"flex flex-col gap-inline\">\n <Header variant=\"h2\">Choose a Jira site</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Select the Jira site to connect to this Shipfox workspace.\n </Text>\n </header>\n\n {failure ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{failure.message}</Text>\n </Callout>\n ) : null}\n\n <section className=\"flex flex-col gap-inline\" aria-label=\"Choose a Jira site\">\n {sites.map((site) => (\n <Card key={site.cloudId} className=\"p-panel-compact\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"min-w-0\">\n <Text size=\"md\" bold className=\"truncate\">\n {site.name}\n </Text>\n <Text size=\"sm\" className=\"truncate text-foreground-neutral-muted\">\n {site.url}\n </Text>\n </div>\n <Button\n variant=\"secondary\"\n disabled={selectedCloudId !== undefined}\n isLoading={selectedCloudId === site.cloudId}\n onClick={() => onSelect(site)}\n >\n Connect\n </Button>\n </div>\n </Card>\n ))}\n </section>\n\n <div className=\"flex flex-col gap-inline sm:flex-row sm:items-center\">\n {failure?.startOver && workspaceSlug ? (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/integrations/jira\" params={{workspaceSlug}}>\n Start over\n </Link>\n </ButtonLink>\n ) : null}\n {workspaceSlug ? (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/w/$workspaceSlug/settings/integrations\" params={{workspaceSlug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n ) : (\n <ButtonLink asChild variant=\"muted\" className=\"min-h-44 w-full sm:w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n )}\n </div>\n </div>\n </main>\n );\n}\n"],"names":["useAuthState","useRefreshAuth","useRouteSearch","sessionStorageOrUndefined","Button","ButtonLink","Callout","Card","FullPageLoader","toast","Header","Text","useQueryClient","Link","useNavigate","useCallback","useEffect","useMemo","useRef","useState","useCompleteIntegrationCallback","useCompleteIntegrationCallbackResult","CallbackStatusShell","useCompleteJiraCallbackMutation","useCompleteJiraSiteSelectionMutation","clearJiraInstallWorkspace","parseJiraCallbackQuery","readJiraInstallWorkspace","serializeJiraCallbackQuery","JIRA_CALLBACK_CACHE_SIZE","jiraCallbackRequests","jiraCompletedConnections","jiraSiteSelectionRequests","jiraToastedCallbacks","classifyJiraCallbackError","rememberCallbackKey","resolveWorkspaceSlug","JiraCallbackPage","navigate","queryClient","refreshAuth","workspaces","isLoading","completeIntegrationCallback","completeIntegrationCallbackResult","mutateAsync","completeJiraCallback","completeJiraSiteSelection","params","callbackKey","undefined","workspaceId","sites","setSites","failure","setFailure","selectedCloudId","setSelectedCloudId","completedWorkspace","setCompletedWorkspace","disposedRef","callbackKeyRef","current","callbackStateKey","setCallbackStateKey","completeConnection","connection","isActive","completedConnection","get","workspaceSlug","fallbackWorkspaces","slug","rememberCompletedConnection","has","success","to","replace","active","request","run","input","complete","query","token","getConnection","result","then","error","selectSite","site","cloudId","selectionKey","state","cloud_id","body","clear","JiraCallbackFailurePage","Error","find","id","aria-label","title","status","message","installPath","JiraSiteSelectionPage","onSelect","key","delete","set","size","oldestKey","keys","next","value","startOver","switchAccount","signIn","main","className","div","header","variant","role","type","section","map","bold","name","url","disabled","onClick","asChild"],"mappings":"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"}
|