@shipfox/client-integrations 4.0.0 → 6.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 +86 -0
- package/dist/application/complete-integration-callback.d.ts +13 -0
- package/dist/application/complete-integration-callback.d.ts.map +1 -0
- package/dist/application/complete-integration-callback.js +26 -0
- package/dist/application/complete-integration-callback.js.map +1 -0
- package/dist/components/connection-picker.d.ts +2 -2
- package/dist/components/connection-picker.d.ts.map +1 -1
- package/dist/components/connection-picker.js +4 -4
- package/dist/components/connection-picker.js.map +1 -1
- package/dist/components/installed-integrations-section.d.ts +8 -6
- package/dist/components/installed-integrations-section.d.ts.map +1 -1
- package/dist/components/installed-integrations-section.js +22 -15
- package/dist/components/installed-integrations-section.js.map +1 -1
- package/dist/components/integration-gallery-for-workspace.d.ts +2 -2
- package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -1
- package/dist/components/integration-gallery-for-workspace.js +14 -7
- package/dist/components/integration-gallery-for-workspace.js.map +1 -1
- package/dist/components/integration-gallery.d.ts +2 -2
- package/dist/components/integration-gallery.d.ts.map +1 -1
- package/dist/components/integration-gallery.js.map +1 -1
- package/dist/components/integration-usage-events.d.ts +1 -3
- package/dist/components/integration-usage-events.d.ts.map +1 -1
- package/dist/components/integration-usage-events.js +1 -125
- package/dist/components/integration-usage-events.js.map +1 -1
- package/dist/components/integration-usage-modal.d.ts +2 -2
- package/dist/components/integration-usage-modal.d.ts.map +1 -1
- package/dist/components/integration-usage-modal.js +2 -2
- package/dist/components/integration-usage-modal.js.map +1 -1
- package/dist/components/provider-grid.d.ts +7 -4
- package/dist/components/provider-grid.d.ts.map +1 -1
- package/dist/components/provider-grid.js +15 -10
- package/dist/components/provider-grid.js.map +1 -1
- package/dist/components/redirect-install-page.d.ts +2 -3
- package/dist/components/redirect-install-page.d.ts.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.d.ts +2 -2
- package/dist/components/repository-picker.d.ts.map +1 -1
- package/dist/components/repository-picker.js +4 -4
- package/dist/components/repository-picker.js.map +1 -1
- package/dist/components/webhook/webhook-create-modal.d.ts +2 -2
- package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -1
- package/dist/components/webhook/webhook-create-modal.js +8 -8
- package/dist/components/webhook/webhook-create-modal.js.map +1 -1
- package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -1
- package/dist/components/webhook/webhook-modals.stories.js +7 -7
- package/dist/components/webhook/webhook-modals.stories.js.map +1 -1
- package/dist/components/webhook/webhook-usage-details.d.ts.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/connection-status-badge.d.ts +2 -2
- package/dist/connection-status-badge.d.ts.map +1 -1
- package/dist/connection-status-badge.js +11 -15
- package/dist/connection-status-badge.js.map +1 -1
- package/dist/core/models.d.ts +66 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +156 -0
- package/dist/core/models.js.map +1 -0
- package/dist/feature.d.ts +18 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +16 -1
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integration-mapper.d.ts +11 -0
- package/dist/hooks/api/integration-mapper.d.ts.map +1 -0
- package/dist/hooks/api/integration-mapper.js +54 -0
- package/dist/hooks/api/integration-mapper.js.map +1 -0
- package/dist/hooks/api/integrations.d.ts +47 -237
- package/dist/hooks/api/integrations.d.ts.map +1 -1
- package/dist/hooks/api/integrations.js +92 -42
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/hooks/api/webhook-connections.d.ts +12 -64
- package/dist/hooks/api/webhook-connections.d.ts.map +1 -1
- package/dist/hooks/api/webhook-connections.js +17 -9
- package/dist/hooks/api/webhook-connections.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/linear-callback.d.ts +2 -1
- package/dist/linear-callback.d.ts.map +1 -1
- package/dist/linear-callback.js +14 -15
- package/dist/linear-callback.js.map +1 -1
- package/dist/pages/gitea-install-page.d.ts.map +1 -1
- package/dist/pages/gitea-install-page.js +1 -10
- package/dist/pages/gitea-install-page.js.map +1 -1
- package/dist/pages/linear-callback-page.d.ts.map +1 -1
- package/dist/pages/linear-callback-page.js +19 -32
- package/dist/pages/linear-callback-page.js.map +1 -1
- package/dist/pages/linear-install-page.d.ts.map +1 -1
- package/dist/pages/linear-install-page.js +2 -1
- package/dist/pages/linear-install-page.js.map +1 -1
- package/dist/pages/sentry-callback-page.d.ts.map +1 -1
- package/dist/pages/sentry-callback-page.js +22 -25
- package/dist/pages/sentry-callback-page.js.map +1 -1
- package/dist/pages/sentry-install-page.d.ts.map +1 -1
- package/dist/pages/sentry-install-page.js +2 -1
- package/dist/pages/sentry-install-page.js.map +1 -1
- package/dist/pages/slack-callback-page.d.ts.map +1 -1
- package/dist/pages/slack-callback-page.js +21 -28
- package/dist/pages/slack-callback-page.js.map +1 -1
- package/dist/pages/slack-install-page.d.ts.map +1 -1
- package/dist/pages/slack-install-page.js +2 -1
- package/dist/pages/slack-install-page.js.map +1 -1
- package/dist/pages/source-control-onboarding-page.d.ts.map +1 -1
- package/dist/pages/source-control-onboarding-page.js +5 -1
- package/dist/pages/source-control-onboarding-page.js.map +1 -1
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +7 -6
- package/dist/provider-catalog.js.map +1 -1
- package/dist/routes/github-callback.d.ts +16 -0
- package/dist/routes/github-callback.d.ts.map +1 -1
- package/dist/routes/github-callback.js +43 -21
- package/dist/routes/github-callback.js.map +1 -1
- package/dist/routes/integrations-settings.d.ts +6 -0
- package/dist/routes/integrations-settings.d.ts.map +1 -0
- package/dist/routes/integrations-settings.js +7 -0
- package/dist/routes/integrations-settings.js.map +1 -0
- package/dist/routes/linear-callback.d.ts +2 -0
- package/dist/routes/linear-callback.d.ts.map +1 -1
- package/dist/routes/linear-callback.js +2 -0
- package/dist/routes/linear-callback.js.map +1 -1
- package/dist/routes/sentry-callback.d.ts +2 -0
- package/dist/routes/sentry-callback.d.ts.map +1 -1
- package/dist/routes/sentry-callback.js +2 -0
- package/dist/routes/sentry-callback.js.map +1 -1
- package/dist/routes/slack-callback.d.ts +2 -0
- package/dist/routes/slack-callback.d.ts.map +1 -1
- package/dist/routes/slack-callback.js +2 -0
- package/dist/routes/slack-callback.js.map +1 -1
- package/dist/sentry-callback.d.ts +2 -1
- package/dist/sentry-callback.d.ts.map +1 -1
- package/dist/sentry-callback.js +16 -19
- package/dist/sentry-callback.js.map +1 -1
- package/dist/slack-callback.d.ts +2 -1
- package/dist/slack-callback.d.ts.map +1 -1
- package/dist/slack-callback.js +14 -15
- package/dist/slack-callback.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/application/complete-integration-callback.test.ts +41 -0
- package/src/application/complete-integration-callback.ts +39 -0
- package/src/components/connection-picker.tsx +7 -7
- package/src/components/installed-integrations-section.tsx +29 -24
- package/src/components/integration-gallery-for-workspace.tsx +23 -16
- package/src/components/integration-gallery.tsx +3 -3
- package/src/components/integration-usage-events.ts +1 -116
- package/src/components/integration-usage-modal.tsx +4 -4
- package/src/components/provider-grid.tsx +30 -18
- package/src/components/redirect-install-page.test.tsx +2 -6
- package/src/components/redirect-install-page.tsx +3 -2
- package/src/components/repository-picker.tsx +6 -6
- package/src/components/webhook/webhook-create-modal.tsx +12 -11
- package/src/components/webhook/webhook-modals.stories.tsx +10 -10
- package/src/components/webhook/webhook-usage-details.tsx +2 -4
- package/src/connection-status-badge.tsx +8 -14
- package/src/core/models.ts +200 -0
- package/src/feature.ts +17 -1
- package/src/hooks/api/integration-mapper.ts +64 -0
- package/src/hooks/api/integrations.test.ts +44 -22
- package/src/hooks/api/integrations.ts +258 -89
- package/src/hooks/api/webhook-connections.ts +58 -17
- package/src/index.ts +2 -1
- package/src/linear-callback.ts +21 -16
- package/src/pages/gitea-install-page.tsx +1 -10
- package/src/pages/linear-callback-page.test.tsx +1 -1
- package/src/pages/linear-callback-page.tsx +18 -29
- package/src/pages/linear-install-page.tsx +2 -1
- package/src/pages/sentry-callback-page.test.tsx +6 -2
- package/src/pages/sentry-callback-page.tsx +28 -25
- package/src/pages/sentry-install-page.tsx +2 -1
- package/src/pages/slack-callback-page.tsx +20 -25
- package/src/pages/slack-install-page.tsx +2 -1
- package/src/pages/source-control-onboarding-page.tsx +5 -1
- package/src/provider-catalog.ts +7 -6
- package/src/routes/github-callback.test.ts +35 -1
- package/src/routes/github-callback.tsx +47 -21
- package/src/routes/integrations-settings.tsx +4 -0
- package/src/routes/linear-callback.tsx +5 -1
- package/src/routes/sentry-callback.tsx +5 -1
- package/src/routes/slack-callback.tsx +6 -1
- package/src/sentry-callback.test.ts +2 -1
- package/src/sentry-callback.ts +28 -20
- package/src/slack-callback.ts +21 -16
- package/test/render.tsx +1 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
- package/dist/integration-icon.d.ts +0 -17
- package/dist/integration-icon.d.ts.map +0 -1
- package/dist/integration-icon.js +0 -22
- package/dist/integration-icon.js.map +0 -1
- package/src/integration-icon.react.test.tsx +0 -40
- package/src/integration-icon.test.ts +0 -19
- package/src/integration-icon.tsx +0 -26
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './components/connection-picker.js';\nexport * from './components/integration-gallery.js';\nexport * from './components/redirect-install-page.js';\nexport * from './components/repository-picker.js';\nexport * from './connection-status-badge.js';\nexport * from './hooks/api/integrations.js';\nexport * from './
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from '@shipfox/integration-icons';\nexport * from '#core/models.js';\nexport * from './components/connection-picker.js';\nexport * from './components/integration-gallery.js';\nexport * from './components/redirect-install-page.js';\nexport * from './components/repository-picker.js';\nexport * from './connection-status-badge.js';\nexport * from './hooks/api/integrations.js';\nexport * from './pages/gitea-install-page.js';\nexport * from './pages/github-install-page.js';\nexport * from './pages/linear-callback-page.js';\nexport * from './pages/linear-install-page.js';\nexport * from './pages/sentry-callback-page.js';\nexport * from './pages/sentry-install-page.js';\nexport * from './pages/slack-callback-page.js';\nexport * from './pages/slack-install-page.js';\nexport * from './pages/source-control-onboarding-page.js';\nexport * from './provider-catalog.js';\nexport * from './sentry-callback.js';\nexport * from './slack-callback.js';\n"],"names":[],"mappings":"AAAA,cAAc,6BAA6B;AAC3C,cAAc,kBAAkB;AAChC,cAAc,oCAAoC;AAClD,cAAc,sCAAsC;AACpD,cAAc,wCAAwC;AACtD,cAAc,oCAAoC;AAClD,cAAc,+BAA+B;AAC7C,cAAc,8BAA8B;AAC5C,cAAc,gCAAgC;AAC9C,cAAc,iCAAiC;AAC/C,cAAc,kCAAkC;AAChD,cAAc,iCAAiC;AAC/C,cAAc,kCAAkC;AAChD,cAAc,iCAAiC;AAC/C,cAAc,iCAAiC;AAC/C,cAAc,gCAAgC;AAC9C,cAAc,4CAA4C;AAC1D,cAAc,wBAAwB;AACtC,cAAc,uBAAuB;AACrC,cAAc,sBAAsB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LinearCallbackQueryDto } from '@shipfox/api-integration-linear-dto';
|
|
2
|
+
import { type BrowserStorage } from '@shipfox/client-ui';
|
|
2
3
|
export declare const LINEAR_INSTALL_WORKSPACE_KEY = "shipfox.linear-install.workspace-id";
|
|
3
|
-
type WorkspaceStorage =
|
|
4
|
+
type WorkspaceStorage = BrowserStorage | undefined;
|
|
4
5
|
export declare function saveLinearInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void;
|
|
5
6
|
export declare function readLinearInstallWorkspace(storage: WorkspaceStorage): string | undefined;
|
|
6
7
|
export declare function clearLinearInstallWorkspace(storage: WorkspaceStorage): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../src/linear-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../src/linear-callback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,qCAAqC,CAAC;AAEhF,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAUnD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,sBAAsB,GAAG,SAAS,CAWpC;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAmEjF"}
|
package/dist/linear-callback.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { ApiError } from '@shipfox/client-api';
|
|
2
|
+
import { createTypedBrowserStorage } from '@shipfox/client-ui';
|
|
2
3
|
export const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id';
|
|
4
|
+
const linearInstallWorkspaceStorageKey = {
|
|
5
|
+
key: LINEAR_INSTALL_WORKSPACE_KEY,
|
|
6
|
+
lifetime: 'session',
|
|
7
|
+
principalScope: 'workspace',
|
|
8
|
+
serialize: (workspaceId)=>workspaceId,
|
|
9
|
+
parse: (value)=>value || undefined
|
|
10
|
+
};
|
|
3
11
|
export function saveLinearInstallWorkspace(storage, workspaceId) {
|
|
4
|
-
|
|
5
|
-
storage.setItem(LINEAR_INSTALL_WORKSPACE_KEY, workspaceId);
|
|
6
|
-
} catch {
|
|
7
|
-
// Storage only improves recovery navigation; callback input always comes from the URL.
|
|
8
|
-
}
|
|
12
|
+
installWorkspaceStorage(storage).write(workspaceId);
|
|
9
13
|
}
|
|
10
14
|
export function readLinearInstallWorkspace(storage) {
|
|
11
|
-
|
|
12
|
-
return storage.getItem(LINEAR_INSTALL_WORKSPACE_KEY) ?? undefined;
|
|
13
|
-
} catch {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
15
|
+
return installWorkspaceStorage(storage).read();
|
|
16
16
|
}
|
|
17
17
|
export function clearLinearInstallWorkspace(storage) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
18
|
+
installWorkspaceStorage(storage).remove();
|
|
19
|
+
}
|
|
20
|
+
function installWorkspaceStorage(storage) {
|
|
21
|
+
return createTypedBrowserStorage(()=>storage, linearInstallWorkspaceStorageKey);
|
|
23
22
|
}
|
|
24
23
|
export function parseLinearCallbackQuery(search) {
|
|
25
24
|
const state = stringParam(search.state);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/linear-callback.ts"],"sourcesContent":["import type {LinearCallbackQueryDto} from '@shipfox/api-integration-linear-dto';\nimport {ApiError} from '@shipfox/client-api';\n\nexport const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id';\n\ntype WorkspaceStorage =
|
|
1
|
+
{"version":3,"sources":["../src/linear-callback.ts"],"sourcesContent":["import type {LinearCallbackQueryDto} from '@shipfox/api-integration-linear-dto';\nimport {ApiError} from '@shipfox/client-api';\nimport {\n type BrowserStorage,\n type BrowserStorageKey,\n createTypedBrowserStorage,\n} from '@shipfox/client-ui';\n\nexport const LINEAR_INSTALL_WORKSPACE_KEY = 'shipfox.linear-install.workspace-id';\n\ntype WorkspaceStorage = BrowserStorage | undefined;\n\nconst linearInstallWorkspaceStorageKey = {\n key: LINEAR_INSTALL_WORKSPACE_KEY,\n lifetime: 'session',\n principalScope: 'workspace',\n serialize: (workspaceId: string) => workspaceId,\n parse: (value: string) => value || undefined,\n} satisfies BrowserStorageKey<string>;\n\nexport function saveLinearInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {\n installWorkspaceStorage(storage).write(workspaceId);\n}\n\nexport function readLinearInstallWorkspace(storage: WorkspaceStorage): string | undefined {\n return installWorkspaceStorage(storage).read();\n}\n\nexport function clearLinearInstallWorkspace(storage: WorkspaceStorage): void {\n installWorkspaceStorage(storage).remove();\n}\n\nfunction installWorkspaceStorage(storage: WorkspaceStorage) {\n return createTypedBrowserStorage(() => storage, linearInstallWorkspaceStorageKey);\n}\n\nexport function parseLinearCallbackQuery(\n search: Record<string, unknown>,\n): LinearCallbackQueryDto | undefined {\n const state = stringParam(search.state);\n if (!state) return undefined;\n\n const code = stringParam(search.code);\n if (code) return {code, state};\n\n const error = stringParam(search.error);\n if (!error) return undefined;\n const errorDescription = stringParam(search.error_description);\n return errorDescription ? {error, error_description: errorDescription, state} : {error, state};\n}\n\nexport function serializeLinearCallbackQuery(query: LinearCallbackQueryDto): string {\n const params = new URLSearchParams();\n if ('code' in query) params.set('code', query.code);\n else {\n params.set('error', query.error);\n if (query.error_description) params.set('error_description', query.error_description);\n }\n params.set('state', query.state);\n return params.toString();\n}\n\nexport type LinearCallbackFailure = {\n title?: string;\n message: string;\n startOver: boolean;\n signIn: boolean;\n};\n\nexport function classifyLinearCallbackError(error: unknown): LinearCallbackFailure {\n if (error instanceof ApiError) {\n if (error.code === 'invalid-linear-install-state') {\n return {\n title: 'Linear install link expired',\n message: 'Linear install link expired. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.code === 'linear-install-state-actor-mismatch' || error.code === 'unauthorized') {\n return {\n title: 'Different Shipfox account',\n message: 'Different Shipfox account. Sign in with the account that started this install.',\n startOver: true,\n signIn: true,\n };\n }\n if (\n error.code === 'linear-installation-already-linked' ||\n error.code === 'linear-connection-already-linked'\n ) {\n return {\n title: 'Linear already linked',\n message: 'This Linear organization is already linked to another workspace.',\n startOver: false,\n signIn: false,\n };\n }\n if (\n error.code === 'linear-authorization-scope-mismatch' ||\n error.code === 'linear-oauth-callback-error'\n ) {\n return {\n title: 'Linear permissions needed',\n message:\n 'Linear did not authorize the permissions Shipfox needs. Review the Linear consent and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (error.status === 0 || error.code === 'network-error') {\n return {\n message: 'Could not reach Shipfox. Check your connection and start again.',\n startOver: true,\n signIn: false,\n };\n }\n if (\n error.status >= 500 ||\n error.status === 429 ||\n error.code === 'provider-unavailable' ||\n error.code === 'timeout' ||\n error.code === 'rate-limited'\n ) {\n return {\n message: 'Linear is temporarily unavailable. Start a new install when it is available.',\n startOver: true,\n signIn: false,\n };\n }\n }\n return {\n message: 'Could not complete the Linear install. Start again from workspace settings.',\n startOver: true,\n signIn: false,\n };\n}\n\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n"],"names":["ApiError","createTypedBrowserStorage","LINEAR_INSTALL_WORKSPACE_KEY","linearInstallWorkspaceStorageKey","key","lifetime","principalScope","serialize","workspaceId","parse","value","undefined","saveLinearInstallWorkspace","storage","installWorkspaceStorage","write","readLinearInstallWorkspace","read","clearLinearInstallWorkspace","remove","parseLinearCallbackQuery","search","state","stringParam","code","error","errorDescription","error_description","serializeLinearCallbackQuery","query","params","URLSearchParams","set","toString","classifyLinearCallbackError","title","message","startOver","signIn","status","length"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitea-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/gitea-install-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gitea-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/gitea-install-page.tsx"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,gCAsF/B"}
|
|
@@ -6,15 +6,13 @@ import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-fi
|
|
|
6
6
|
import { toast } from '@shipfox/react-ui/toast';
|
|
7
7
|
import { Header, Text } from '@shipfox/react-ui/typography';
|
|
8
8
|
import { useForm } from '@tanstack/react-form';
|
|
9
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
10
9
|
import { Link, useNavigate } from '@tanstack/react-router';
|
|
11
10
|
import { useState } from 'react';
|
|
12
|
-
import {
|
|
11
|
+
import { useCreateGiteaConnectionMutation } from '#hooks/api/integrations.js';
|
|
13
12
|
import { giteaConnectErrorToFormError } from './gitea-form-errors.js';
|
|
14
13
|
export function GiteaInstallPage() {
|
|
15
14
|
const workspace = useActiveWorkspace();
|
|
16
15
|
const connect = useCreateGiteaConnectionMutation();
|
|
17
|
-
const queryClient = useQueryClient();
|
|
18
16
|
const navigate = useNavigate();
|
|
19
17
|
const [formError, setFormError] = useState();
|
|
20
18
|
const form = useForm({
|
|
@@ -29,13 +27,6 @@ export function GiteaInstallPage() {
|
|
|
29
27
|
workspace_id: workspaceId,
|
|
30
28
|
org: value.org.trim()
|
|
31
29
|
});
|
|
32
|
-
await queryClient.invalidateQueries({
|
|
33
|
-
queryKey: integrationsQueryKeys.sourceConnections(workspaceId),
|
|
34
|
-
// The workspace home is the next active observer, not this page, so the
|
|
35
|
-
// default 'active' refetch would no-op and leave the cache stale until
|
|
36
|
-
// after the home redirects back here.
|
|
37
|
-
refetchType: 'all'
|
|
38
|
-
});
|
|
39
30
|
toast.success('Gitea organization installed.');
|
|
40
31
|
await navigate({
|
|
41
32
|
to: '/workspaces/$wid',
|
|
@@ -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 {
|
|
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: '/workspaces/$wid', params: {wid: workspaceId}});\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-20\">\n <header className=\"flex flex-col gap-8\">\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-18\"\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-12\">\n <Button type=\"submit\" isLoading={connect.isPending}>\n Install\n </Button>\n <ButtonLink asChild variant=\"muted\">\n <Link to=\"/workspaces/$wid/integrations\" params={{wid: workspace.id}}>\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","wid","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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/linear-callback-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"linear-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/linear-callback-page.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,kBAAkB,gCA0EjC"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRefreshAuth } from '@shipfox/client-auth';
|
|
3
|
-
import {
|
|
3
|
+
import { useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
|
+
import { createSingleFlight, sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
4
5
|
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
5
6
|
import { toast } from '@shipfox/react-ui/toast';
|
|
6
|
-
import {
|
|
7
|
-
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
7
|
+
import { useNavigate } from '@tanstack/react-router';
|
|
8
8
|
import { useEffect, useMemo, useState } from 'react';
|
|
9
|
+
import { useCompleteIntegrationCallback } from '#application/complete-integration-callback.js';
|
|
9
10
|
import { CallbackStatusShell } from '#components/callback-status-shell.js';
|
|
10
|
-
import {
|
|
11
|
+
import { useCompleteLinearCallbackMutation } from '#hooks/api/integrations.js';
|
|
11
12
|
import { classifyLinearCallbackError, clearLinearInstallWorkspace, parseLinearCallbackQuery, readLinearInstallWorkspace, serializeLinearCallbackQuery } from '#linear-callback.js';
|
|
12
13
|
// Retain only recent completions: this bounds long-lived callback pages while
|
|
13
14
|
// still covering StrictMode and immediate Back/Forward remounts.
|
|
@@ -18,46 +19,32 @@ const callbackRequests = createSingleFlight({
|
|
|
18
19
|
// effect re-runs against the cached request as the mutation identity churns.
|
|
19
20
|
const toastedCallbacks = new Set();
|
|
20
21
|
export function LinearCallbackPage() {
|
|
21
|
-
const search = useSearch({
|
|
22
|
-
strict: false
|
|
23
|
-
});
|
|
24
22
|
const navigate = useNavigate();
|
|
25
23
|
const refreshAuth = useRefreshAuth();
|
|
26
|
-
const
|
|
24
|
+
const completeIntegrationCallback = useCompleteIntegrationCallback();
|
|
27
25
|
const { mutateAsync: completeLinearCallback } = useCompleteLinearCallbackMutation();
|
|
28
|
-
const params =
|
|
29
|
-
|
|
30
|
-
]);
|
|
31
|
-
const workspaceId = useMemo(()=>{
|
|
32
|
-
try {
|
|
33
|
-
return readLinearInstallWorkspace(window.sessionStorage);
|
|
34
|
-
} catch {
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
}, []);
|
|
26
|
+
const params = useRouteSearch(parseLinearCallbackQuery);
|
|
27
|
+
const workspaceId = useMemo(()=>readLinearInstallWorkspace(sessionStorageOrUndefined()), []);
|
|
38
28
|
const [failure, setFailure] = useState();
|
|
39
29
|
useEffect(()=>{
|
|
40
30
|
if (!params) return;
|
|
41
31
|
let disposed = false;
|
|
42
32
|
const key = serializeLinearCallbackQuery(params);
|
|
43
|
-
const request = callbackRequests.run(key, async ()=>await
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
const request = callbackRequests.run(key, async ()=>await completeIntegrationCallback({
|
|
34
|
+
input: params,
|
|
35
|
+
refreshAuth,
|
|
36
|
+
complete: async (query, token)=>await completeLinearCallback({
|
|
37
|
+
query,
|
|
38
|
+
token
|
|
39
|
+
})
|
|
40
|
+
}));
|
|
47
41
|
request.then(async (connection)=>{
|
|
48
42
|
if (disposed) return;
|
|
49
43
|
try {
|
|
50
|
-
clearLinearInstallWorkspace(
|
|
44
|
+
clearLinearInstallWorkspace(sessionStorageOrUndefined());
|
|
51
45
|
} catch {
|
|
52
46
|
// The successful API response remains the source of truth for navigation.
|
|
53
47
|
}
|
|
54
|
-
try {
|
|
55
|
-
await queryClient.invalidateQueries({
|
|
56
|
-
queryKey: integrationsQueryKeys.connectionsByWorkspace(connection.workspace_id)
|
|
57
|
-
});
|
|
58
|
-
} catch {
|
|
59
|
-
// Cache refresh is best effort: the successful callback is already committed server-side.
|
|
60
|
-
}
|
|
61
48
|
if (disposed) return;
|
|
62
49
|
if (!toastedCallbacks.has(key)) {
|
|
63
50
|
toastedCallbacks.add(key);
|
|
@@ -67,7 +54,7 @@ export function LinearCallbackPage() {
|
|
|
67
54
|
await navigate({
|
|
68
55
|
to: '/workspaces/$wid/settings/integrations',
|
|
69
56
|
params: {
|
|
70
|
-
wid: connection.
|
|
57
|
+
wid: connection.workspaceId
|
|
71
58
|
},
|
|
72
59
|
replace: true
|
|
73
60
|
});
|
|
@@ -82,10 +69,10 @@ export function LinearCallbackPage() {
|
|
|
82
69
|
disposed = true;
|
|
83
70
|
};
|
|
84
71
|
}, [
|
|
72
|
+
completeIntegrationCallback,
|
|
85
73
|
completeLinearCallback,
|
|
86
74
|
navigate,
|
|
87
75
|
params,
|
|
88
|
-
queryClient,
|
|
89
76
|
refreshAuth
|
|
90
77
|
]);
|
|
91
78
|
if (!params) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/linear-callback-page.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../src/pages/linear-callback-page.tsx"],"sourcesContent":["import {useRefreshAuth} from '@shipfox/client-auth';\nimport {useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {createSingleFlight, sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useMemo, useState} from 'react';\nimport {useCompleteIntegrationCallback} from '#application/complete-integration-callback.js';\nimport {CallbackStatusShell} from '#components/callback-status-shell.js';\nimport type {IntegrationConnection} from '#core/models.js';\nimport {useCompleteLinearCallbackMutation} from '#hooks/api/integrations.js';\nimport {\n classifyLinearCallbackError,\n clearLinearInstallWorkspace,\n type LinearCallbackFailure,\n parseLinearCallbackQuery,\n readLinearInstallWorkspace,\n serializeLinearCallbackQuery,\n} from '#linear-callback.js';\n\n// Retain only recent completions: this bounds long-lived callback pages while\n// still covering StrictMode and immediate Back/Forward remounts.\nconst callbackRequests = createSingleFlight<string, IntegrationConnection>({\n maxTerminalResults: 32,\n});\n// Keeps the success toast firing once per distinct callback even though the\n// effect re-runs against the cached request as the mutation identity churns.\nconst toastedCallbacks = new Set<string>();\n\nexport function LinearCallbackPage() {\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n const {mutateAsync: completeLinearCallback} = useCompleteLinearCallbackMutation();\n const params = useRouteSearch(parseLinearCallbackQuery);\n const workspaceId = useMemo(() => readLinearInstallWorkspace(sessionStorageOrUndefined()), []);\n const [failure, setFailure] = useState<LinearCallbackFailure | undefined>();\n useEffect(() => {\n if (!params) return;\n\n let disposed = false;\n const key = serializeLinearCallbackQuery(params);\n const request = callbackRequests.run(\n key,\n async () =>\n await completeIntegrationCallback({\n input: params,\n refreshAuth,\n complete: async (query, token) => await completeLinearCallback({query, token}),\n }),\n );\n\n request.then(\n async (connection) => {\n if (disposed) return;\n try {\n clearLinearInstallWorkspace(sessionStorageOrUndefined());\n } catch {\n // The successful API response remains the source of truth for navigation.\n }\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('Linear installed.');\n }\n try {\n await navigate({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: connection.workspaceId},\n replace: true,\n });\n } catch {\n // Keep the completed callback page visible if client navigation is interrupted.\n }\n },\n (error: unknown) => {\n if (disposed) return;\n setFailure(classifyLinearCallbackError(error));\n },\n );\n\n return () => {\n disposed = true;\n };\n }, [completeIntegrationCallback, completeLinearCallback, navigate, params, refreshAuth]);\n\n if (!params) {\n return (\n <LinearCallbackFailurePage\n failure={{\n title: 'Invalid Linear callback',\n message: 'Invalid Linear callback. Start the install again from workspace settings.',\n startOver: true,\n signIn: false,\n }}\n workspaceId={workspaceId}\n />\n );\n }\n\n if (!failure) return <FullPageLoader aria-label=\"Connecting Linear\" />;\n\n return <LinearCallbackFailurePage failure={failure} workspaceId={workspaceId} />;\n}\n\nfunction LinearCallbackFailurePage({\n failure,\n workspaceId,\n}: {\n failure: LinearCallbackFailure;\n workspaceId: string | undefined;\n}) {\n return (\n <CallbackStatusShell\n title={failure.title ?? 'Linear install could not be completed'}\n message={failure.message}\n startOver={failure.startOver}\n switchAccount={failure.signIn}\n workspaceId={workspaceId}\n installPath=\"/workspaces/$wid/integrations/linear\"\n />\n );\n}\n"],"names":["useRefreshAuth","useRouteSearch","createSingleFlight","sessionStorageOrUndefined","FullPageLoader","toast","useNavigate","useEffect","useMemo","useState","useCompleteIntegrationCallback","CallbackStatusShell","useCompleteLinearCallbackMutation","classifyLinearCallbackError","clearLinearInstallWorkspace","parseLinearCallbackQuery","readLinearInstallWorkspace","serializeLinearCallbackQuery","callbackRequests","maxTerminalResults","toastedCallbacks","Set","LinearCallbackPage","navigate","refreshAuth","completeIntegrationCallback","mutateAsync","completeLinearCallback","params","workspaceId","failure","setFailure","disposed","key","request","run","input","complete","query","token","then","connection","has","add","success","to","wid","replace","error","LinearCallbackFailurePage","title","message","startOver","signIn","aria-label","switchAccount","installPath"],"mappings":";AAAA,SAAQA,cAAc,QAAO,uBAAuB;AACpD,SAAQC,cAAc,QAAO,gCAAgC;AAC7D,SAAQC,kBAAkB,EAAEC,yBAAyB,QAAO,qBAAqB;AACjF,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AACnD,SAAQC,8BAA8B,QAAO,gDAAgD;AAC7F,SAAQC,mBAAmB,QAAO,uCAAuC;AAEzE,SAAQC,iCAAiC,QAAO,6BAA6B;AAC7E,SACEC,2BAA2B,EAC3BC,2BAA2B,EAE3BC,wBAAwB,EACxBC,0BAA0B,EAC1BC,4BAA4B,QACvB,sBAAsB;AAE7B,8EAA8E;AAC9E,iEAAiE;AACjE,MAAMC,mBAAmBhB,mBAAkD;IACzEiB,oBAAoB;AACtB;AACA,4EAA4E;AAC5E,6EAA6E;AAC7E,MAAMC,mBAAmB,IAAIC;AAE7B,OAAO,SAASC;IACd,MAAMC,WAAWjB;IACjB,MAAMkB,cAAcxB;IACpB,MAAMyB,8BAA8Bf;IACpC,MAAM,EAACgB,aAAaC,sBAAsB,EAAC,GAAGf;IAC9C,MAAMgB,SAAS3B,eAAec;IAC9B,MAAMc,cAAcrB,QAAQ,IAAMQ,2BAA2Bb,8BAA8B,EAAE;IAC7F,MAAM,CAAC2B,SAASC,WAAW,GAAGtB;IAC9BF,UAAU;QACR,IAAI,CAACqB,QAAQ;QAEb,IAAII,WAAW;QACf,MAAMC,MAAMhB,6BAA6BW;QACzC,MAAMM,UAAUhB,iBAAiBiB,GAAG,CAClCF,KACA,UACE,MAAMR,4BAA4B;gBAChCW,OAAOR;gBACPJ;gBACAa,UAAU,OAAOC,OAAOC,QAAU,MAAMZ,uBAAuB;wBAACW;wBAAOC;oBAAK;YAC9E;QAGJL,QAAQM,IAAI,CACV,OAAOC;YACL,IAAIT,UAAU;YACd,IAAI;gBACFlB,4BAA4BX;YAC9B,EAAE,OAAM;YACN,0EAA0E;YAC5E;YACA,IAAI6B,UAAU;YACd,IAAI,CAACZ,iBAAiBsB,GAAG,CAACT,MAAM;gBAC9Bb,iBAAiBuB,GAAG,CAACV;gBACrB5B,MAAMuC,OAAO,CAAC;YAChB;YACA,IAAI;gBACF,MAAMrB,SAAS;oBACbsB,IAAI;oBACJjB,QAAQ;wBAACkB,KAAKL,WAAWZ,WAAW;oBAAA;oBACpCkB,SAAS;gBACX;YACF,EAAE,OAAM;YACN,gFAAgF;YAClF;QACF,GACA,CAACC;YACC,IAAIhB,UAAU;YACdD,WAAWlB,4BAA4BmC;QACzC;QAGF,OAAO;YACLhB,WAAW;QACb;IACF,GAAG;QAACP;QAA6BE;QAAwBJ;QAAUK;QAAQJ;KAAY;IAEvF,IAAI,CAACI,QAAQ;QACX,qBACE,KAACqB;YACCnB,SAAS;gBACPoB,OAAO;gBACPC,SAAS;gBACTC,WAAW;gBACXC,QAAQ;YACV;YACAxB,aAAaA;;IAGnB;IAEA,IAAI,CAACC,SAAS,qBAAO,KAAC1B;QAAekD,cAAW;;IAEhD,qBAAO,KAACL;QAA0BnB,SAASA;QAASD,aAAaA;;AACnE;AAEA,SAASoB,0BAA0B,EACjCnB,OAAO,EACPD,WAAW,EAIZ;IACC,qBACE,KAAClB;QACCuC,OAAOpB,QAAQoB,KAAK,IAAI;QACxBC,SAASrB,QAAQqB,OAAO;QACxBC,WAAWtB,QAAQsB,SAAS;QAC5BG,eAAezB,QAAQuB,MAAM;QAC7BxB,aAAaA;QACb2B,aAAY;;AAGlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/linear-install-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"linear-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/linear-install-page.tsx"],"names":[],"mappings":"AAMA,wBAAgB,iBAAiB,gCAqBhC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
2
3
|
import { useCallback } from 'react';
|
|
3
4
|
import { RedirectInstallPage } from '#components/redirect-install-page.js';
|
|
4
5
|
import { useCreateLinearInstallMutation } from '#hooks/api/integrations.js';
|
|
@@ -14,7 +15,7 @@ export function LinearInstallPage() {
|
|
|
14
15
|
loadingLabel: "Connecting Linear",
|
|
15
16
|
beforeRedirect: (workspaceId)=>{
|
|
16
17
|
try {
|
|
17
|
-
saveLinearInstallWorkspace(
|
|
18
|
+
saveLinearInstallWorkspace(sessionStorageOrUndefined(), workspaceId);
|
|
18
19
|
} catch {
|
|
19
20
|
// Storage can be disabled before the helper gets a usable Storage object.
|
|
20
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/linear-install-page.tsx"],"sourcesContent":["import {useCallback} from 'react';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {useCreateLinearInstallMutation} from '#hooks/api/integrations.js';\nimport {saveLinearInstallWorkspace} from '#linear-callback.js';\n\nexport function LinearInstallPage() {\n const createInstall = useCreateLinearInstallMutation();\n const installRequest = useCallback(\n async (body: {workspace_id: string}) => await createInstall.mutateAsync(body),\n [createInstall],\n );\n\n return (\n <RedirectInstallPage\n installRequest={installRequest}\n errorFallbackMessage=\"Could not start Linear install.\"\n loadingLabel=\"Connecting Linear\"\n beforeRedirect={(workspaceId) => {\n try {\n saveLinearInstallWorkspace(
|
|
1
|
+
{"version":3,"sources":["../../src/pages/linear-install-page.tsx"],"sourcesContent":["import {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {useCallback} from 'react';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {useCreateLinearInstallMutation} from '#hooks/api/integrations.js';\nimport {saveLinearInstallWorkspace} from '#linear-callback.js';\n\nexport function LinearInstallPage() {\n const createInstall = useCreateLinearInstallMutation();\n const installRequest = useCallback(\n async (body: {workspace_id: string}) => await createInstall.mutateAsync(body),\n [createInstall],\n );\n\n return (\n <RedirectInstallPage\n installRequest={installRequest}\n errorFallbackMessage=\"Could not start Linear install.\"\n loadingLabel=\"Connecting Linear\"\n beforeRedirect={(workspaceId) => {\n try {\n saveLinearInstallWorkspace(sessionStorageOrUndefined(), workspaceId);\n } catch {\n // Storage can be disabled before the helper gets a usable Storage object.\n }\n }}\n />\n );\n}\n"],"names":["sessionStorageOrUndefined","useCallback","RedirectInstallPage","useCreateLinearInstallMutation","saveLinearInstallWorkspace","LinearInstallPage","createInstall","installRequest","body","mutateAsync","errorFallbackMessage","loadingLabel","beforeRedirect","workspaceId"],"mappings":";AAAA,SAAQA,yBAAyB,QAAO,qBAAqB;AAC7D,SAAQC,WAAW,QAAO,QAAQ;AAClC,SAAQC,mBAAmB,QAAO,uCAAuC;AACzE,SAAQC,8BAA8B,QAAO,6BAA6B;AAC1E,SAAQC,0BAA0B,QAAO,sBAAsB;AAE/D,OAAO,SAASC;IACd,MAAMC,gBAAgBH;IACtB,MAAMI,iBAAiBN,YACrB,OAAOO,OAAiC,MAAMF,cAAcG,WAAW,CAACD,OACxE;QAACF;KAAc;IAGjB,qBACE,KAACJ;QACCK,gBAAgBA;QAChBG,sBAAqB;QACrBC,cAAa;QACbC,gBAAgB,CAACC;YACf,IAAI;gBACFT,2BAA2BJ,6BAA6Ba;YAC1D,EAAE,OAAM;YACN,0EAA0E;YAC5E;QACF;;AAGN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/sentry-callback-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sentry-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/sentry-callback-page.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,kBAAkB,uCAmMjC"}
|
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useAuthState, useRefreshAuth } from '@shipfox/client-auth';
|
|
3
|
-
import {
|
|
3
|
+
import { useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
|
+
import { createSingleFlight, sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
4
5
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
5
6
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
6
7
|
import { Card } from '@shipfox/react-ui/card';
|
|
7
8
|
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
8
9
|
import { toast } from '@shipfox/react-ui/toast';
|
|
9
10
|
import { Header, Text } from '@shipfox/react-ui/typography';
|
|
10
|
-
import {
|
|
11
|
-
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
|
|
11
|
+
import { Link, useNavigate } from '@tanstack/react-router';
|
|
12
12
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
13
|
-
import {
|
|
13
|
+
import { useCompleteIntegrationCallback } from '#application/complete-integration-callback.js';
|
|
14
|
+
import { connectSentry } from '#hooks/api/integrations.js';
|
|
14
15
|
import { classifySentryConnectError, clearSentryInstallWorkspace, parseSentryCallbackParams, preselectSentryWorkspace, readSentryInstallWorkspace } from '#sentry-callback.js';
|
|
15
16
|
const connectRequests = createSingleFlight();
|
|
16
17
|
export function SentryCallbackPage() {
|
|
17
|
-
const search = useSearch({
|
|
18
|
-
strict: false
|
|
19
|
-
});
|
|
20
18
|
const navigate = useNavigate();
|
|
21
19
|
const refreshAuth = useRefreshAuth();
|
|
22
20
|
const { workspaces, isLoading } = useAuthState();
|
|
23
|
-
const
|
|
24
|
-
const params =
|
|
25
|
-
|
|
26
|
-
]);
|
|
27
|
-
const storedWorkspaceId = useMemo(()=>readSentryInstallWorkspace(window.sessionStorage), []);
|
|
21
|
+
const completeIntegrationCallback = useCompleteIntegrationCallback();
|
|
22
|
+
const params = useRouteSearch(parseSentryCallbackParams);
|
|
23
|
+
const storedWorkspaceId = useMemo(()=>readSentryInstallWorkspace(sessionStorageOrUndefined()), []);
|
|
28
24
|
const preselection = preselectSentryWorkspace(storedWorkspaceId, workspaces);
|
|
29
25
|
const preselectedId = preselection.kind === 'pick' ? preselection.preselectedId : undefined;
|
|
30
26
|
const [connectingId, setConnectingId] = useState();
|
|
@@ -97,19 +93,20 @@ export function SentryCallbackPage() {
|
|
|
97
93
|
setConnectingId(workspaceId);
|
|
98
94
|
setFailure(undefined);
|
|
99
95
|
const key = `${params.installationId}|${workspaceId}|${params.code}`;
|
|
100
|
-
const request = connectRequests.run(key, async ()=>await
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
96
|
+
const request = connectRequests.run(key, async ()=>await completeIntegrationCallback({
|
|
97
|
+
input: workspaceId,
|
|
98
|
+
refreshAuth,
|
|
99
|
+
complete: async (selectedWorkspaceId, token)=>await connectSentry({
|
|
100
|
+
body: {
|
|
101
|
+
workspace_id: selectedWorkspaceId,
|
|
102
|
+
code: params.code,
|
|
103
|
+
installation_id: params.installationId
|
|
104
|
+
},
|
|
105
|
+
token
|
|
106
|
+
})
|
|
107
|
+
}));
|
|
108
108
|
request.then(async ()=>{
|
|
109
|
-
clearSentryInstallWorkspace(
|
|
110
|
-
await queryClient.invalidateQueries({
|
|
111
|
-
queryKey: integrationsQueryKeys.connectionsByWorkspace(workspaceId)
|
|
112
|
-
});
|
|
109
|
+
clearSentryInstallWorkspace(sessionStorageOrUndefined());
|
|
113
110
|
if (disposedRef.current) return;
|
|
114
111
|
toast.success('Sentry installed.');
|
|
115
112
|
await navigate({
|
|
@@ -125,7 +122,7 @@ export function SentryCallbackPage() {
|
|
|
125
122
|
if (classified.kind === 'terminal') {
|
|
126
123
|
// Only a fresh install (new grant code) can recover; the stored
|
|
127
124
|
// handoff has served its purpose either way.
|
|
128
|
-
clearSentryInstallWorkspace(
|
|
125
|
+
clearSentryInstallWorkspace(sessionStorageOrUndefined());
|
|
129
126
|
}
|
|
130
127
|
setConnectingId(undefined);
|
|
131
128
|
setFailure({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/sentry-callback-page.tsx"],"sourcesContent":["import type {SentryConnectResponseDto} from '@shipfox/api-integration-sentry-dto';\nimport {useAuthState, useRefreshAuth} from '@shipfox/client-auth';\nimport {createSingleFlight} 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, useSearch} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {connectSentry, integrationsQueryKeys} from '#hooks/api/integrations.js';\nimport {\n classifySentryConnectError,\n clearSentryInstallWorkspace,\n parseSentryCallbackParams,\n preselectSentryWorkspace,\n readSentryInstallWorkspace,\n type SentryConnectFailure,\n} from '#sentry-callback.js';\n\nconst connectRequests = createSingleFlight<string, SentryConnectResponseDto>();\n\nexport function SentryCallbackPage() {\n const search = useSearch({strict: false});\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const {workspaces, isLoading} = useAuthState();\n const queryClient = useQueryClient();\n\n const params = useMemo(() => parseSentryCallbackParams(search), [search]);\n const storedWorkspaceId = useMemo(() => readSentryInstallWorkspace(window.sessionStorage), []);\n const preselection = preselectSentryWorkspace(storedWorkspaceId, workspaces);\n const preselectedId = preselection.kind === 'pick' ? preselection.preselectedId : undefined;\n\n const [connectingId, setConnectingId] = useState<string | undefined>();\n const [failure, setFailure] = useState<\n {workspaceId: string; failure: SentryConnectFailure} | undefined\n >();\n const [retryLocked, setRetryLocked] = useState(false);\n const disposedRef = useRef(false);\n\n useEffect(() => {\n disposedRef.current = false;\n return () => {\n disposedRef.current = true;\n };\n }, []);\n\n // Wait for auth before treating an empty workspace list as \"no workspace\": on\n // a cold return from Sentry, `workspaces` is [] until the session refresh\n // resolves, and bouncing to `/` then would discard the one-shot grant code.\n const noWorkspace = !isLoading && preselection.kind === 'none';\n useEffect(() => {\n if (!noWorkspace) return;\n toast.error('You need a workspace before installing Sentry.');\n navigate({to: '/', replace: true});\n }, [noWorkspace, navigate]);\n\n const retryAfterSeconds =\n failure?.failure.kind === 'retryable' ? failure.failure.retryAfterSeconds : undefined;\n useEffect(() => {\n // No backoff window (any non-rate-limited failure) re-enables Retry, so a\n // prior rate-limit lock can never strand the button in a disabled state.\n if (!retryAfterSeconds) {\n setRetryLocked(false);\n return;\n }\n setRetryLocked(true);\n const timer = setTimeout(() => setRetryLocked(false), retryAfterSeconds * 1000);\n return () => clearTimeout(timer);\n }, [retryAfterSeconds]);\n\n if (!params) {\n return (\n <CallbackColumn>\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">\n This Sentry link is missing required parameters. Start the install again from your\n workspace settings.\n </Text>\n </Callout>\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n </CallbackColumn>\n );\n }\n\n // Hold the loader until auth resolves; the workspace list and the\n // noWorkspace redirect both depend on a settled `workspaces`.\n if (isLoading) return <FullPageLoader />;\n\n if (noWorkspace) return null;\n\n const connect = (workspaceId: string) => {\n setConnectingId(workspaceId);\n setFailure(undefined);\n const key = `${params.installationId}|${workspaceId}|${params.code}`;\n const request = connectRequests.run(\n key,\n async () =>\n await refreshAuth().then((session) =>\n connectSentry({\n body: {\n workspace_id: workspaceId,\n code: params.code,\n installation_id: params.installationId,\n },\n token: session.token,\n }),\n ),\n );\n\n request\n .then(async () => {\n clearSentryInstallWorkspace(window.sessionStorage);\n await queryClient.invalidateQueries({\n queryKey: integrationsQueryKeys.connectionsByWorkspace(workspaceId),\n });\n if (disposedRef.current) return;\n toast.success('Sentry installed.');\n await navigate({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: workspaceId},\n replace: true,\n });\n })\n .catch((error: unknown) => {\n if (disposedRef.current) return;\n const classified = classifySentryConnectError(error);\n if (classified.kind === 'terminal') {\n // Only a fresh install (new grant code) can recover; the stored\n // handoff has served its purpose either way.\n clearSentryInstallWorkspace(window.sessionStorage);\n }\n setConnectingId(undefined);\n setFailure({workspaceId, failure: classified});\n });\n };\n\n const orderedWorkspaces = preselectedId\n ? [\n ...workspaces.filter((workspace) => workspace.id === preselectedId),\n ...workspaces.filter((workspace) => workspace.id !== preselectedId),\n ]\n : workspaces;\n\n const failureWorkspaceId = failure?.workspaceId ?? preselectedId ?? workspaces[0]?.id;\n\n return (\n <CallbackColumn>\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h2\">Install Sentry</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {params.orgSlug\n ? `Install the Sentry org \"${params.orgSlug}\" in a workspace.`\n : 'Install this Sentry integration in a workspace.'}\n </Text>\n </header>\n\n {failure ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-8\">\n <Text size=\"sm\">{failure.failure.message}</Text>\n {failure.failure.kind === 'retryable' ? (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n disabled={retryLocked || connectingId !== undefined}\n onClick={() => connect(failure.workspaceId)}\n >\n Retry\n </Button>\n ) : null}\n {failure.failure.kind === 'terminal' && failure.failure.startOver ? (\n <Button asChild size=\"sm\" variant=\"secondary\" className=\"w-fit\">\n <Link to=\"/workspaces/$wid/integrations/sentry\" params={{wid: failure.workspaceId}}>\n Start over\n </Link>\n </Button>\n ) : null}\n </div>\n </Callout>\n ) : null}\n\n <section className=\"flex flex-col gap-8\" aria-label=\"Choose a workspace\">\n {orderedWorkspaces.map((workspace) => (\n <Card key={workspace.id} className=\"p-16\">\n <div className=\"flex items-center justify-between gap-12\">\n <Text size=\"md\" bold className=\"truncate\">\n {workspace.name}\n </Text>\n <Button\n variant=\"secondary\"\n disabled={connectingId !== undefined}\n isLoading={connectingId === workspace.id}\n onClick={() => connect(workspace.id)}\n >\n Install\n </Button>\n </div>\n </Card>\n ))}\n </section>\n\n {failureWorkspaceId ? (\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/workspaces/$wid/settings/integrations\" params={{wid: failureWorkspaceId}}>\n Back to settings\n </Link>\n </ButtonLink>\n ) : null}\n </CallbackColumn>\n );\n}\n\nfunction CallbackColumn({children}: {children: React.ReactNode}) {\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 {children}\n </div>\n </main>\n );\n}\n"],"names":["useAuthState","useRefreshAuth","createSingleFlight","Button","ButtonLink","Callout","Card","FullPageLoader","toast","Header","Text","useQueryClient","Link","useNavigate","useSearch","useEffect","useMemo","useRef","useState","connectSentry","integrationsQueryKeys","classifySentryConnectError","clearSentryInstallWorkspace","parseSentryCallbackParams","preselectSentryWorkspace","readSentryInstallWorkspace","connectRequests","SentryCallbackPage","search","strict","navigate","refreshAuth","workspaces","isLoading","queryClient","params","storedWorkspaceId","window","sessionStorage","preselection","preselectedId","kind","undefined","connectingId","setConnectingId","failure","setFailure","retryLocked","setRetryLocked","disposedRef","current","noWorkspace","error","to","replace","retryAfterSeconds","timer","setTimeout","clearTimeout","CallbackColumn","role","type","size","asChild","variant","className","connect","workspaceId","key","installationId","code","request","run","then","session","body","workspace_id","installation_id","token","invalidateQueries","queryKey","connectionsByWorkspace","success","wid","catch","classified","orderedWorkspaces","filter","workspace","id","failureWorkspaceId","header","orgSlug","div","message","disabled","onClick","startOver","section","aria-label","map","bold","name","children","main"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/sentry-callback-page.tsx"],"sourcesContent":["import {useAuthState, useRefreshAuth} from '@shipfox/client-auth';\nimport {useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {createSingleFlight, 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 {Link, useNavigate} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {useCompleteIntegrationCallback} from '#application/complete-integration-callback.js';\nimport type {IntegrationConnection} from '#core/models.js';\nimport {connectSentry} from '#hooks/api/integrations.js';\nimport {\n classifySentryConnectError,\n clearSentryInstallWorkspace,\n parseSentryCallbackParams,\n preselectSentryWorkspace,\n readSentryInstallWorkspace,\n type SentryConnectFailure,\n} from '#sentry-callback.js';\n\nconst connectRequests = createSingleFlight<string, IntegrationConnection>();\n\nexport function SentryCallbackPage() {\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const {workspaces, isLoading} = useAuthState();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n\n const params = useRouteSearch(parseSentryCallbackParams);\n const storedWorkspaceId = useMemo(\n () => readSentryInstallWorkspace(sessionStorageOrUndefined()),\n [],\n );\n const preselection = preselectSentryWorkspace(storedWorkspaceId, workspaces);\n const preselectedId = preselection.kind === 'pick' ? preselection.preselectedId : undefined;\n\n const [connectingId, setConnectingId] = useState<string | undefined>();\n const [failure, setFailure] = useState<\n {workspaceId: string; failure: SentryConnectFailure} | undefined\n >();\n const [retryLocked, setRetryLocked] = useState(false);\n const disposedRef = useRef(false);\n\n useEffect(() => {\n disposedRef.current = false;\n return () => {\n disposedRef.current = true;\n };\n }, []);\n\n // Wait for auth before treating an empty workspace list as \"no workspace\": on\n // a cold return from Sentry, `workspaces` is [] until the session refresh\n // resolves, and bouncing to `/` then would discard the one-shot grant code.\n const noWorkspace = !isLoading && preselection.kind === 'none';\n useEffect(() => {\n if (!noWorkspace) return;\n toast.error('You need a workspace before installing Sentry.');\n navigate({to: '/', replace: true});\n }, [noWorkspace, navigate]);\n\n const retryAfterSeconds =\n failure?.failure.kind === 'retryable' ? failure.failure.retryAfterSeconds : undefined;\n useEffect(() => {\n // No backoff window (any non-rate-limited failure) re-enables Retry, so a\n // prior rate-limit lock can never strand the button in a disabled state.\n if (!retryAfterSeconds) {\n setRetryLocked(false);\n return;\n }\n setRetryLocked(true);\n const timer = setTimeout(() => setRetryLocked(false), retryAfterSeconds * 1000);\n return () => clearTimeout(timer);\n }, [retryAfterSeconds]);\n\n if (!params) {\n return (\n <CallbackColumn>\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">\n This Sentry link is missing required parameters. Start the install again from your\n workspace settings.\n </Text>\n </Callout>\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/\">Back to Shipfox</Link>\n </ButtonLink>\n </CallbackColumn>\n );\n }\n\n // Hold the loader until auth resolves; the workspace list and the\n // noWorkspace redirect both depend on a settled `workspaces`.\n if (isLoading) return <FullPageLoader />;\n\n if (noWorkspace) return null;\n\n const connect = (workspaceId: string) => {\n setConnectingId(workspaceId);\n setFailure(undefined);\n const key = `${params.installationId}|${workspaceId}|${params.code}`;\n const request = connectRequests.run(\n key,\n async () =>\n await completeIntegrationCallback({\n input: workspaceId,\n refreshAuth,\n complete: async (selectedWorkspaceId, token) =>\n await connectSentry({\n body: {\n workspace_id: selectedWorkspaceId,\n code: params.code,\n installation_id: params.installationId,\n },\n token,\n }),\n }),\n );\n\n request\n .then(async () => {\n clearSentryInstallWorkspace(sessionStorageOrUndefined());\n if (disposedRef.current) return;\n toast.success('Sentry installed.');\n await navigate({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: workspaceId},\n replace: true,\n });\n })\n .catch((error: unknown) => {\n if (disposedRef.current) return;\n const classified = classifySentryConnectError(error);\n if (classified.kind === 'terminal') {\n // Only a fresh install (new grant code) can recover; the stored\n // handoff has served its purpose either way.\n clearSentryInstallWorkspace(sessionStorageOrUndefined());\n }\n setConnectingId(undefined);\n setFailure({workspaceId, failure: classified});\n });\n };\n\n const orderedWorkspaces = preselectedId\n ? [\n ...workspaces.filter((workspace) => workspace.id === preselectedId),\n ...workspaces.filter((workspace) => workspace.id !== preselectedId),\n ]\n : workspaces;\n\n const failureWorkspaceId = failure?.workspaceId ?? preselectedId ?? workspaces[0]?.id;\n\n return (\n <CallbackColumn>\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h2\">Install Sentry</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {params.orgSlug\n ? `Install the Sentry org \"${params.orgSlug}\" in a workspace.`\n : 'Install this Sentry integration in a workspace.'}\n </Text>\n </header>\n\n {failure ? (\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-8\">\n <Text size=\"sm\">{failure.failure.message}</Text>\n {failure.failure.kind === 'retryable' ? (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n disabled={retryLocked || connectingId !== undefined}\n onClick={() => connect(failure.workspaceId)}\n >\n Retry\n </Button>\n ) : null}\n {failure.failure.kind === 'terminal' && failure.failure.startOver ? (\n <Button asChild size=\"sm\" variant=\"secondary\" className=\"w-fit\">\n <Link to=\"/workspaces/$wid/integrations/sentry\" params={{wid: failure.workspaceId}}>\n Start over\n </Link>\n </Button>\n ) : null}\n </div>\n </Callout>\n ) : null}\n\n <section className=\"flex flex-col gap-8\" aria-label=\"Choose a workspace\">\n {orderedWorkspaces.map((workspace) => (\n <Card key={workspace.id} className=\"p-16\">\n <div className=\"flex items-center justify-between gap-12\">\n <Text size=\"md\" bold className=\"truncate\">\n {workspace.name}\n </Text>\n <Button\n variant=\"secondary\"\n disabled={connectingId !== undefined}\n isLoading={connectingId === workspace.id}\n onClick={() => connect(workspace.id)}\n >\n Install\n </Button>\n </div>\n </Card>\n ))}\n </section>\n\n {failureWorkspaceId ? (\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/workspaces/$wid/settings/integrations\" params={{wid: failureWorkspaceId}}>\n Back to settings\n </Link>\n </ButtonLink>\n ) : null}\n </CallbackColumn>\n );\n}\n\nfunction CallbackColumn({children}: {children: React.ReactNode}) {\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 {children}\n </div>\n </main>\n );\n}\n"],"names":["useAuthState","useRefreshAuth","useRouteSearch","createSingleFlight","sessionStorageOrUndefined","Button","ButtonLink","Callout","Card","FullPageLoader","toast","Header","Text","Link","useNavigate","useEffect","useMemo","useRef","useState","useCompleteIntegrationCallback","connectSentry","classifySentryConnectError","clearSentryInstallWorkspace","parseSentryCallbackParams","preselectSentryWorkspace","readSentryInstallWorkspace","connectRequests","SentryCallbackPage","navigate","refreshAuth","workspaces","isLoading","completeIntegrationCallback","params","storedWorkspaceId","preselection","preselectedId","kind","undefined","connectingId","setConnectingId","failure","setFailure","retryLocked","setRetryLocked","disposedRef","current","noWorkspace","error","to","replace","retryAfterSeconds","timer","setTimeout","clearTimeout","CallbackColumn","role","type","size","asChild","variant","className","connect","workspaceId","key","installationId","code","request","run","input","complete","selectedWorkspaceId","token","body","workspace_id","installation_id","then","success","wid","catch","classified","orderedWorkspaces","filter","workspace","id","failureWorkspaceId","header","orgSlug","div","message","disabled","onClick","startOver","section","aria-label","map","bold","name","children","main"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/sentry-install-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sentry-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/sentry-install-page.tsx"],"names":[],"mappings":"AAKA,wBAAgB,iBAAiB,gCAahC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
2
3
|
import { RedirectInstallPage } from '#components/redirect-install-page.js';
|
|
3
4
|
import { createSentryInstall } from '#hooks/api/integrations.js';
|
|
4
5
|
import { saveSentryInstallWorkspace } from '#sentry-callback.js';
|
|
@@ -10,7 +11,7 @@ export function SentryInstallPage() {
|
|
|
10
11
|
// Sentry's redirect has no state param; the stored id lets the callback
|
|
11
12
|
// pre-select this workspace. Saving never throws (see helper).
|
|
12
13
|
beforeRedirect: (workspaceId)=>{
|
|
13
|
-
saveSentryInstallWorkspace(
|
|
14
|
+
saveSentryInstallWorkspace(sessionStorageOrUndefined(), workspaceId);
|
|
14
15
|
}
|
|
15
16
|
});
|
|
16
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/sentry-install-page.tsx"],"sourcesContent":["import {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {createSentryInstall} from '#hooks/api/integrations.js';\nimport {saveSentryInstallWorkspace} from '#sentry-callback.js';\n\nexport function SentryInstallPage() {\n return (\n <RedirectInstallPage\n installRequest={createSentryInstall}\n errorFallbackMessage=\"Could not start Sentry install.\"\n loadingLabel=\"Connecting Sentry\"\n // Sentry's redirect has no state param; the stored id lets the callback\n // pre-select this workspace. Saving never throws (see helper).\n beforeRedirect={(workspaceId) => {\n saveSentryInstallWorkspace(
|
|
1
|
+
{"version":3,"sources":["../../src/pages/sentry-install-page.tsx"],"sourcesContent":["import {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {createSentryInstall} from '#hooks/api/integrations.js';\nimport {saveSentryInstallWorkspace} from '#sentry-callback.js';\n\nexport function SentryInstallPage() {\n return (\n <RedirectInstallPage\n installRequest={createSentryInstall}\n errorFallbackMessage=\"Could not start Sentry install.\"\n loadingLabel=\"Connecting Sentry\"\n // Sentry's redirect has no state param; the stored id lets the callback\n // pre-select this workspace. Saving never throws (see helper).\n beforeRedirect={(workspaceId) => {\n saveSentryInstallWorkspace(sessionStorageOrUndefined(), workspaceId);\n }}\n />\n );\n}\n"],"names":["sessionStorageOrUndefined","RedirectInstallPage","createSentryInstall","saveSentryInstallWorkspace","SentryInstallPage","installRequest","errorFallbackMessage","loadingLabel","beforeRedirect","workspaceId"],"mappings":";AAAA,SAAQA,yBAAyB,QAAO,qBAAqB;AAC7D,SAAQC,mBAAmB,QAAO,uCAAuC;AACzE,SAAQC,mBAAmB,QAAO,6BAA6B;AAC/D,SAAQC,0BAA0B,QAAO,sBAAsB;AAE/D,OAAO,SAASC;IACd,qBACE,KAACH;QACCI,gBAAgBH;QAChBI,sBAAqB;QACrBC,cAAa;QACb,wEAAwE;QACxE,+DAA+D;QAC/DC,gBAAgB,CAACC;YACfN,2BAA2BH,6BAA6BS;QAC1D;;AAGN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/slack-callback-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack-callback-page.d.ts","sourceRoot":"","sources":["../../src/pages/slack-callback-page.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,iBAAiB,gCAyFhC"}
|