@shipfox/client-integrations 0.2.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/.storybook/main.ts +1 -0
- package/.storybook/preview.css +4 -0
- package/.storybook/preview.tsx +56 -0
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +207 -0
- package/LICENSE +21 -0
- package/dist/components/connection-picker.d.ts +7 -0
- package/dist/components/connection-picker.d.ts.map +1 -0
- package/dist/components/connection-picker.js +98 -0
- package/dist/components/connection-picker.js.map +1 -0
- package/dist/components/installed-integrations-section.d.ts +14 -0
- package/dist/components/installed-integrations-section.d.ts.map +1 -0
- package/dist/components/installed-integrations-section.js +203 -0
- package/dist/components/installed-integrations-section.js.map +1 -0
- package/dist/components/integration-delete-confirm-modal.d.ts +10 -0
- package/dist/components/integration-delete-confirm-modal.d.ts.map +1 -0
- package/dist/components/integration-delete-confirm-modal.js +61 -0
- package/dist/components/integration-delete-confirm-modal.js.map +1 -0
- package/dist/components/integration-gallery-for-workspace.d.ts +9 -0
- package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -0
- package/dist/components/integration-gallery-for-workspace.js +169 -0
- package/dist/components/integration-gallery-for-workspace.js.map +1 -0
- package/dist/components/integration-gallery.d.ts +8 -0
- package/dist/components/integration-gallery.d.ts.map +1 -0
- package/dist/components/integration-gallery.js +26 -0
- package/dist/components/integration-gallery.js.map +1 -0
- package/dist/components/integration-gallery.stories.d.ts +26 -0
- package/dist/components/integration-gallery.stories.d.ts.map +1 -0
- package/dist/components/integration-gallery.stories.js +335 -0
- package/dist/components/integration-gallery.stories.js.map +1 -0
- package/dist/components/integration-usage-events.d.ts +4 -0
- package/dist/components/integration-usage-events.d.ts.map +1 -0
- package/dist/components/integration-usage-events.js +127 -0
- package/dist/components/integration-usage-events.js.map +1 -0
- package/dist/components/integration-usage-modal.d.ts +16 -0
- package/dist/components/integration-usage-modal.d.ts.map +1 -0
- package/dist/components/integration-usage-modal.js +184 -0
- package/dist/components/integration-usage-modal.js.map +1 -0
- package/dist/components/provider-grid.d.ts +14 -0
- package/dist/components/provider-grid.d.ts.map +1 -0
- package/dist/components/provider-grid.js +152 -0
- package/dist/components/provider-grid.js.map +1 -0
- package/dist/components/redirect-install-page.d.ts +19 -0
- package/dist/components/redirect-install-page.d.ts.map +1 -0
- package/dist/components/redirect-install-page.js +68 -0
- package/dist/components/redirect-install-page.js.map +1 -0
- package/dist/components/repository-picker.d.ts +15 -0
- package/dist/components/repository-picker.d.ts.map +1 -0
- package/dist/components/repository-picker.js +78 -0
- package/dist/components/repository-picker.js.map +1 -0
- package/dist/components/webhook/copyable-value.d.ts +10 -0
- package/dist/components/webhook/copyable-value.d.ts.map +1 -0
- package/dist/components/webhook/copyable-value.js +65 -0
- package/dist/components/webhook/copyable-value.js.map +1 -0
- package/dist/components/webhook/webhook-create-modal.d.ts +10 -0
- package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -0
- package/dist/components/webhook/webhook-create-modal.js +233 -0
- package/dist/components/webhook/webhook-create-modal.js.map +1 -0
- package/dist/components/webhook/webhook-form-errors.d.ts +11 -0
- package/dist/components/webhook/webhook-form-errors.d.ts.map +1 -0
- package/dist/components/webhook/webhook-form-errors.js +27 -0
- package/dist/components/webhook/webhook-form-errors.js.map +1 -0
- package/dist/components/webhook/webhook-modals.stories.d.ts +25 -0
- package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -0
- package/dist/components/webhook/webhook-modals.stories.js +222 -0
- package/dist/components/webhook/webhook-modals.stories.js.map +1 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.d.ts +2 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.d.ts.map +1 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.js +20 -0
- package/dist/components/webhook/webhook-public-endpoint-alert.js.map +1 -0
- package/dist/components/webhook/webhook-usage-details.d.ts +7 -0
- package/dist/components/webhook/webhook-usage-details.d.ts.map +1 -0
- package/dist/components/webhook/webhook-usage-details.js +49 -0
- package/dist/components/webhook/webhook-usage-details.js.map +1 -0
- package/dist/connection-status-badge.d.ts +12 -0
- package/dist/connection-status-badge.d.ts.map +1 -0
- package/dist/connection-status-badge.js +34 -0
- package/dist/connection-status-badge.js.map +1 -0
- package/dist/feature.d.ts +37 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/feature.js +48 -0
- package/dist/feature.js.map +1 -0
- package/dist/hooks/api/integrations.d.ts +265 -0
- package/dist/hooks/api/integrations.d.ts.map +1 -0
- package/dist/hooks/api/integrations.js +229 -0
- package/dist/hooks/api/integrations.js.map +1 -0
- package/dist/hooks/api/webhook-connections.d.ts +91 -0
- package/dist/hooks/api/webhook-connections.d.ts.map +1 -0
- package/dist/hooks/api/webhook-connections.js +97 -0
- package/dist/hooks/api/webhook-connections.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/integration-icon.d.ts +17 -0
- package/dist/integration-icon.d.ts.map +1 -0
- package/dist/integration-icon.js +22 -0
- package/dist/integration-icon.js.map +1 -0
- package/dist/linear-callback.d.ts +17 -0
- package/dist/linear-callback.d.ts.map +1 -0
- package/dist/linear-callback.js +113 -0
- package/dist/linear-callback.js.map +1 -0
- package/dist/pages/gitea-form-errors.d.ts +11 -0
- package/dist/pages/gitea-form-errors.d.ts.map +1 -0
- package/dist/pages/gitea-form-errors.js +35 -0
- package/dist/pages/gitea-form-errors.js.map +1 -0
- package/dist/pages/gitea-install-page.d.ts +2 -0
- package/dist/pages/gitea-install-page.d.ts.map +1 -0
- package/dist/pages/gitea-install-page.js +139 -0
- package/dist/pages/gitea-install-page.js.map +1 -0
- package/dist/pages/github-install-page.d.ts +2 -0
- package/dist/pages/github-install-page.d.ts.map +1 -0
- package/dist/pages/github-install-page.js +11 -0
- package/dist/pages/github-install-page.js.map +1 -0
- package/dist/pages/linear-callback-page.d.ts +2 -0
- package/dist/pages/linear-callback-page.d.ts.map +1 -0
- package/dist/pages/linear-callback-page.js +192 -0
- package/dist/pages/linear-callback-page.js.map +1 -0
- package/dist/pages/linear-install-page.d.ts +2 -0
- package/dist/pages/linear-install-page.d.ts.map +1 -0
- package/dist/pages/linear-install-page.js +24 -0
- package/dist/pages/linear-install-page.js.map +1 -0
- package/dist/pages/sentry-callback-page.d.ts +2 -0
- package/dist/pages/sentry-callback-page.d.ts.map +1 -0
- package/dist/pages/sentry-callback-page.js +256 -0
- package/dist/pages/sentry-callback-page.js.map +1 -0
- package/dist/pages/sentry-install-page.d.ts +2 -0
- package/dist/pages/sentry-install-page.d.ts.map +1 -0
- package/dist/pages/sentry-install-page.js +17 -0
- package/dist/pages/sentry-install-page.js.map +1 -0
- package/dist/pages/source-control-onboarding-page.d.ts +2 -0
- package/dist/pages/source-control-onboarding-page.d.ts.map +1 -0
- package/dist/pages/source-control-onboarding-page.js +37 -0
- package/dist/pages/source-control-onboarding-page.js.map +1 -0
- package/dist/provider-catalog.d.ts +15 -0
- package/dist/provider-catalog.d.ts.map +1 -0
- package/dist/provider-catalog.js +28 -0
- package/dist/provider-catalog.js.map +1 -0
- package/dist/routes/gitea.d.ts +6 -0
- package/dist/routes/gitea.d.ts.map +1 -0
- package/dist/routes/gitea.js +7 -0
- package/dist/routes/gitea.js.map +1 -0
- package/dist/routes/github-callback.d.ts +6 -0
- package/dist/routes/github-callback.d.ts.map +1 -0
- package/dist/routes/github-callback.js +129 -0
- package/dist/routes/github-callback.js.map +1 -0
- package/dist/routes/github.d.ts +6 -0
- package/dist/routes/github.d.ts.map +1 -0
- package/dist/routes/github.js +7 -0
- package/dist/routes/github.js.map +1 -0
- package/dist/routes/integrations.d.ts +6 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +7 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/linear-callback.d.ts +6 -0
- package/dist/routes/linear-callback.d.ts.map +1 -0
- package/dist/routes/linear-callback.js +7 -0
- package/dist/routes/linear-callback.js.map +1 -0
- package/dist/routes/linear.d.ts +6 -0
- package/dist/routes/linear.d.ts.map +1 -0
- package/dist/routes/linear.js +7 -0
- package/dist/routes/linear.js.map +1 -0
- package/dist/routes/sentry-callback.d.ts +6 -0
- package/dist/routes/sentry-callback.d.ts.map +1 -0
- package/dist/routes/sentry-callback.js +7 -0
- package/dist/routes/sentry-callback.js.map +1 -0
- package/dist/routes/sentry.d.ts +6 -0
- package/dist/routes/sentry.d.ts.map +1 -0
- package/dist/routes/sentry.js +7 -0
- package/dist/routes/sentry.js.map +1 -0
- package/dist/sentry-callback.d.ts +35 -0
- package/dist/sentry-callback.d.ts.map +1 -0
- package/dist/sentry-callback.js +132 -0
- package/dist/sentry-callback.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +120 -0
- package/src/components/connection-picker.tsx +89 -0
- package/src/components/installed-integrations-section.tsx +198 -0
- package/src/components/integration-delete-confirm-modal.tsx +62 -0
- package/src/components/integration-gallery-for-workspace.tsx +193 -0
- package/src/components/integration-gallery.stories.tsx +302 -0
- package/src/components/integration-gallery.test.tsx +692 -0
- package/src/components/integration-gallery.tsx +49 -0
- package/src/components/integration-usage-events.test.ts +80 -0
- package/src/components/integration-usage-events.ts +116 -0
- package/src/components/integration-usage-modal.tsx +178 -0
- package/src/components/provider-grid.tsx +163 -0
- package/src/components/redirect-install-page.test.tsx +98 -0
- package/src/components/redirect-install-page.tsx +68 -0
- package/src/components/repository-picker.tsx +102 -0
- package/src/components/webhook/copyable-value.test.tsx +41 -0
- package/src/components/webhook/copyable-value.tsx +72 -0
- package/src/components/webhook/webhook-create-modal.tsx +247 -0
- package/src/components/webhook/webhook-form-errors.test.ts +45 -0
- package/src/components/webhook/webhook-form-errors.ts +32 -0
- package/src/components/webhook/webhook-modals.stories.tsx +225 -0
- package/src/components/webhook/webhook-public-endpoint-alert.tsx +15 -0
- package/src/components/webhook/webhook-usage-details.tsx +44 -0
- package/src/connection-status-badge.tsx +38 -0
- package/src/feature.ts +47 -0
- package/src/hooks/api/integrations.test.ts +105 -0
- package/src/hooks/api/integrations.ts +287 -0
- package/src/hooks/api/webhook-connections.ts +115 -0
- package/src/index.ts +16 -0
- package/src/integration-icon.react.test.tsx +40 -0
- package/src/integration-icon.test.ts +19 -0
- package/src/integration-icon.tsx +26 -0
- package/src/linear-callback.test.ts +227 -0
- package/src/linear-callback.ts +136 -0
- package/src/pages/gitea-form-errors.test.ts +55 -0
- package/src/pages/gitea-form-errors.ts +36 -0
- package/src/pages/gitea-install-page.tsx +108 -0
- package/src/pages/github-install-page.tsx +11 -0
- package/src/pages/linear-callback-page.test.tsx +157 -0
- package/src/pages/linear-callback-page.tsx +175 -0
- package/src/pages/linear-install-page.tsx +26 -0
- package/src/pages/sentry-callback-page.test.tsx +205 -0
- package/src/pages/sentry-callback-page.tsx +238 -0
- package/src/pages/sentry-install-page.tsx +17 -0
- package/src/pages/source-control-onboarding-page.test.tsx +46 -0
- package/src/pages/source-control-onboarding-page.tsx +26 -0
- package/src/provider-catalog.test.ts +42 -0
- package/src/provider-catalog.ts +49 -0
- package/src/routes/gitea.tsx +4 -0
- package/src/routes/github-callback.tsx +114 -0
- package/src/routes/github.tsx +4 -0
- package/src/routes/integrations.tsx +4 -0
- package/src/routes/linear-callback.tsx +4 -0
- package/src/routes/linear.tsx +4 -0
- package/src/routes/sentry-callback.tsx +4 -0
- package/src/routes/sentry.tsx +4 -0
- package/src/sentry-callback.test.ts +239 -0
- package/src/sentry-callback.ts +142 -0
- package/test/render.tsx +111 -0
- package/test/setup.ts +3 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vercel.json +8 -0
- package/vitest.config.ts +75 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@shipfox/react-ui/button';
|
|
3
|
+
import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBlockFilename, CodeBlockFiles, CodeBlockHeader, CodeBlockItem } from '@shipfox/react-ui/code-block';
|
|
4
|
+
import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
|
|
5
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shipfox/react-ui/select';
|
|
6
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
8
|
+
import { ConnectionStatusBadge } from '#connection-status-badge.js';
|
|
9
|
+
const WORKFLOW_FILENAME = '.shipfox/workflows/integration.yml';
|
|
10
|
+
export function IntegrationUsageModal({ connection, events, open, onOpenChange, children }) {
|
|
11
|
+
const [selectedEvent, setSelectedEvent] = useState('');
|
|
12
|
+
const eventValuesKey = events.map((event)=>event.value).join('\u0000');
|
|
13
|
+
useEffect(()=>{
|
|
14
|
+
const eventValues = eventValuesKey ? eventValuesKey.split('\u0000') : [];
|
|
15
|
+
setSelectedEvent((current)=>eventValues.includes(current) ? current : eventValues[0] ?? '');
|
|
16
|
+
}, [
|
|
17
|
+
eventValuesKey
|
|
18
|
+
]);
|
|
19
|
+
const workflowExample = connection ? buildWorkflowExample({
|
|
20
|
+
source: connection.slug,
|
|
21
|
+
event: selectedEvent
|
|
22
|
+
}) : '';
|
|
23
|
+
const data = useMemo(()=>[
|
|
24
|
+
{
|
|
25
|
+
language: 'yaml',
|
|
26
|
+
filename: WORKFLOW_FILENAME,
|
|
27
|
+
code: workflowExample
|
|
28
|
+
}
|
|
29
|
+
], [
|
|
30
|
+
workflowExample
|
|
31
|
+
]);
|
|
32
|
+
const title = connection ? `Use ${connection.display_name}` : 'Use integration';
|
|
33
|
+
return /*#__PURE__*/ _jsx(Modal, {
|
|
34
|
+
open: open && connection !== null,
|
|
35
|
+
onOpenChange: onOpenChange,
|
|
36
|
+
children: /*#__PURE__*/ _jsxs(ModalContent, {
|
|
37
|
+
"aria-describedby": undefined,
|
|
38
|
+
className: "max-h-[calc(100vh-32px)] max-w-[640px]",
|
|
39
|
+
children: [
|
|
40
|
+
/*#__PURE__*/ _jsx(ModalTitle, {
|
|
41
|
+
className: "sr-only",
|
|
42
|
+
children: title
|
|
43
|
+
}),
|
|
44
|
+
/*#__PURE__*/ _jsx(ModalHeader, {
|
|
45
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
46
|
+
className: "flex min-w-0 flex-col gap-2",
|
|
47
|
+
children: [
|
|
48
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
49
|
+
className: "flex min-w-0 items-center gap-8",
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
52
|
+
size: "lg",
|
|
53
|
+
"aria-hidden": "true",
|
|
54
|
+
className: "truncate",
|
|
55
|
+
children: title
|
|
56
|
+
}),
|
|
57
|
+
connection ? /*#__PURE__*/ _jsx(ConnectionStatusBadge, {
|
|
58
|
+
status: connection.lifecycle_status,
|
|
59
|
+
className: "shrink-0"
|
|
60
|
+
}) : null
|
|
61
|
+
]
|
|
62
|
+
}),
|
|
63
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
64
|
+
size: "sm",
|
|
65
|
+
className: "text-foreground-neutral-muted",
|
|
66
|
+
children: "Reference this connection from a workflow trigger."
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
})
|
|
70
|
+
}),
|
|
71
|
+
connection ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
72
|
+
children: [
|
|
73
|
+
/*#__PURE__*/ _jsxs(ModalBody, {
|
|
74
|
+
className: "min-h-0 flex-1 gap-24 overflow-y-auto overflow-x-clip scrollbar",
|
|
75
|
+
children: [
|
|
76
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
77
|
+
className: "flex w-full flex-col gap-12",
|
|
78
|
+
children: [
|
|
79
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
80
|
+
size: "md",
|
|
81
|
+
bold: true,
|
|
82
|
+
children: "Usage"
|
|
83
|
+
}),
|
|
84
|
+
events.length > 1 ? /*#__PURE__*/ _jsxs("div", {
|
|
85
|
+
className: "flex flex-col gap-6",
|
|
86
|
+
children: [
|
|
87
|
+
/*#__PURE__*/ _jsx("label", {
|
|
88
|
+
htmlFor: "integration-usage-event",
|
|
89
|
+
className: "font-display text-sm font-medium text-foreground-neutral-base",
|
|
90
|
+
children: "Event"
|
|
91
|
+
}),
|
|
92
|
+
/*#__PURE__*/ _jsxs(Select, {
|
|
93
|
+
value: selectedEvent,
|
|
94
|
+
onValueChange: setSelectedEvent,
|
|
95
|
+
children: [
|
|
96
|
+
/*#__PURE__*/ _jsx(SelectTrigger, {
|
|
97
|
+
id: "integration-usage-event",
|
|
98
|
+
children: /*#__PURE__*/ _jsx(SelectValue, {
|
|
99
|
+
placeholder: "Select event"
|
|
100
|
+
})
|
|
101
|
+
}),
|
|
102
|
+
/*#__PURE__*/ _jsx(SelectContent, {
|
|
103
|
+
children: events.map((event)=>/*#__PURE__*/ _jsx(SelectItem, {
|
|
104
|
+
value: event.value,
|
|
105
|
+
children: event.label
|
|
106
|
+
}, event.value))
|
|
107
|
+
})
|
|
108
|
+
]
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
}) : null,
|
|
112
|
+
/*#__PURE__*/ _jsxs(CodeBlock, {
|
|
113
|
+
data: data,
|
|
114
|
+
className: "h-auto min-h-0 rounded-8",
|
|
115
|
+
children: [
|
|
116
|
+
/*#__PURE__*/ _jsxs(CodeBlockHeader, {
|
|
117
|
+
children: [
|
|
118
|
+
/*#__PURE__*/ _jsx(CodeBlockFiles, {
|
|
119
|
+
children: (item)=>/*#__PURE__*/ _jsx(CodeBlockFilename, {
|
|
120
|
+
value: item.filename,
|
|
121
|
+
children: item.filename
|
|
122
|
+
})
|
|
123
|
+
}),
|
|
124
|
+
/*#__PURE__*/ _jsx(CodeBlockCopyButton, {})
|
|
125
|
+
]
|
|
126
|
+
}),
|
|
127
|
+
/*#__PURE__*/ _jsx(CodeBlockBody, {
|
|
128
|
+
children: (item)=>/*#__PURE__*/ _jsx(CodeBlockItem, {
|
|
129
|
+
value: item.filename,
|
|
130
|
+
children: /*#__PURE__*/ _jsx(CodeBlockContent, {
|
|
131
|
+
language: "yaml",
|
|
132
|
+
syntaxHighlighting: true,
|
|
133
|
+
highlightedLineRange: {
|
|
134
|
+
startLine: 3,
|
|
135
|
+
endLine: 4
|
|
136
|
+
},
|
|
137
|
+
children: item.code
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
]
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
}),
|
|
145
|
+
children ? /*#__PURE__*/ _jsxs("section", {
|
|
146
|
+
className: "flex w-full flex-col gap-12",
|
|
147
|
+
children: [
|
|
148
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
149
|
+
size: "md",
|
|
150
|
+
bold: true,
|
|
151
|
+
children: "Details"
|
|
152
|
+
}),
|
|
153
|
+
children
|
|
154
|
+
]
|
|
155
|
+
}) : null
|
|
156
|
+
]
|
|
157
|
+
}),
|
|
158
|
+
/*#__PURE__*/ _jsx(ModalFooter, {
|
|
159
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
160
|
+
type: "button",
|
|
161
|
+
onClick: ()=>onOpenChange(false),
|
|
162
|
+
children: "Done"
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
]
|
|
166
|
+
}) : null
|
|
167
|
+
]
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function buildWorkflowExample({ source, event }) {
|
|
172
|
+
return [
|
|
173
|
+
'triggers:',
|
|
174
|
+
' integration:',
|
|
175
|
+
` source: ${source}`,
|
|
176
|
+
` event: ${event}`,
|
|
177
|
+
'jobs:',
|
|
178
|
+
' handle-event:',
|
|
179
|
+
' steps:',
|
|
180
|
+
' - run: echo "Received event"'
|
|
181
|
+
].join('\n');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=integration-usage-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/integration-usage-modal.tsx"],"sourcesContent":["import type {IntegrationConnectionDto} from '@shipfox/api-integration-core-dto';\nimport {Button} from '@shipfox/react-ui/button';\nimport {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n CodeBlockItem,\n} from '@shipfox/react-ui/code-block';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {Text} from '@shipfox/react-ui/typography';\nimport type {ReactNode} from 'react';\nimport {useEffect, useMemo, useState} from 'react';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\n\nexport interface IntegrationUsageEvent {\n value: string;\n label: string;\n}\n\ninterface IntegrationUsageModalProps {\n connection: IntegrationConnectionDto | null;\n events: IntegrationUsageEvent[];\n open: boolean;\n onOpenChange: (open: boolean) => void;\n children?: ReactNode;\n}\n\nconst WORKFLOW_FILENAME = '.shipfox/workflows/integration.yml';\n\nexport function IntegrationUsageModal({\n connection,\n events,\n open,\n onOpenChange,\n children,\n}: IntegrationUsageModalProps) {\n const [selectedEvent, setSelectedEvent] = useState('');\n const eventValuesKey = events.map((event) => event.value).join('\\u0000');\n\n useEffect(() => {\n const eventValues = eventValuesKey ? eventValuesKey.split('\\u0000') : [];\n setSelectedEvent((current) =>\n eventValues.includes(current) ? current : (eventValues[0] ?? ''),\n );\n }, [eventValuesKey]);\n\n const workflowExample = connection\n ? buildWorkflowExample({source: connection.slug, event: selectedEvent})\n : '';\n const data = useMemo(\n () => [{language: 'yaml', filename: WORKFLOW_FILENAME, code: workflowExample}],\n [workflowExample],\n );\n const title = connection ? `Use ${connection.display_name}` : 'Use integration';\n\n return (\n <Modal open={open && connection !== null} onOpenChange={onOpenChange}>\n <ModalContent aria-describedby={undefined} className=\"max-h-[calc(100vh-32px)] max-w-[640px]\">\n <ModalTitle className=\"sr-only\">{title}</ModalTitle>\n <ModalHeader>\n <div className=\"flex min-w-0 flex-col gap-2\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text size=\"lg\" aria-hidden=\"true\" className=\"truncate\">\n {title}\n </Text>\n {connection ? (\n <ConnectionStatusBadge status={connection.lifecycle_status} className=\"shrink-0\" />\n ) : null}\n </div>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference this connection from a workflow trigger.\n </Text>\n </div>\n </ModalHeader>\n {connection ? (\n <>\n <ModalBody className=\"min-h-0 flex-1 gap-24 overflow-y-auto overflow-x-clip scrollbar\">\n <section className=\"flex w-full flex-col gap-12\">\n <Text size=\"md\" bold>\n Usage\n </Text>\n {events.length > 1 ? (\n <div className=\"flex flex-col gap-6\">\n <label\n htmlFor=\"integration-usage-event\"\n className=\"font-display text-sm font-medium text-foreground-neutral-base\"\n >\n Event\n </label>\n <Select value={selectedEvent} onValueChange={setSelectedEvent}>\n <SelectTrigger id=\"integration-usage-event\">\n <SelectValue placeholder=\"Select event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((event) => (\n <SelectItem key={event.value} value={event.value}>\n {event.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n <CodeBlock data={data} className=\"h-auto min-h-0 rounded-8\">\n <CodeBlockHeader>\n <CodeBlockFiles>\n {(item) => (\n <CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>\n )}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n </CodeBlockHeader>\n <CodeBlockBody>\n {(item) => (\n <CodeBlockItem value={item.filename}>\n <CodeBlockContent\n language=\"yaml\"\n syntaxHighlighting\n highlightedLineRange={{startLine: 3, endLine: 4}}\n >\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n </section>\n {children ? (\n <section className=\"flex w-full flex-col gap-12\">\n <Text size=\"md\" bold>\n Details\n </Text>\n {children}\n </section>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Button type=\"button\" onClick={() => onOpenChange(false)}>\n Done\n </Button>\n </ModalFooter>\n </>\n ) : null}\n </ModalContent>\n </Modal>\n );\n}\n\nfunction buildWorkflowExample({source, event}: {source: string; event: string}) {\n return [\n 'triggers:',\n ' integration:',\n ` source: ${source}`,\n ` event: ${event}`,\n 'jobs:',\n ' handle-event:',\n ' steps:',\n ' - run: echo \"Received event\"',\n ].join('\\n');\n}\n"],"names":["Button","CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","Text","useEffect","useMemo","useState","ConnectionStatusBadge","WORKFLOW_FILENAME","IntegrationUsageModal","connection","events","open","onOpenChange","children","selectedEvent","setSelectedEvent","eventValuesKey","map","event","value","join","eventValues","split","current","includes","workflowExample","buildWorkflowExample","source","slug","data","language","filename","code","title","display_name","aria-describedby","undefined","className","div","size","aria-hidden","status","lifecycle_status","section","bold","length","label","htmlFor","onValueChange","id","placeholder","item","syntaxHighlighting","highlightedLineRange","startLine","endLine","type","onClick"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ListIntegrationProvidersResponseDto } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import type { UseQueryResult } from '@tanstack/react-query';
|
|
3
|
+
export interface ProviderGridProps {
|
|
4
|
+
providersQuery: UseQueryResult<ListIntegrationProvidersResponseDto, Error>;
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
emptyMessage: string;
|
|
7
|
+
loadingLabel?: string;
|
|
8
|
+
errorSubject?: string;
|
|
9
|
+
onOpenProvider?: ((provider: string) => void) | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare const PROVIDER_GRID_CLASS = "grid grid-cols-2 gap-12 max-[760px]:grid-cols-1";
|
|
12
|
+
export declare const PROVIDER_SURFACE_CLASS = "overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base";
|
|
13
|
+
export declare function ProviderGrid({ providersQuery, workspaceId, emptyMessage, loadingLabel, errorSubject, onOpenProvider, }: ProviderGridProps): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=provider-grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-grid.d.ts","sourceRoot":"","sources":["../../src/components/provider-grid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mCAAmC,EACpC,MAAM,mCAAmC,CAAC;AAQ3C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAK1D,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,cAAc,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC3D;AAED,eAAO,MAAM,mBAAmB,oDAAoD,CAAC;AAErF,eAAO,MAAM,sBAAsB,2FACuD,CAAC;AAE3F,wBAAgB,YAAY,CAAC,EAC3B,cAAc,EACd,WAAW,EACX,YAAY,EACZ,YAAkC,EAClC,YAAuC,EACvC,cAAc,GACf,EAAE,iBAAiB,+BAuCnB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { QueryLoadError } from '@shipfox/client-ui';
|
|
3
|
+
import { Card } from '@shipfox/react-ui/card';
|
|
4
|
+
import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
5
|
+
import { Icon } from '@shipfox/react-ui/icon';
|
|
6
|
+
import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { cn } from '@shipfox/react-ui/utils';
|
|
9
|
+
import { Link } from '@tanstack/react-router';
|
|
10
|
+
import { IntegrationIcon } from '#integration-icon.js';
|
|
11
|
+
import { PROVIDER_CATALOG } from '#provider-catalog.js';
|
|
12
|
+
export const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-12 max-[760px]:grid-cols-1';
|
|
13
|
+
export const PROVIDER_SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
14
|
+
export function ProviderGrid({ providersQuery, workspaceId, emptyMessage, loadingLabel = 'Loading providers', errorSubject = 'available integrations', onOpenProvider }) {
|
|
15
|
+
const providers = providersQuery.data?.providers ?? [];
|
|
16
|
+
const installableProviders = providers.filter((provider)=>PROVIDER_CATALOG[provider.provider]);
|
|
17
|
+
if (providersQuery.isPending) return /*#__PURE__*/ _jsx(ProviderGridSkeleton, {
|
|
18
|
+
label: loadingLabel
|
|
19
|
+
});
|
|
20
|
+
if (providersQuery.isError && providersQuery.data === undefined) {
|
|
21
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
22
|
+
className: cn(PROVIDER_SURFACE_CLASS, 'px-16'),
|
|
23
|
+
children: /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
24
|
+
query: providersQuery,
|
|
25
|
+
subject: errorSubject
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (providersQuery.data !== undefined && installableProviders.length === 0) {
|
|
30
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
31
|
+
className: cn(PROVIDER_SURFACE_CLASS, 'px-16'),
|
|
32
|
+
children: /*#__PURE__*/ _jsx(EmptyState, {
|
|
33
|
+
icon: "componentLine",
|
|
34
|
+
title: "No integrations available",
|
|
35
|
+
description: emptyMessage
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return /*#__PURE__*/ _jsx("ul", {
|
|
40
|
+
className: PROVIDER_GRID_CLASS,
|
|
41
|
+
children: installableProviders.map((provider)=>/*#__PURE__*/ _jsx("li", {
|
|
42
|
+
children: /*#__PURE__*/ _jsx(ProviderCard, {
|
|
43
|
+
provider: provider,
|
|
44
|
+
workspaceId: workspaceId,
|
|
45
|
+
onOpenProvider: onOpenProvider
|
|
46
|
+
})
|
|
47
|
+
}, provider.provider))
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function ProviderCard({ provider, workspaceId, onOpenProvider }) {
|
|
51
|
+
const catalog = PROVIDER_CATALOG[provider.provider];
|
|
52
|
+
if (!catalog) return null;
|
|
53
|
+
if (catalog.kind === 'modal-connect') {
|
|
54
|
+
return /*#__PURE__*/ _jsx("button", {
|
|
55
|
+
type: "button",
|
|
56
|
+
"aria-label": `Add ${provider.display_name}`,
|
|
57
|
+
onClick: ()=>onOpenProvider?.(provider.provider),
|
|
58
|
+
className: "group flex h-full w-full min-w-0 items-center justify-between gap-12 rounded-8 border border-border-neutral-base bg-background-neutral-base p-16 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
|
|
59
|
+
children: /*#__PURE__*/ _jsx(ProviderCardContent, {
|
|
60
|
+
provider: provider,
|
|
61
|
+
action: "Add"
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return /*#__PURE__*/ _jsx(Link, {
|
|
66
|
+
to: catalog.setupPath,
|
|
67
|
+
params: {
|
|
68
|
+
wid: workspaceId
|
|
69
|
+
},
|
|
70
|
+
"aria-label": `Install ${provider.display_name}`,
|
|
71
|
+
className: "group flex h-full min-w-0 items-center justify-between gap-12 rounded-8 border border-border-neutral-base bg-background-neutral-base p-16 transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
|
|
72
|
+
children: /*#__PURE__*/ _jsx(ProviderCardContent, {
|
|
73
|
+
provider: provider,
|
|
74
|
+
action: "Install"
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function ProviderCardContent({ provider, action }) {
|
|
79
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
80
|
+
children: [
|
|
81
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
82
|
+
className: "flex min-w-0 items-center gap-12",
|
|
83
|
+
children: [
|
|
84
|
+
/*#__PURE__*/ _jsx(IntegrationIcon, {
|
|
85
|
+
source: provider.provider,
|
|
86
|
+
"aria-hidden": true,
|
|
87
|
+
className: "size-24 shrink-0 text-foreground-neutral-base"
|
|
88
|
+
}),
|
|
89
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
90
|
+
as: "span",
|
|
91
|
+
size: "md",
|
|
92
|
+
bold: true,
|
|
93
|
+
className: "truncate",
|
|
94
|
+
children: provider.display_name
|
|
95
|
+
})
|
|
96
|
+
]
|
|
97
|
+
}),
|
|
98
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
99
|
+
className: "flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive",
|
|
100
|
+
children: [
|
|
101
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
102
|
+
as: "span",
|
|
103
|
+
size: "sm",
|
|
104
|
+
children: action
|
|
105
|
+
}),
|
|
106
|
+
/*#__PURE__*/ _jsx(Icon, {
|
|
107
|
+
name: "chevronRight",
|
|
108
|
+
className: "size-16"
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
})
|
|
112
|
+
]
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function ProviderGridSkeleton({ label }) {
|
|
116
|
+
return /*#__PURE__*/ _jsx("ul", {
|
|
117
|
+
role: "status",
|
|
118
|
+
"aria-label": label,
|
|
119
|
+
className: PROVIDER_GRID_CLASS,
|
|
120
|
+
children: [
|
|
121
|
+
0,
|
|
122
|
+
1,
|
|
123
|
+
2,
|
|
124
|
+
3
|
|
125
|
+
].map((tile)=>/*#__PURE__*/ _jsx("li", {
|
|
126
|
+
children: /*#__PURE__*/ _jsx(Card, {
|
|
127
|
+
className: "h-full p-16",
|
|
128
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
129
|
+
className: "flex items-center justify-between gap-12",
|
|
130
|
+
children: [
|
|
131
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
132
|
+
className: "flex min-w-0 items-center gap-12",
|
|
133
|
+
children: [
|
|
134
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
135
|
+
className: "size-24 shrink-0"
|
|
136
|
+
}),
|
|
137
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
138
|
+
className: "h-16 w-100"
|
|
139
|
+
})
|
|
140
|
+
]
|
|
141
|
+
}),
|
|
142
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
143
|
+
className: "h-16 w-64 shrink-0"
|
|
144
|
+
})
|
|
145
|
+
]
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
}, tile))
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=provider-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/provider-grid.tsx"],"sourcesContent":["import type {\n IntegrationProviderDto,\n ListIntegrationProvidersResponseDto,\n} from '@shipfox/api-integration-core-dto';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {UseQueryResult} from '@tanstack/react-query';\nimport {Link} from '@tanstack/react-router';\nimport {IntegrationIcon} from '#integration-icon.js';\nimport {PROVIDER_CATALOG} from '#provider-catalog.js';\n\nexport interface ProviderGridProps {\n providersQuery: UseQueryResult<ListIntegrationProvidersResponseDto, Error>;\n workspaceId: string;\n emptyMessage: string;\n loadingLabel?: string;\n errorSubject?: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}\n\nexport const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-12 max-[760px]:grid-cols-1';\n\nexport const PROVIDER_SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function ProviderGrid({\n providersQuery,\n workspaceId,\n emptyMessage,\n loadingLabel = 'Loading providers',\n errorSubject = 'available integrations',\n onOpenProvider,\n}: ProviderGridProps) {\n const providers = providersQuery.data?.providers ?? [];\n const installableProviders = providers.filter((provider) => PROVIDER_CATALOG[provider.provider]);\n\n if (providersQuery.isPending) return <ProviderGridSkeleton label={loadingLabel} />;\n\n if (providersQuery.isError && providersQuery.data === undefined) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-16')}>\n <QueryLoadError query={providersQuery} subject={errorSubject} />\n </div>\n );\n }\n\n if (providersQuery.data !== undefined && installableProviders.length === 0) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-16')}>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations available\"\n description={emptyMessage}\n />\n </div>\n );\n }\n\n return (\n <ul className={PROVIDER_GRID_CLASS}>\n {installableProviders.map((provider) => (\n <li key={provider.provider}>\n <ProviderCard\n provider={provider}\n workspaceId={workspaceId}\n onOpenProvider={onOpenProvider}\n />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction ProviderCard({\n provider,\n workspaceId,\n onOpenProvider,\n}: {\n provider: IntegrationProviderDto;\n workspaceId: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}) {\n const catalog = PROVIDER_CATALOG[provider.provider];\n if (!catalog) return null;\n\n if (catalog.kind === 'modal-connect') {\n return (\n <button\n type=\"button\"\n aria-label={`Add ${provider.display_name}`}\n onClick={() => onOpenProvider?.(provider.provider)}\n className=\"group flex h-full w-full min-w-0 items-center justify-between gap-12 rounded-8 border border-border-neutral-base bg-background-neutral-base p-16 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Add\" />\n </button>\n );\n }\n\n return (\n <Link\n to={catalog.setupPath}\n params={{wid: workspaceId}}\n aria-label={`Install ${provider.display_name}`}\n className=\"group flex h-full min-w-0 items-center justify-between gap-12 rounded-8 border border-border-neutral-base bg-background-neutral-base p-16 transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Install\" />\n </Link>\n );\n}\n\nfunction ProviderCardContent({\n provider,\n action,\n}: {\n provider: IntegrationProviderDto;\n action: 'Add' | 'Install';\n}) {\n return (\n <>\n <span className=\"flex min-w-0 items-center gap-12\">\n <IntegrationIcon\n source={provider.provider}\n aria-hidden\n className=\"size-24 shrink-0 text-foreground-neutral-base\"\n />\n <Text as=\"span\" size=\"md\" bold className=\"truncate\">\n {provider.display_name}\n </Text>\n </span>\n <span className=\"flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive\">\n <Text as=\"span\" size=\"sm\">\n {action}\n </Text>\n <Icon name=\"chevronRight\" className=\"size-16\" />\n </span>\n </>\n );\n}\n\nfunction ProviderGridSkeleton({label}: {label: string}) {\n return (\n <ul role=\"status\" aria-label={label} className={PROVIDER_GRID_CLASS}>\n {[0, 1, 2, 3].map((tile) => (\n <li key={tile}>\n <Card className=\"h-full p-16\">\n <div className=\"flex items-center justify-between gap-12\">\n <div className=\"flex min-w-0 items-center gap-12\">\n <Skeleton className=\"size-24 shrink-0\" />\n <Skeleton className=\"h-16 w-100\" />\n </div>\n <Skeleton className=\"h-16 w-64 shrink-0\" />\n </div>\n </Card>\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","Card","EmptyState","Icon","Skeleton","Text","cn","Link","IntegrationIcon","PROVIDER_CATALOG","PROVIDER_GRID_CLASS","PROVIDER_SURFACE_CLASS","ProviderGrid","providersQuery","workspaceId","emptyMessage","loadingLabel","errorSubject","onOpenProvider","providers","data","installableProviders","filter","provider","isPending","ProviderGridSkeleton","label","isError","undefined","div","className","query","subject","length","icon","title","description","ul","map","li","ProviderCard","catalog","kind","button","type","aria-label","display_name","onClick","ProviderCardContent","action","to","setupPath","params","wid","span","source","aria-hidden","as","size","bold","name","role","tile"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface RedirectInstallPageProps {
|
|
2
|
+
installRequest: (body: {
|
|
3
|
+
workspace_id: string;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
install_url: string;
|
|
6
|
+
}>;
|
|
7
|
+
errorFallbackMessage: string;
|
|
8
|
+
/**
|
|
9
|
+
* Runs before leaving the app (e.g. to persist the workspace id for a
|
|
10
|
+
* state-less provider callback). Must not throw — a failed side effect
|
|
11
|
+
* should never block the redirect.
|
|
12
|
+
*/
|
|
13
|
+
beforeRedirect?: (workspaceId: string) => void;
|
|
14
|
+
/** Injectable for tests: jsdom's window.location cannot be stubbed. */
|
|
15
|
+
assignLocation?: (url: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function RedirectInstallPage({ installRequest, errorFallbackMessage, beforeRedirect, assignLocation, }: RedirectInstallPageProps): import("react").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=redirect-install-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect-install-page.d.ts","sourceRoot":"","sources":["../../src/components/redirect-install-page.tsx"],"names":[],"mappings":"AASA,UAAU,wBAAwB;IAChC,cAAc,EAAE,CAAC,IAAI,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACjF,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,uEAAuE;IACvE,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,cAAqD,GACtD,EAAE,wBAAwB,+BAwC1B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ApiError } from '@shipfox/client-api';
|
|
3
|
+
import { useActiveWorkspace } from '@shipfox/client-auth';
|
|
4
|
+
import { ButtonLink } from '@shipfox/react-ui/button';
|
|
5
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
6
|
+
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { Link } from '@tanstack/react-router';
|
|
9
|
+
import { useEffect, useRef, useState } from 'react';
|
|
10
|
+
export function RedirectInstallPage({ installRequest, errorFallbackMessage, beforeRedirect, assignLocation = (url)=>window.location.assign(url) }) {
|
|
11
|
+
const workspace = useActiveWorkspace();
|
|
12
|
+
const startedRef = useRef(false);
|
|
13
|
+
const [errorMessage, setErrorMessage] = useState();
|
|
14
|
+
useEffect(()=>{
|
|
15
|
+
if (startedRef.current) return;
|
|
16
|
+
startedRef.current = true;
|
|
17
|
+
// The side effect is best-effort persistence; a throw here must never
|
|
18
|
+
// block the install redirect, so swallow it and continue.
|
|
19
|
+
try {
|
|
20
|
+
beforeRedirect?.(workspace.id);
|
|
21
|
+
} catch {
|
|
22
|
+
// ignore
|
|
23
|
+
}
|
|
24
|
+
installRequest({
|
|
25
|
+
workspace_id: workspace.id
|
|
26
|
+
}).then((response)=>{
|
|
27
|
+
assignLocation(response.install_url);
|
|
28
|
+
}).catch((error)=>{
|
|
29
|
+
setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);
|
|
30
|
+
});
|
|
31
|
+
}, [
|
|
32
|
+
workspace,
|
|
33
|
+
installRequest,
|
|
34
|
+
beforeRedirect,
|
|
35
|
+
errorFallbackMessage,
|
|
36
|
+
assignLocation
|
|
37
|
+
]);
|
|
38
|
+
if (errorMessage) {
|
|
39
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
40
|
+
className: "mx-auto flex w-full max-w-[480px] flex-col gap-16",
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ _jsx(Callout, {
|
|
43
|
+
role: "alert",
|
|
44
|
+
type: "error",
|
|
45
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
46
|
+
size: "sm",
|
|
47
|
+
children: errorMessage
|
|
48
|
+
})
|
|
49
|
+
}),
|
|
50
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
51
|
+
asChild: true,
|
|
52
|
+
variant: "muted",
|
|
53
|
+
className: "w-fit",
|
|
54
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
55
|
+
to: "/workspaces/$wid/integrations",
|
|
56
|
+
params: {
|
|
57
|
+
wid: workspace.id
|
|
58
|
+
},
|
|
59
|
+
children: "Back to integrations"
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return /*#__PURE__*/ _jsx(FullPageLoader, {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=redirect-install-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/redirect-install-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface RedirectInstallPageProps {\n installRequest: (body: {workspace_id: string}) => Promise<{install_url: string}>;\n errorFallbackMessage: string;\n /**\n * Runs before leaving the app (e.g. to persist the workspace id for a\n * state-less provider callback). Must not throw — a failed side effect\n * should never block the redirect.\n */\n beforeRedirect?: (workspaceId: string) => void;\n /** Injectable for tests: jsdom's window.location cannot be stubbed. */\n assignLocation?: (url: string) => void;\n}\n\nexport function RedirectInstallPage({\n installRequest,\n errorFallbackMessage,\n beforeRedirect,\n assignLocation = (url) => window.location.assign(url),\n}: RedirectInstallPageProps) {\n const workspace = useActiveWorkspace();\n const startedRef = useRef(false);\n const [errorMessage, setErrorMessage] = useState<string | undefined>();\n\n useEffect(() => {\n if (startedRef.current) return;\n startedRef.current = true;\n // The side effect is best-effort persistence; a throw here must never\n // block the install redirect, so swallow it and continue.\n try {\n beforeRedirect?.(workspace.id);\n } catch {\n // ignore\n }\n installRequest({workspace_id: workspace.id})\n .then((response) => {\n assignLocation(response.install_url);\n })\n .catch((error: unknown) => {\n setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);\n });\n }, [workspace, installRequest, beforeRedirect, errorFallbackMessage, assignLocation]);\n\n if (errorMessage) {\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-16\">\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{errorMessage}</Text>\n </Callout>\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/workspaces/$wid/integrations\" params={{wid: workspace.id}}>\n Back to integrations\n </Link>\n </ButtonLink>\n </div>\n );\n }\n\n return <FullPageLoader />;\n}\n"],"names":["ApiError","useActiveWorkspace","ButtonLink","Callout","FullPageLoader","Text","Link","useEffect","useRef","useState","RedirectInstallPage","installRequest","errorFallbackMessage","beforeRedirect","assignLocation","url","window","location","assign","workspace","startedRef","errorMessage","setErrorMessage","current","id","workspace_id","then","response","install_url","catch","error","message","div","className","role","type","size","asChild","variant","to","params","wid"],"mappings":";AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAelD,OAAO,SAASC,oBAAoB,EAClCC,cAAc,EACdC,oBAAoB,EACpBC,cAAc,EACdC,iBAAiB,CAACC,MAAQC,OAAOC,QAAQ,CAACC,MAAM,CAACH,IAAI,EAC5B;IACzB,MAAMI,YAAYlB;IAClB,MAAMmB,aAAaZ,OAAO;IAC1B,MAAM,CAACa,cAAcC,gBAAgB,GAAGb;IAExCF,UAAU;QACR,IAAIa,WAAWG,OAAO,EAAE;QACxBH,WAAWG,OAAO,GAAG;QACrB,sEAAsE;QACtE,0DAA0D;QAC1D,IAAI;YACFV,iBAAiBM,UAAUK,EAAE;QAC/B,EAAE,OAAM;QACN,SAAS;QACX;QACAb,eAAe;YAACc,cAAcN,UAAUK,EAAE;QAAA,GACvCE,IAAI,CAAC,CAACC;YACLb,eAAea,SAASC,WAAW;QACrC,GACCC,KAAK,CAAC,CAACC;YACNR,gBAAgBQ,iBAAiB9B,WAAW8B,MAAMC,OAAO,GAAGnB;QAC9D;IACJ,GAAG;QAACO;QAAWR;QAAgBE;QAAgBD;QAAsBE;KAAe;IAEpF,IAAIO,cAAc;QAChB,qBACE,MAACW;YAAIC,WAAU;;8BACb,KAAC9B;oBAAQ+B,MAAK;oBAAQC,MAAK;8BACzB,cAAA,KAAC9B;wBAAK+B,MAAK;kCAAMf;;;8BAEnB,KAACnB;oBAAWmC,OAAO;oBAACC,SAAQ;oBAAQL,WAAU;8BAC5C,cAAA,KAAC3B;wBAAKiC,IAAG;wBAAgCC,QAAQ;4BAACC,KAAKtB,UAAUK,EAAE;wBAAA;kCAAG;;;;;IAM9E;IAEA,qBAAO,KAACpB;AACV"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RepositoryDto } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
export declare function RepositoryPicker({ repositories, selectedRepositoryId, onSelect, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, emptyMessage, searchValue, onSearchChange, searchDisabled, }: {
|
|
3
|
+
repositories: RepositoryDto[];
|
|
4
|
+
selectedRepositoryId: string | undefined;
|
|
5
|
+
onSelect: (repositoryId: string) => void;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isFetchingNextPage?: boolean;
|
|
8
|
+
hasNextPage?: boolean;
|
|
9
|
+
onLoadMore?: () => void;
|
|
10
|
+
emptyMessage?: string;
|
|
11
|
+
searchValue?: string;
|
|
12
|
+
onSearchChange?: (value: string) => void;
|
|
13
|
+
searchDisabled?: boolean;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=repository-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-picker.d.ts","sourceRoot":"","sources":["../../src/components/repository-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,mCAAmC,CAAC;AASrE,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,oBAAoB,EACpB,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,YAAuC,EACvC,WAAW,EACX,cAAc,EACd,cAAc,GACf,EAAE;IACD,YAAY,EAAE,aAAa,EAAE,CAAC;IAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,+BAoEA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@shipfox/react-ui/button';
|
|
3
|
+
import { Input } from '@shipfox/react-ui/input';
|
|
4
|
+
import { Label } from '@shipfox/react-ui/label';
|
|
5
|
+
import { RadioGroup, RadioGroupItem } from '@shipfox/react-ui/radio-group';
|
|
6
|
+
import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { useId } from 'react';
|
|
9
|
+
export function RepositoryPicker({ repositories, selectedRepositoryId, onSelect, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, emptyMessage = 'No repositories found.', searchValue, onSearchChange, searchDisabled }) {
|
|
10
|
+
const labelId = useId();
|
|
11
|
+
const showSearch = onSearchChange !== undefined;
|
|
12
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
13
|
+
className: "flex flex-col gap-10",
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ _jsx(Label, {
|
|
16
|
+
id: labelId,
|
|
17
|
+
className: "sr-only",
|
|
18
|
+
children: "Repository"
|
|
19
|
+
}),
|
|
20
|
+
showSearch ? /*#__PURE__*/ _jsx(Input, {
|
|
21
|
+
type: "search",
|
|
22
|
+
placeholder: "Search repositories…",
|
|
23
|
+
"aria-label": "Search repositories",
|
|
24
|
+
value: searchValue ?? '',
|
|
25
|
+
onChange: (event)=>onSearchChange?.(event.target.value),
|
|
26
|
+
disabled: searchDisabled
|
|
27
|
+
}) : null,
|
|
28
|
+
isLoading ? /*#__PURE__*/ _jsx(Skeleton, {
|
|
29
|
+
className: "h-58 w-full"
|
|
30
|
+
}) : null,
|
|
31
|
+
!isLoading && repositories.length === 0 ? /*#__PURE__*/ _jsx("div", {
|
|
32
|
+
className: "rounded-8 border border-border-neutral-base bg-background-subtle-base p-14",
|
|
33
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
34
|
+
size: "sm",
|
|
35
|
+
children: emptyMessage
|
|
36
|
+
})
|
|
37
|
+
}) : null,
|
|
38
|
+
repositories.length > 0 ? /*#__PURE__*/ _jsx(RadioGroup, {
|
|
39
|
+
"aria-labelledby": labelId,
|
|
40
|
+
value: selectedRepositoryId ?? '',
|
|
41
|
+
onValueChange: onSelect,
|
|
42
|
+
className: "grid grid-cols-2 gap-8 min-[1200px]:grid-cols-3 max-[760px]:grid-cols-1",
|
|
43
|
+
children: repositories.map((repository)=>/*#__PURE__*/ _jsx(RadioGroupItem, {
|
|
44
|
+
value: repository.external_repository_id,
|
|
45
|
+
className: "p-12",
|
|
46
|
+
children: /*#__PURE__*/ _jsxs("span", {
|
|
47
|
+
className: "flex min-w-0 items-center justify-between gap-10",
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
50
|
+
as: "span",
|
|
51
|
+
size: "sm",
|
|
52
|
+
bold: true,
|
|
53
|
+
className: "truncate",
|
|
54
|
+
children: repository.full_name
|
|
55
|
+
}),
|
|
56
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
57
|
+
as: "span",
|
|
58
|
+
size: "xs",
|
|
59
|
+
className: "shrink-0 text-foreground-neutral-muted",
|
|
60
|
+
children: repository.default_branch
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
})
|
|
64
|
+
}, repository.external_repository_id))
|
|
65
|
+
}) : null,
|
|
66
|
+
hasNextPage && onLoadMore ? /*#__PURE__*/ _jsx(Button, {
|
|
67
|
+
type: "button",
|
|
68
|
+
variant: "secondary",
|
|
69
|
+
size: "sm",
|
|
70
|
+
isLoading: isFetchingNextPage ?? false,
|
|
71
|
+
onClick: onLoadMore,
|
|
72
|
+
children: "Load more"
|
|
73
|
+
}) : null
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=repository-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/repository-picker.tsx"],"sourcesContent":["import type {RepositoryDto} from '@shipfox/api-integration-core-dto';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\n\nexport function RepositoryPicker({\n repositories,\n selectedRepositoryId,\n onSelect,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n onLoadMore,\n emptyMessage = 'No repositories found.',\n searchValue,\n onSearchChange,\n searchDisabled,\n}: {\n repositories: RepositoryDto[];\n selectedRepositoryId: string | undefined;\n onSelect: (repositoryId: string) => void;\n isLoading: boolean;\n isFetchingNextPage?: boolean;\n hasNextPage?: boolean;\n onLoadMore?: () => void;\n emptyMessage?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n searchDisabled?: boolean;\n}) {\n const labelId = useId();\n const showSearch = onSearchChange !== undefined;\n\n return (\n <div className=\"flex flex-col gap-10\">\n <Label id={labelId} className=\"sr-only\">\n Repository\n </Label>\n\n {showSearch ? (\n <Input\n type=\"search\"\n placeholder=\"Search repositories…\"\n aria-label=\"Search repositories\"\n value={searchValue ?? ''}\n onChange={(event) => onSearchChange?.(event.target.value)}\n disabled={searchDisabled}\n />\n ) : null}\n\n {isLoading ? <Skeleton className=\"h-58 w-full\" /> : null}\n\n {!isLoading && repositories.length === 0 ? (\n <div className=\"rounded-8 border border-border-neutral-base bg-background-subtle-base p-14\">\n <Text size=\"sm\">{emptyMessage}</Text>\n </div>\n ) : null}\n\n {repositories.length > 0 ? (\n <RadioGroup\n aria-labelledby={labelId}\n value={selectedRepositoryId ?? ''}\n onValueChange={onSelect}\n className=\"grid grid-cols-2 gap-8 min-[1200px]:grid-cols-3 max-[760px]:grid-cols-1\"\n >\n {repositories.map((repository) => (\n <RadioGroupItem\n key={repository.external_repository_id}\n value={repository.external_repository_id}\n className=\"p-12\"\n >\n <span className=\"flex min-w-0 items-center justify-between gap-10\">\n <Text as=\"span\" size=\"sm\" bold className=\"truncate\">\n {repository.full_name}\n </Text>\n <Text as=\"span\" size=\"xs\" className=\"shrink-0 text-foreground-neutral-muted\">\n {repository.default_branch}\n </Text>\n </span>\n </RadioGroupItem>\n ))}\n </RadioGroup>\n ) : null}\n\n {hasNextPage && onLoadMore ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n isLoading={isFetchingNextPage ?? false}\n onClick={onLoadMore}\n >\n Load more\n </Button>\n ) : null}\n </div>\n );\n}\n"],"names":["Button","Input","Label","RadioGroup","RadioGroupItem","Skeleton","Text","useId","RepositoryPicker","repositories","selectedRepositoryId","onSelect","isLoading","isFetchingNextPage","hasNextPage","onLoadMore","emptyMessage","searchValue","onSearchChange","searchDisabled","labelId","showSearch","undefined","div","className","id","type","placeholder","aria-label","value","onChange","event","target","disabled","length","size","aria-labelledby","onValueChange","map","repository","external_repository_id","span","as","bold","full_name","default_branch","variant","onClick"],"mappings":";AACA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAE5B,OAAO,SAASC,iBAAiB,EAC/BC,YAAY,EACZC,oBAAoB,EACpBC,QAAQ,EACRC,SAAS,EACTC,kBAAkB,EAClBC,WAAW,EACXC,UAAU,EACVC,eAAe,wBAAwB,EACvCC,WAAW,EACXC,cAAc,EACdC,cAAc,EAaf;IACC,MAAMC,UAAUb;IAChB,MAAMc,aAAaH,mBAAmBI;IAEtC,qBACE,MAACC;QAAIC,WAAU;;0BACb,KAACtB;gBAAMuB,IAAIL;gBAASI,WAAU;0BAAU;;YAIvCH,2BACC,KAACpB;gBACCyB,MAAK;gBACLC,aAAY;gBACZC,cAAW;gBACXC,OAAOZ,eAAe;gBACtBa,UAAU,CAACC,QAAUb,iBAAiBa,MAAMC,MAAM,CAACH,KAAK;gBACxDI,UAAUd;iBAEV;YAEHP,0BAAY,KAACP;gBAASmB,WAAU;iBAAmB;YAEnD,CAACZ,aAAaH,aAAayB,MAAM,KAAK,kBACrC,KAACX;gBAAIC,WAAU;0BACb,cAAA,KAAClB;oBAAK6B,MAAK;8BAAMnB;;iBAEjB;YAEHP,aAAayB,MAAM,GAAG,kBACrB,KAAC/B;gBACCiC,mBAAiBhB;gBACjBS,OAAOnB,wBAAwB;gBAC/B2B,eAAe1B;gBACfa,WAAU;0BAETf,aAAa6B,GAAG,CAAC,CAACC,2BACjB,KAACnC;wBAECyB,OAAOU,WAAWC,sBAAsB;wBACxChB,WAAU;kCAEV,cAAA,MAACiB;4BAAKjB,WAAU;;8CACd,KAAClB;oCAAKoC,IAAG;oCAAOP,MAAK;oCAAKQ,IAAI;oCAACnB,WAAU;8CACtCe,WAAWK,SAAS;;8CAEvB,KAACtC;oCAAKoC,IAAG;oCAAOP,MAAK;oCAAKX,WAAU;8CACjCe,WAAWM,cAAc;;;;uBATzBN,WAAWC,sBAAsB;iBAe1C;YAEH1B,eAAeC,2BACd,KAACf;gBACC0B,MAAK;gBACLoB,SAAQ;gBACRX,MAAK;gBACLvB,WAAWC,sBAAsB;gBACjCkC,SAAShC;0BACV;iBAGC;;;AAGV"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
interface CopyableValueProps {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
note?: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function CopyableValue({ label, value, note, className }: CopyableValueProps): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=copyable-value.d.ts.map
|