@shipfox/client-integrations 13.0.0 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/application/complete-integration-callback.d.ts +11 -0
- package/dist/application/complete-integration-callback.d.ts.map +1 -1
- package/dist/application/complete-integration-callback.js +38 -8
- package/dist/application/complete-integration-callback.js.map +1 -1
- package/dist/components/callback-status-shell.d.ts +1 -1
- package/dist/components/callback-status-shell.d.ts.map +1 -1
- package/dist/components/callback-status-shell.js.map +1 -1
- package/dist/core/models.d.ts +6 -0
- package/dist/core/models.d.ts.map +1 -1
- package/dist/core/models.js +12 -0
- package/dist/core/models.js.map +1 -1
- package/dist/feature.d.ts +8 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +10 -0
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integration-mapper.d.ts +3 -1
- package/dist/hooks/api/integration-mapper.d.ts.map +1 -1
- package/dist/hooks/api/integration-mapper.js +8 -0
- package/dist/hooks/api/integration-mapper.js.map +1 -1
- package/dist/hooks/api/integrations.d.ts +25 -1
- package/dist/hooks/api/integrations.d.ts.map +1 -1
- package/dist/hooks/api/integrations.js +44 -1
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/jira-callback-state.d.ts +9 -0
- package/dist/jira-callback-state.d.ts.map +1 -0
- package/dist/jira-callback-state.js +16 -0
- package/dist/jira-callback-state.js.map +1 -0
- package/dist/jira-callback.d.ts +11 -0
- package/dist/jira-callback.d.ts.map +1 -0
- package/dist/jira-callback.js +59 -0
- package/dist/jira-callback.js.map +1 -0
- package/dist/jira-form-errors.d.ts +8 -0
- package/dist/jira-form-errors.d.ts.map +1 -0
- package/dist/jira-form-errors.js +55 -0
- package/dist/jira-form-errors.js.map +1 -0
- package/dist/pages/jira-callback-page.d.ts +2 -0
- package/dist/pages/jira-callback-page.d.ts.map +1 -0
- package/dist/pages/jira-callback-page.js +341 -0
- package/dist/pages/jira-callback-page.js.map +1 -0
- package/dist/pages/jira-install-page.d.ts +2 -0
- package/dist/pages/jira-install-page.d.ts.map +1 -0
- package/dist/pages/jira-install-page.js +26 -0
- package/dist/pages/jira-install-page.js.map +1 -0
- package/dist/provider-catalog.d.ts +1 -1
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +5 -0
- package/dist/provider-catalog.js.map +1 -1
- package/dist/routes/jira-callback.d.ts +8 -0
- package/dist/routes/jira-callback.d.ts.map +1 -0
- package/dist/routes/jira-callback.js +9 -0
- package/dist/routes/jira-callback.js.map +1 -0
- package/dist/routes/jira.d.ts +6 -0
- package/dist/routes/jira.d.ts.map +1 -0
- package/dist/routes/jira.js +7 -0
- package/dist/routes/jira.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -8
- package/src/application/complete-integration-callback.test.ts +44 -1
- package/src/application/complete-integration-callback.ts +61 -8
- package/src/components/callback-status-shell.tsx +4 -1
- package/src/components/integration-usage-events.test.ts +13 -0
- package/src/core/models.ts +18 -0
- package/src/feature.ts +10 -0
- package/src/hooks/api/integration-mapper.ts +6 -0
- package/src/hooks/api/integrations.test.ts +92 -0
- package/src/hooks/api/integrations.ts +71 -0
- package/src/index.ts +4 -0
- package/src/jira-callback-state.ts +19 -0
- package/src/jira-callback.test.ts +76 -0
- package/src/jira-callback.ts +67 -0
- package/src/jira-form-errors.test.ts +57 -0
- package/src/jira-form-errors.ts +94 -0
- package/src/pages/jira-callback-page.test.tsx +250 -0
- package/src/pages/jira-callback-page.tsx +366 -0
- package/src/pages/jira-install-page.tsx +28 -0
- package/src/provider-catalog.test.ts +6 -0
- package/src/provider-catalog.ts +7 -1
- package/src/routes/jira-callback.tsx +8 -0
- package/src/routes/jira.tsx +4 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuthState, useRefreshAuth } from '@shipfox/client-auth';
|
|
3
|
+
import { useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
|
+
import { sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
5
|
+
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
6
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
7
|
+
import { Card } from '@shipfox/react-ui/card';
|
|
8
|
+
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
9
|
+
import { toast } from '@shipfox/react-ui/toast';
|
|
10
|
+
import { Header, Text } from '@shipfox/react-ui/typography';
|
|
11
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
12
|
+
import { Link, useNavigate } from '@tanstack/react-router';
|
|
13
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
14
|
+
import { useCompleteIntegrationCallback, useCompleteIntegrationCallbackResult } from '#application/complete-integration-callback.js';
|
|
15
|
+
import { CallbackStatusShell } from '#components/callback-status-shell.js';
|
|
16
|
+
import { useCompleteJiraCallbackMutation, useCompleteJiraSiteSelectionMutation } from '#hooks/api/integrations.js';
|
|
17
|
+
import { clearJiraInstallWorkspace, parseJiraCallbackQuery, readJiraInstallWorkspace, serializeJiraCallbackQuery } from '#jira-callback.js';
|
|
18
|
+
import { JIRA_CALLBACK_CACHE_SIZE, jiraCallbackRequests, jiraCompletedConnections, jiraSiteSelectionRequests, jiraToastedCallbacks } from '#jira-callback-state.js';
|
|
19
|
+
import { classifyJiraCallbackError } from '#jira-form-errors.js';
|
|
20
|
+
import { rememberCallbackKey, resolveWorkspaceSlug } from '#workspace-navigation.js';
|
|
21
|
+
export function JiraCallbackPage() {
|
|
22
|
+
const navigate = useNavigate();
|
|
23
|
+
const queryClient = useQueryClient();
|
|
24
|
+
const refreshAuth = useRefreshAuth();
|
|
25
|
+
const { workspaces, isLoading } = useAuthState();
|
|
26
|
+
const completeIntegrationCallback = useCompleteIntegrationCallback();
|
|
27
|
+
const completeIntegrationCallbackResult = useCompleteIntegrationCallbackResult();
|
|
28
|
+
const { mutateAsync: completeJiraCallback } = useCompleteJiraCallbackMutation();
|
|
29
|
+
const { mutateAsync: completeJiraSiteSelection } = useCompleteJiraSiteSelectionMutation();
|
|
30
|
+
const params = useRouteSearch(parseJiraCallbackQuery);
|
|
31
|
+
const callbackKey = params ? serializeJiraCallbackQuery(params) : undefined;
|
|
32
|
+
const workspaceId = useMemo(()=>readJiraInstallWorkspace(sessionStorageOrUndefined()), []);
|
|
33
|
+
const [sites, setSites] = useState();
|
|
34
|
+
const [failure, setFailure] = useState();
|
|
35
|
+
const [selectedCloudId, setSelectedCloudId] = useState();
|
|
36
|
+
const [completedWorkspace, setCompletedWorkspace] = useState();
|
|
37
|
+
const disposedRef = useRef(false);
|
|
38
|
+
const callbackKeyRef = useRef(callbackKey);
|
|
39
|
+
callbackKeyRef.current = callbackKey;
|
|
40
|
+
const [callbackStateKey, setCallbackStateKey] = useState(callbackKey);
|
|
41
|
+
useEffect(()=>{
|
|
42
|
+
disposedRef.current = false;
|
|
43
|
+
return ()=>{
|
|
44
|
+
disposedRef.current = true;
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
const completeConnection = useCallback(async (connection, callbackKey, isActive = ()=>true)=>{
|
|
48
|
+
if (disposedRef.current || !isActive()) return;
|
|
49
|
+
const completedConnection = jiraCompletedConnections.get(callbackKey);
|
|
50
|
+
if (completedConnection) {
|
|
51
|
+
const workspaceSlug = await resolveWorkspaceSlug({
|
|
52
|
+
workspaceId: completedConnection.workspaceId,
|
|
53
|
+
fallbackWorkspaces: workspaces,
|
|
54
|
+
queryClient
|
|
55
|
+
});
|
|
56
|
+
if (!disposedRef.current && isActive()) setCompletedWorkspace(workspaceSlug ? {
|
|
57
|
+
slug: workspaceSlug
|
|
58
|
+
} : {});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
rememberCompletedConnection(callbackKey, connection);
|
|
62
|
+
try {
|
|
63
|
+
clearJiraInstallWorkspace(sessionStorageOrUndefined());
|
|
64
|
+
} catch {
|
|
65
|
+
// The successful API response remains the source of truth for navigation.
|
|
66
|
+
}
|
|
67
|
+
if (disposedRef.current || !isActive()) return;
|
|
68
|
+
if (!jiraToastedCallbacks.has(callbackKey)) {
|
|
69
|
+
rememberCallbackKey(jiraToastedCallbacks, callbackKey);
|
|
70
|
+
toast.success('Jira installed.');
|
|
71
|
+
}
|
|
72
|
+
let workspaceSlug;
|
|
73
|
+
try {
|
|
74
|
+
workspaceSlug = await resolveWorkspaceSlug({
|
|
75
|
+
workspaceId: connection.workspaceId,
|
|
76
|
+
fallbackWorkspaces: workspaces,
|
|
77
|
+
queryClient
|
|
78
|
+
});
|
|
79
|
+
if (disposedRef.current || !isActive()) return;
|
|
80
|
+
if (!workspaceSlug) {
|
|
81
|
+
setCompletedWorkspace({});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
setCompletedWorkspace({
|
|
85
|
+
slug: workspaceSlug
|
|
86
|
+
});
|
|
87
|
+
await navigate({
|
|
88
|
+
to: '/w/$workspaceSlug/settings/integrations',
|
|
89
|
+
params: {
|
|
90
|
+
workspaceSlug
|
|
91
|
+
},
|
|
92
|
+
replace: true
|
|
93
|
+
});
|
|
94
|
+
} catch {
|
|
95
|
+
if (!disposedRef.current && isActive()) setCompletedWorkspace({
|
|
96
|
+
slug: workspaceSlug
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}, [
|
|
100
|
+
navigate,
|
|
101
|
+
queryClient,
|
|
102
|
+
workspaces
|
|
103
|
+
]);
|
|
104
|
+
useEffect(()=>{
|
|
105
|
+
if (callbackKey === undefined) return;
|
|
106
|
+
setCallbackStateKey(callbackKey);
|
|
107
|
+
setSites(undefined);
|
|
108
|
+
setSelectedCloudId(undefined);
|
|
109
|
+
setFailure(undefined);
|
|
110
|
+
setCompletedWorkspace(undefined);
|
|
111
|
+
}, [
|
|
112
|
+
callbackKey
|
|
113
|
+
]);
|
|
114
|
+
useEffect(()=>{
|
|
115
|
+
if (!params || !callbackKey || isLoading) return;
|
|
116
|
+
let active = true;
|
|
117
|
+
const completedConnection = jiraCompletedConnections.get(callbackKey);
|
|
118
|
+
if (completedConnection) {
|
|
119
|
+
void completeConnection(completedConnection, callbackKey, ()=>active);
|
|
120
|
+
return ()=>{
|
|
121
|
+
active = false;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const request = jiraCallbackRequests.run(callbackKey, async ()=>{
|
|
125
|
+
return await completeIntegrationCallbackResult({
|
|
126
|
+
input: params,
|
|
127
|
+
refreshAuth,
|
|
128
|
+
complete: async (query, token)=>await completeJiraCallback({
|
|
129
|
+
query,
|
|
130
|
+
token
|
|
131
|
+
}),
|
|
132
|
+
getConnection: (result)=>'sites' in result ? undefined : result
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
request.then(async (result)=>{
|
|
136
|
+
if (disposedRef.current || !active) return;
|
|
137
|
+
if ('sites' in result) {
|
|
138
|
+
setFailure(undefined);
|
|
139
|
+
setSites(result.sites);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
await completeConnection(result, callbackKey, ()=>active);
|
|
143
|
+
}, (error)=>{
|
|
144
|
+
if (!disposedRef.current && active) setFailure(classifyJiraCallbackError(error));
|
|
145
|
+
});
|
|
146
|
+
return ()=>{
|
|
147
|
+
active = false;
|
|
148
|
+
};
|
|
149
|
+
}, [
|
|
150
|
+
callbackKey,
|
|
151
|
+
completeIntegrationCallbackResult,
|
|
152
|
+
completeJiraCallback,
|
|
153
|
+
completeConnection,
|
|
154
|
+
isLoading,
|
|
155
|
+
params,
|
|
156
|
+
refreshAuth
|
|
157
|
+
]);
|
|
158
|
+
function selectSite(site) {
|
|
159
|
+
if (!params || !callbackKey || selectedCloudId) return;
|
|
160
|
+
setSelectedCloudId(site.cloudId);
|
|
161
|
+
setFailure(undefined);
|
|
162
|
+
const selectionKey = callbackKey;
|
|
163
|
+
const request = jiraSiteSelectionRequests.run(params.state, async ()=>{
|
|
164
|
+
return await completeIntegrationCallback({
|
|
165
|
+
input: {
|
|
166
|
+
cloud_id: site.cloudId,
|
|
167
|
+
state: params.state
|
|
168
|
+
},
|
|
169
|
+
refreshAuth,
|
|
170
|
+
complete: async (body, token)=>await completeJiraSiteSelection({
|
|
171
|
+
body,
|
|
172
|
+
token
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
request.then(async (connection)=>{
|
|
177
|
+
if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;
|
|
178
|
+
jiraCallbackRequests.clear(selectionKey);
|
|
179
|
+
await completeConnection(connection, selectionKey, ()=>callbackKeyRef.current === selectionKey);
|
|
180
|
+
}, (error)=>{
|
|
181
|
+
if (disposedRef.current || callbackKeyRef.current !== selectionKey) return;
|
|
182
|
+
setSelectedCloudId(undefined);
|
|
183
|
+
setFailure(classifyJiraCallbackError(error));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (!params) {
|
|
187
|
+
return /*#__PURE__*/ _jsx(JiraCallbackFailurePage, {
|
|
188
|
+
failure: classifyJiraCallbackError(new Error('invalid callback')),
|
|
189
|
+
workspaceSlug: workspaces.find(({ id })=>id === workspaceId)?.slug
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (isLoading) return /*#__PURE__*/ _jsx(FullPageLoader, {
|
|
193
|
+
"aria-label": "Connecting Jira"
|
|
194
|
+
});
|
|
195
|
+
if (callbackStateKey === callbackKey && completedWorkspace) return /*#__PURE__*/ _jsx(CallbackStatusShell, {
|
|
196
|
+
title: "Jira connected",
|
|
197
|
+
status: "success",
|
|
198
|
+
message: completedWorkspace.slug ? 'Jira is connected. Continue in integrations settings.' : 'Jira is connected. Return to Shipfox to continue.',
|
|
199
|
+
workspaceSlug: completedWorkspace.slug,
|
|
200
|
+
installPath: "/w/$workspaceSlug/integrations/jira"
|
|
201
|
+
});
|
|
202
|
+
if (callbackStateKey === callbackKey && sites) return /*#__PURE__*/ _jsx(JiraSiteSelectionPage, {
|
|
203
|
+
sites: sites,
|
|
204
|
+
selectedCloudId: selectedCloudId,
|
|
205
|
+
failure: failure,
|
|
206
|
+
workspaceSlug: workspaces.find(({ id })=>id === workspaceId)?.slug,
|
|
207
|
+
onSelect: selectSite
|
|
208
|
+
});
|
|
209
|
+
if (callbackStateKey === callbackKey && failure) return /*#__PURE__*/ _jsx(JiraCallbackFailurePage, {
|
|
210
|
+
failure: failure,
|
|
211
|
+
workspaceSlug: workspaces.find(({ id })=>id === workspaceId)?.slug
|
|
212
|
+
});
|
|
213
|
+
return /*#__PURE__*/ _jsx(FullPageLoader, {
|
|
214
|
+
"aria-label": "Connecting Jira"
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function rememberCompletedConnection(key, connection) {
|
|
218
|
+
jiraCompletedConnections.delete(key);
|
|
219
|
+
jiraCompletedConnections.set(key, connection);
|
|
220
|
+
while(jiraCompletedConnections.size > JIRA_CALLBACK_CACHE_SIZE){
|
|
221
|
+
const oldestKey = jiraCompletedConnections.keys().next().value;
|
|
222
|
+
if (oldestKey === undefined) return;
|
|
223
|
+
jiraCompletedConnections.delete(oldestKey);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function JiraCallbackFailurePage({ failure, workspaceSlug }) {
|
|
227
|
+
return /*#__PURE__*/ _jsx(CallbackStatusShell, {
|
|
228
|
+
title: failure.title,
|
|
229
|
+
message: failure.message,
|
|
230
|
+
startOver: failure.startOver,
|
|
231
|
+
switchAccount: failure.signIn,
|
|
232
|
+
workspaceSlug: workspaceSlug,
|
|
233
|
+
installPath: "/w/$workspaceSlug/integrations/jira"
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function JiraSiteSelectionPage({ sites, selectedCloudId, failure, workspaceSlug, onSelect }) {
|
|
237
|
+
return /*#__PURE__*/ _jsx("main", {
|
|
238
|
+
className: "flex min-h-screen bg-background-subtle-base px-16 py-32",
|
|
239
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
240
|
+
className: "mx-auto flex w-full max-w-[480px] flex-col justify-center gap-20",
|
|
241
|
+
children: [
|
|
242
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
243
|
+
className: "flex flex-col gap-8",
|
|
244
|
+
children: [
|
|
245
|
+
/*#__PURE__*/ _jsx(Header, {
|
|
246
|
+
variant: "h2",
|
|
247
|
+
children: "Choose a Jira site"
|
|
248
|
+
}),
|
|
249
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
250
|
+
size: "sm",
|
|
251
|
+
className: "text-foreground-neutral-muted",
|
|
252
|
+
children: "Select the Jira site to connect to this Shipfox workspace."
|
|
253
|
+
})
|
|
254
|
+
]
|
|
255
|
+
}),
|
|
256
|
+
failure ? /*#__PURE__*/ _jsx(Callout, {
|
|
257
|
+
role: "alert",
|
|
258
|
+
type: "error",
|
|
259
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
260
|
+
size: "sm",
|
|
261
|
+
children: failure.message
|
|
262
|
+
})
|
|
263
|
+
}) : null,
|
|
264
|
+
/*#__PURE__*/ _jsx("section", {
|
|
265
|
+
className: "flex flex-col gap-8",
|
|
266
|
+
"aria-label": "Choose a Jira site",
|
|
267
|
+
children: sites.map((site)=>/*#__PURE__*/ _jsx(Card, {
|
|
268
|
+
className: "p-16",
|
|
269
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
270
|
+
className: "flex items-center justify-between gap-12",
|
|
271
|
+
children: [
|
|
272
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
273
|
+
className: "min-w-0",
|
|
274
|
+
children: [
|
|
275
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
276
|
+
size: "md",
|
|
277
|
+
bold: true,
|
|
278
|
+
className: "truncate",
|
|
279
|
+
children: site.name
|
|
280
|
+
}),
|
|
281
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
282
|
+
size: "sm",
|
|
283
|
+
className: "truncate text-foreground-neutral-muted",
|
|
284
|
+
children: site.url
|
|
285
|
+
})
|
|
286
|
+
]
|
|
287
|
+
}),
|
|
288
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
289
|
+
variant: "secondary",
|
|
290
|
+
disabled: selectedCloudId !== undefined,
|
|
291
|
+
isLoading: selectedCloudId === site.cloudId,
|
|
292
|
+
onClick: ()=>onSelect(site),
|
|
293
|
+
children: "Connect"
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
})
|
|
297
|
+
}, site.cloudId))
|
|
298
|
+
}),
|
|
299
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
300
|
+
className: "flex flex-col gap-8 sm:flex-row sm:items-center",
|
|
301
|
+
children: [
|
|
302
|
+
failure?.startOver && workspaceSlug ? /*#__PURE__*/ _jsx(ButtonLink, {
|
|
303
|
+
asChild: true,
|
|
304
|
+
variant: "muted",
|
|
305
|
+
className: "min-h-44 w-full sm:w-fit",
|
|
306
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
307
|
+
to: "/w/$workspaceSlug/integrations/jira",
|
|
308
|
+
params: {
|
|
309
|
+
workspaceSlug
|
|
310
|
+
},
|
|
311
|
+
children: "Start over"
|
|
312
|
+
})
|
|
313
|
+
}) : null,
|
|
314
|
+
workspaceSlug ? /*#__PURE__*/ _jsx(ButtonLink, {
|
|
315
|
+
asChild: true,
|
|
316
|
+
variant: "muted",
|
|
317
|
+
className: "min-h-44 w-full sm:w-fit",
|
|
318
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
319
|
+
to: "/w/$workspaceSlug/settings/integrations",
|
|
320
|
+
params: {
|
|
321
|
+
workspaceSlug
|
|
322
|
+
},
|
|
323
|
+
children: "Back to integrations"
|
|
324
|
+
})
|
|
325
|
+
}) : /*#__PURE__*/ _jsx(ButtonLink, {
|
|
326
|
+
asChild: true,
|
|
327
|
+
variant: "muted",
|
|
328
|
+
className: "min-h-44 w-full sm:w-fit",
|
|
329
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
330
|
+
to: "/",
|
|
331
|
+
children: "Back to Shipfox"
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
]
|
|
335
|
+
})
|
|
336
|
+
]
|
|
337
|
+
})
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
//# sourceMappingURL=jira-callback-page.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/jira-install-page.tsx"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,gCAqB9B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { sessionStorageOrUndefined } from '@shipfox/client-ui';
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { RedirectInstallPage } from '#components/redirect-install-page.js';
|
|
5
|
+
import { useCreateJiraInstallMutation } from '#hooks/api/integrations.js';
|
|
6
|
+
import { saveJiraInstallWorkspace } from '#jira-callback.js';
|
|
7
|
+
export function JiraInstallPage() {
|
|
8
|
+
const createInstall = useCreateJiraInstallMutation();
|
|
9
|
+
const installRequest = useCallback(async (body)=>await createInstall.mutateAsync(body), [
|
|
10
|
+
createInstall
|
|
11
|
+
]);
|
|
12
|
+
return /*#__PURE__*/ _jsx(RedirectInstallPage, {
|
|
13
|
+
installRequest: installRequest,
|
|
14
|
+
errorFallbackMessage: "Could not start Jira install.",
|
|
15
|
+
loadingLabel: "Connecting Jira",
|
|
16
|
+
beforeRedirect: (workspaceId)=>{
|
|
17
|
+
try {
|
|
18
|
+
saveJiraInstallWorkspace(sessionStorageOrUndefined(), workspaceId);
|
|
19
|
+
} catch {
|
|
20
|
+
// Storage is only a navigation hint; the signed state is authoritative.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=jira-install-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pages/jira-install-page.tsx"],"sourcesContent":["import {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {useCallback} from 'react';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {useCreateJiraInstallMutation} from '#hooks/api/integrations.js';\nimport {saveJiraInstallWorkspace} from '#jira-callback.js';\n\nexport function JiraInstallPage() {\n const createInstall = useCreateJiraInstallMutation();\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 Jira install.\"\n loadingLabel=\"Connecting Jira\"\n beforeRedirect={(workspaceId) => {\n try {\n saveJiraInstallWorkspace(sessionStorageOrUndefined(), workspaceId);\n } catch {\n // Storage is only a navigation hint; the signed state is authoritative.\n }\n }}\n />\n );\n}\n"],"names":["sessionStorageOrUndefined","useCallback","RedirectInstallPage","useCreateJiraInstallMutation","saveJiraInstallWorkspace","JiraInstallPage","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,4BAA4B,QAAO,6BAA6B;AACxE,SAAQC,wBAAwB,QAAO,oBAAoB;AAE3D,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,yBAAyBJ,6BAA6Ba;YACxD,EAAE,OAAM;YACN,wEAAwE;YAC1E;QACF;;AAGN"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IconName } from '@shipfox/react-ui/icon';
|
|
2
|
-
export type ProviderSetupPath = '/w/$workspaceSlug/integrations/github' | '/w/$workspaceSlug/integrations/gitea' | '/w/$workspaceSlug/integrations/sentry' | '/w/$workspaceSlug/integrations/linear' | '/w/$workspaceSlug/integrations/slack';
|
|
2
|
+
export type ProviderSetupPath = '/w/$workspaceSlug/integrations/github' | '/w/$workspaceSlug/integrations/gitea' | '/w/$workspaceSlug/integrations/sentry' | '/w/$workspaceSlug/integrations/linear' | '/w/$workspaceSlug/integrations/slack' | '/w/$workspaceSlug/integrations/jira';
|
|
3
3
|
interface RouteProviderCatalogEntry {
|
|
4
4
|
kind: 'redirect-install' | 'direct-connect';
|
|
5
5
|
iconName: IconName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-catalog.d.ts","sourceRoot":"","sources":["../src/provider-catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAIrD,MAAM,MAAM,iBAAiB,GACzB,uCAAuC,GACvC,sCAAsC,GACtC,uCAAuC,GACvC,uCAAuC,GACvC,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"provider-catalog.d.ts","sourceRoot":"","sources":["../src/provider-catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAIrD,MAAM,MAAM,iBAAiB,GACzB,uCAAuC,GACvC,sCAAsC,GACtC,uCAAuC,GACvC,uCAAuC,GACvC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C,UAAU,yBAAyB;IACjC,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,CAAC;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,yBAAyB,CAAC;AAEzF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAmCjE,CAAC"}
|
package/dist/provider-catalog.js
CHANGED
|
@@ -20,6 +20,11 @@ export const PROVIDER_CATALOG = {
|
|
|
20
20
|
iconName: PROVIDER_ICONS.slack,
|
|
21
21
|
setupPath: '/w/$workspaceSlug/integrations/slack'
|
|
22
22
|
},
|
|
23
|
+
jira: {
|
|
24
|
+
kind: 'redirect-install',
|
|
25
|
+
iconName: PROVIDER_ICONS.jira,
|
|
26
|
+
setupPath: '/w/$workspaceSlug/integrations/jira'
|
|
27
|
+
},
|
|
23
28
|
gitea: {
|
|
24
29
|
kind: 'direct-connect',
|
|
25
30
|
iconName: PROVIDER_ICONS.gitea,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider-catalog.ts"],"sourcesContent":["import {PROVIDER_ICONS} from '@shipfox/integration-icons';\nimport type {IconName} from '@shipfox/react-ui/icon';\n\n// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed\n// against TanStack Router's route tree.\nexport type ProviderSetupPath =\n | '/w/$workspaceSlug/integrations/github'\n | '/w/$workspaceSlug/integrations/gitea'\n | '/w/$workspaceSlug/integrations/sentry'\n | '/w/$workspaceSlug/integrations/linear'\n | '/w/$workspaceSlug/integrations/slack';\n\ninterface RouteProviderCatalogEntry {\n kind: 'redirect-install' | 'direct-connect';\n iconName: IconName;\n setupPath: ProviderSetupPath;\n}\n\ninterface ModalProviderCatalogEntry {\n kind: 'modal-connect';\n iconName: IconName;\n}\n\nexport type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {\n github: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.github,\n setupPath: '/w/$workspaceSlug/integrations/github',\n },\n sentry: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.sentry,\n setupPath: '/w/$workspaceSlug/integrations/sentry',\n },\n linear: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.linear,\n setupPath: '/w/$workspaceSlug/integrations/linear',\n },\n slack: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.slack,\n setupPath: '/w/$workspaceSlug/integrations/slack',\n },\n gitea: {\n kind: 'direct-connect',\n iconName: PROVIDER_ICONS.gitea,\n setupPath: '/w/$workspaceSlug/integrations/gitea',\n },\n webhook: {\n kind: 'modal-connect',\n iconName: PROVIDER_ICONS.webhook,\n },\n};\n"],"names":["PROVIDER_ICONS","PROVIDER_CATALOG","github","kind","iconName","setupPath","sentry","linear","slack","gitea","webhook"],"mappings":"AAAA,SAAQA,cAAc,QAAO,6BAA6B;
|
|
1
|
+
{"version":3,"sources":["../src/provider-catalog.ts"],"sourcesContent":["import {PROVIDER_ICONS} from '@shipfox/integration-icons';\nimport type {IconName} from '@shipfox/react-ui/icon';\n\n// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed\n// against TanStack Router's route tree.\nexport type ProviderSetupPath =\n | '/w/$workspaceSlug/integrations/github'\n | '/w/$workspaceSlug/integrations/gitea'\n | '/w/$workspaceSlug/integrations/sentry'\n | '/w/$workspaceSlug/integrations/linear'\n | '/w/$workspaceSlug/integrations/slack'\n | '/w/$workspaceSlug/integrations/jira';\n\ninterface RouteProviderCatalogEntry {\n kind: 'redirect-install' | 'direct-connect';\n iconName: IconName;\n setupPath: ProviderSetupPath;\n}\n\ninterface ModalProviderCatalogEntry {\n kind: 'modal-connect';\n iconName: IconName;\n}\n\nexport type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {\n github: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.github,\n setupPath: '/w/$workspaceSlug/integrations/github',\n },\n sentry: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.sentry,\n setupPath: '/w/$workspaceSlug/integrations/sentry',\n },\n linear: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.linear,\n setupPath: '/w/$workspaceSlug/integrations/linear',\n },\n slack: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.slack,\n setupPath: '/w/$workspaceSlug/integrations/slack',\n },\n jira: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.jira,\n setupPath: '/w/$workspaceSlug/integrations/jira',\n },\n gitea: {\n kind: 'direct-connect',\n iconName: PROVIDER_ICONS.gitea,\n setupPath: '/w/$workspaceSlug/integrations/gitea',\n },\n webhook: {\n kind: 'modal-connect',\n iconName: PROVIDER_ICONS.webhook,\n },\n};\n"],"names":["PROVIDER_ICONS","PROVIDER_CATALOG","github","kind","iconName","setupPath","sentry","linear","slack","jira","gitea","webhook"],"mappings":"AAAA,SAAQA,cAAc,QAAO,6BAA6B;AA0B1D,OAAO,MAAMC,mBAAyD;IACpEC,QAAQ;QACNC,MAAM;QACNC,UAAUJ,eAAeE,MAAM;QAC/BG,WAAW;IACb;IACAC,QAAQ;QACNH,MAAM;QACNC,UAAUJ,eAAeM,MAAM;QAC/BD,WAAW;IACb;IACAE,QAAQ;QACNJ,MAAM;QACNC,UAAUJ,eAAeO,MAAM;QAC/BF,WAAW;IACb;IACAG,OAAO;QACLL,MAAM;QACNC,UAAUJ,eAAeQ,KAAK;QAC9BH,WAAW;IACb;IACAI,MAAM;QACJN,MAAM;QACNC,UAAUJ,eAAeS,IAAI;QAC7BJ,WAAW;IACb;IACAK,OAAO;QACLP,MAAM;QACNC,UAAUJ,eAAeU,KAAK;QAC9BL,WAAW;IACb;IACAM,SAAS;QACPR,MAAM;QACNC,UAAUJ,eAAeW,OAAO;IAClC;AACF,EAAE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JiraCallbackPage } from '#pages/jira-callback-page.js';
|
|
2
|
+
import { parseJiraCallbackQuery } from '../jira-callback.js';
|
|
3
|
+
declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
|
|
4
|
+
readonly validateSearch: typeof parseJiraCallbackQuery;
|
|
5
|
+
readonly component: typeof JiraCallbackPage;
|
|
6
|
+
}>;
|
|
7
|
+
export default _default;
|
|
8
|
+
//# sourceMappingURL=jira-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira-callback.d.ts","sourceRoot":"","sources":["../../src/routes/jira-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAC,sBAAsB,EAAC,MAAM,qBAAqB,CAAC;;;;;AAE3D,wBAGG"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineRoute } from '@shipfox/client-shell/runtime';
|
|
2
|
+
import { JiraCallbackPage } from '#pages/jira-callback-page.js';
|
|
3
|
+
import { parseJiraCallbackQuery } from '../jira-callback.js';
|
|
4
|
+
export default defineRoute({
|
|
5
|
+
validateSearch: parseJiraCallbackQuery,
|
|
6
|
+
component: JiraCallbackPage
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=jira-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/jira-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {JiraCallbackPage} from '#pages/jira-callback-page.js';\nimport {parseJiraCallbackQuery} from '../jira-callback.js';\n\nexport default defineRoute({\n validateSearch: parseJiraCallbackQuery,\n component: JiraCallbackPage,\n});\n"],"names":["defineRoute","JiraCallbackPage","parseJiraCallbackQuery","validateSearch","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,gBAAgB,QAAO,+BAA+B;AAC9D,SAAQC,sBAAsB,QAAO,sBAAsB;AAE3D,eAAeF,YAAY;IACzBG,gBAAgBD;IAChBE,WAAWH;AACb,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/routes/jira.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,eAAe,EAAC,MAAM,6BAA6B,CAAC;;;;AAE5D,wBAAyD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/jira.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {JiraInstallPage} from '#pages/jira-install-page.js';\n\nexport default defineRoute({component: JiraInstallPage});\n"],"names":["defineRoute","JiraInstallPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,eAAe,QAAO,8BAA8B;AAE5D,eAAeD,YAAY;IAACE,WAAWD;AAAe,GAAG"}
|