@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.
Files changed (243) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/.storybook/preview.css +4 -0
  3. package/.storybook/preview.tsx +56 -0
  4. package/.turbo/turbo-build.log +2 -0
  5. package/.turbo/turbo-type$colon$emit.log +1 -0
  6. package/.turbo/turbo-type.log +1 -0
  7. package/CHANGELOG.md +207 -0
  8. package/LICENSE +21 -0
  9. package/dist/components/connection-picker.d.ts +7 -0
  10. package/dist/components/connection-picker.d.ts.map +1 -0
  11. package/dist/components/connection-picker.js +98 -0
  12. package/dist/components/connection-picker.js.map +1 -0
  13. package/dist/components/installed-integrations-section.d.ts +14 -0
  14. package/dist/components/installed-integrations-section.d.ts.map +1 -0
  15. package/dist/components/installed-integrations-section.js +203 -0
  16. package/dist/components/installed-integrations-section.js.map +1 -0
  17. package/dist/components/integration-delete-confirm-modal.d.ts +10 -0
  18. package/dist/components/integration-delete-confirm-modal.d.ts.map +1 -0
  19. package/dist/components/integration-delete-confirm-modal.js +61 -0
  20. package/dist/components/integration-delete-confirm-modal.js.map +1 -0
  21. package/dist/components/integration-gallery-for-workspace.d.ts +9 -0
  22. package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -0
  23. package/dist/components/integration-gallery-for-workspace.js +169 -0
  24. package/dist/components/integration-gallery-for-workspace.js.map +1 -0
  25. package/dist/components/integration-gallery.d.ts +8 -0
  26. package/dist/components/integration-gallery.d.ts.map +1 -0
  27. package/dist/components/integration-gallery.js +26 -0
  28. package/dist/components/integration-gallery.js.map +1 -0
  29. package/dist/components/integration-gallery.stories.d.ts +26 -0
  30. package/dist/components/integration-gallery.stories.d.ts.map +1 -0
  31. package/dist/components/integration-gallery.stories.js +335 -0
  32. package/dist/components/integration-gallery.stories.js.map +1 -0
  33. package/dist/components/integration-usage-events.d.ts +4 -0
  34. package/dist/components/integration-usage-events.d.ts.map +1 -0
  35. package/dist/components/integration-usage-events.js +127 -0
  36. package/dist/components/integration-usage-events.js.map +1 -0
  37. package/dist/components/integration-usage-modal.d.ts +16 -0
  38. package/dist/components/integration-usage-modal.d.ts.map +1 -0
  39. package/dist/components/integration-usage-modal.js +184 -0
  40. package/dist/components/integration-usage-modal.js.map +1 -0
  41. package/dist/components/provider-grid.d.ts +14 -0
  42. package/dist/components/provider-grid.d.ts.map +1 -0
  43. package/dist/components/provider-grid.js +152 -0
  44. package/dist/components/provider-grid.js.map +1 -0
  45. package/dist/components/redirect-install-page.d.ts +19 -0
  46. package/dist/components/redirect-install-page.d.ts.map +1 -0
  47. package/dist/components/redirect-install-page.js +68 -0
  48. package/dist/components/redirect-install-page.js.map +1 -0
  49. package/dist/components/repository-picker.d.ts +15 -0
  50. package/dist/components/repository-picker.d.ts.map +1 -0
  51. package/dist/components/repository-picker.js +78 -0
  52. package/dist/components/repository-picker.js.map +1 -0
  53. package/dist/components/webhook/copyable-value.d.ts +10 -0
  54. package/dist/components/webhook/copyable-value.d.ts.map +1 -0
  55. package/dist/components/webhook/copyable-value.js +65 -0
  56. package/dist/components/webhook/copyable-value.js.map +1 -0
  57. package/dist/components/webhook/webhook-create-modal.d.ts +10 -0
  58. package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -0
  59. package/dist/components/webhook/webhook-create-modal.js +233 -0
  60. package/dist/components/webhook/webhook-create-modal.js.map +1 -0
  61. package/dist/components/webhook/webhook-form-errors.d.ts +11 -0
  62. package/dist/components/webhook/webhook-form-errors.d.ts.map +1 -0
  63. package/dist/components/webhook/webhook-form-errors.js +27 -0
  64. package/dist/components/webhook/webhook-form-errors.js.map +1 -0
  65. package/dist/components/webhook/webhook-modals.stories.d.ts +25 -0
  66. package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -0
  67. package/dist/components/webhook/webhook-modals.stories.js +222 -0
  68. package/dist/components/webhook/webhook-modals.stories.js.map +1 -0
  69. package/dist/components/webhook/webhook-public-endpoint-alert.d.ts +2 -0
  70. package/dist/components/webhook/webhook-public-endpoint-alert.d.ts.map +1 -0
  71. package/dist/components/webhook/webhook-public-endpoint-alert.js +20 -0
  72. package/dist/components/webhook/webhook-public-endpoint-alert.js.map +1 -0
  73. package/dist/components/webhook/webhook-usage-details.d.ts +7 -0
  74. package/dist/components/webhook/webhook-usage-details.d.ts.map +1 -0
  75. package/dist/components/webhook/webhook-usage-details.js +49 -0
  76. package/dist/components/webhook/webhook-usage-details.js.map +1 -0
  77. package/dist/connection-status-badge.d.ts +12 -0
  78. package/dist/connection-status-badge.d.ts.map +1 -0
  79. package/dist/connection-status-badge.js +34 -0
  80. package/dist/connection-status-badge.js.map +1 -0
  81. package/dist/feature.d.ts +37 -0
  82. package/dist/feature.d.ts.map +1 -0
  83. package/dist/feature.js +48 -0
  84. package/dist/feature.js.map +1 -0
  85. package/dist/hooks/api/integrations.d.ts +265 -0
  86. package/dist/hooks/api/integrations.d.ts.map +1 -0
  87. package/dist/hooks/api/integrations.js +229 -0
  88. package/dist/hooks/api/integrations.js.map +1 -0
  89. package/dist/hooks/api/webhook-connections.d.ts +91 -0
  90. package/dist/hooks/api/webhook-connections.d.ts.map +1 -0
  91. package/dist/hooks/api/webhook-connections.js +97 -0
  92. package/dist/hooks/api/webhook-connections.js.map +1 -0
  93. package/dist/index.d.ts +17 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +18 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/integration-icon.d.ts +17 -0
  98. package/dist/integration-icon.d.ts.map +1 -0
  99. package/dist/integration-icon.js +22 -0
  100. package/dist/integration-icon.js.map +1 -0
  101. package/dist/linear-callback.d.ts +17 -0
  102. package/dist/linear-callback.d.ts.map +1 -0
  103. package/dist/linear-callback.js +113 -0
  104. package/dist/linear-callback.js.map +1 -0
  105. package/dist/pages/gitea-form-errors.d.ts +11 -0
  106. package/dist/pages/gitea-form-errors.d.ts.map +1 -0
  107. package/dist/pages/gitea-form-errors.js +35 -0
  108. package/dist/pages/gitea-form-errors.js.map +1 -0
  109. package/dist/pages/gitea-install-page.d.ts +2 -0
  110. package/dist/pages/gitea-install-page.d.ts.map +1 -0
  111. package/dist/pages/gitea-install-page.js +139 -0
  112. package/dist/pages/gitea-install-page.js.map +1 -0
  113. package/dist/pages/github-install-page.d.ts +2 -0
  114. package/dist/pages/github-install-page.d.ts.map +1 -0
  115. package/dist/pages/github-install-page.js +11 -0
  116. package/dist/pages/github-install-page.js.map +1 -0
  117. package/dist/pages/linear-callback-page.d.ts +2 -0
  118. package/dist/pages/linear-callback-page.d.ts.map +1 -0
  119. package/dist/pages/linear-callback-page.js +192 -0
  120. package/dist/pages/linear-callback-page.js.map +1 -0
  121. package/dist/pages/linear-install-page.d.ts +2 -0
  122. package/dist/pages/linear-install-page.d.ts.map +1 -0
  123. package/dist/pages/linear-install-page.js +24 -0
  124. package/dist/pages/linear-install-page.js.map +1 -0
  125. package/dist/pages/sentry-callback-page.d.ts +2 -0
  126. package/dist/pages/sentry-callback-page.d.ts.map +1 -0
  127. package/dist/pages/sentry-callback-page.js +256 -0
  128. package/dist/pages/sentry-callback-page.js.map +1 -0
  129. package/dist/pages/sentry-install-page.d.ts +2 -0
  130. package/dist/pages/sentry-install-page.d.ts.map +1 -0
  131. package/dist/pages/sentry-install-page.js +17 -0
  132. package/dist/pages/sentry-install-page.js.map +1 -0
  133. package/dist/pages/source-control-onboarding-page.d.ts +2 -0
  134. package/dist/pages/source-control-onboarding-page.d.ts.map +1 -0
  135. package/dist/pages/source-control-onboarding-page.js +37 -0
  136. package/dist/pages/source-control-onboarding-page.js.map +1 -0
  137. package/dist/provider-catalog.d.ts +15 -0
  138. package/dist/provider-catalog.d.ts.map +1 -0
  139. package/dist/provider-catalog.js +28 -0
  140. package/dist/provider-catalog.js.map +1 -0
  141. package/dist/routes/gitea.d.ts +6 -0
  142. package/dist/routes/gitea.d.ts.map +1 -0
  143. package/dist/routes/gitea.js +7 -0
  144. package/dist/routes/gitea.js.map +1 -0
  145. package/dist/routes/github-callback.d.ts +6 -0
  146. package/dist/routes/github-callback.d.ts.map +1 -0
  147. package/dist/routes/github-callback.js +129 -0
  148. package/dist/routes/github-callback.js.map +1 -0
  149. package/dist/routes/github.d.ts +6 -0
  150. package/dist/routes/github.d.ts.map +1 -0
  151. package/dist/routes/github.js +7 -0
  152. package/dist/routes/github.js.map +1 -0
  153. package/dist/routes/integrations.d.ts +6 -0
  154. package/dist/routes/integrations.d.ts.map +1 -0
  155. package/dist/routes/integrations.js +7 -0
  156. package/dist/routes/integrations.js.map +1 -0
  157. package/dist/routes/linear-callback.d.ts +6 -0
  158. package/dist/routes/linear-callback.d.ts.map +1 -0
  159. package/dist/routes/linear-callback.js +7 -0
  160. package/dist/routes/linear-callback.js.map +1 -0
  161. package/dist/routes/linear.d.ts +6 -0
  162. package/dist/routes/linear.d.ts.map +1 -0
  163. package/dist/routes/linear.js +7 -0
  164. package/dist/routes/linear.js.map +1 -0
  165. package/dist/routes/sentry-callback.d.ts +6 -0
  166. package/dist/routes/sentry-callback.d.ts.map +1 -0
  167. package/dist/routes/sentry-callback.js +7 -0
  168. package/dist/routes/sentry-callback.js.map +1 -0
  169. package/dist/routes/sentry.d.ts +6 -0
  170. package/dist/routes/sentry.d.ts.map +1 -0
  171. package/dist/routes/sentry.js +7 -0
  172. package/dist/routes/sentry.js.map +1 -0
  173. package/dist/sentry-callback.d.ts +35 -0
  174. package/dist/sentry-callback.d.ts.map +1 -0
  175. package/dist/sentry-callback.js +132 -0
  176. package/dist/sentry-callback.js.map +1 -0
  177. package/dist/tsconfig.test.tsbuildinfo +1 -0
  178. package/package.json +120 -0
  179. package/src/components/connection-picker.tsx +89 -0
  180. package/src/components/installed-integrations-section.tsx +198 -0
  181. package/src/components/integration-delete-confirm-modal.tsx +62 -0
  182. package/src/components/integration-gallery-for-workspace.tsx +193 -0
  183. package/src/components/integration-gallery.stories.tsx +302 -0
  184. package/src/components/integration-gallery.test.tsx +692 -0
  185. package/src/components/integration-gallery.tsx +49 -0
  186. package/src/components/integration-usage-events.test.ts +80 -0
  187. package/src/components/integration-usage-events.ts +116 -0
  188. package/src/components/integration-usage-modal.tsx +178 -0
  189. package/src/components/provider-grid.tsx +163 -0
  190. package/src/components/redirect-install-page.test.tsx +98 -0
  191. package/src/components/redirect-install-page.tsx +68 -0
  192. package/src/components/repository-picker.tsx +102 -0
  193. package/src/components/webhook/copyable-value.test.tsx +41 -0
  194. package/src/components/webhook/copyable-value.tsx +72 -0
  195. package/src/components/webhook/webhook-create-modal.tsx +247 -0
  196. package/src/components/webhook/webhook-form-errors.test.ts +45 -0
  197. package/src/components/webhook/webhook-form-errors.ts +32 -0
  198. package/src/components/webhook/webhook-modals.stories.tsx +225 -0
  199. package/src/components/webhook/webhook-public-endpoint-alert.tsx +15 -0
  200. package/src/components/webhook/webhook-usage-details.tsx +44 -0
  201. package/src/connection-status-badge.tsx +38 -0
  202. package/src/feature.ts +47 -0
  203. package/src/hooks/api/integrations.test.ts +105 -0
  204. package/src/hooks/api/integrations.ts +287 -0
  205. package/src/hooks/api/webhook-connections.ts +115 -0
  206. package/src/index.ts +16 -0
  207. package/src/integration-icon.react.test.tsx +40 -0
  208. package/src/integration-icon.test.ts +19 -0
  209. package/src/integration-icon.tsx +26 -0
  210. package/src/linear-callback.test.ts +227 -0
  211. package/src/linear-callback.ts +136 -0
  212. package/src/pages/gitea-form-errors.test.ts +55 -0
  213. package/src/pages/gitea-form-errors.ts +36 -0
  214. package/src/pages/gitea-install-page.tsx +108 -0
  215. package/src/pages/github-install-page.tsx +11 -0
  216. package/src/pages/linear-callback-page.test.tsx +157 -0
  217. package/src/pages/linear-callback-page.tsx +175 -0
  218. package/src/pages/linear-install-page.tsx +26 -0
  219. package/src/pages/sentry-callback-page.test.tsx +205 -0
  220. package/src/pages/sentry-callback-page.tsx +238 -0
  221. package/src/pages/sentry-install-page.tsx +17 -0
  222. package/src/pages/source-control-onboarding-page.test.tsx +46 -0
  223. package/src/pages/source-control-onboarding-page.tsx +26 -0
  224. package/src/provider-catalog.test.ts +42 -0
  225. package/src/provider-catalog.ts +49 -0
  226. package/src/routes/gitea.tsx +4 -0
  227. package/src/routes/github-callback.tsx +114 -0
  228. package/src/routes/github.tsx +4 -0
  229. package/src/routes/integrations.tsx +4 -0
  230. package/src/routes/linear-callback.tsx +4 -0
  231. package/src/routes/linear.tsx +4 -0
  232. package/src/routes/sentry-callback.tsx +4 -0
  233. package/src/routes/sentry.tsx +4 -0
  234. package/src/sentry-callback.test.ts +239 -0
  235. package/src/sentry-callback.ts +142 -0
  236. package/test/render.tsx +111 -0
  237. package/test/setup.ts +3 -0
  238. package/tsconfig.build.json +9 -0
  239. package/tsconfig.build.tsbuildinfo +1 -0
  240. package/tsconfig.json +3 -0
  241. package/tsconfig.test.json +8 -0
  242. package/vercel.json +8 -0
  243. package/vitest.config.ts +75 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/installed-integrations-section.tsx"],"sourcesContent":["import type {\n IntegrationConnectionDto,\n ListIntegrationConnectionsResponseDto,\n} from '@shipfox/api-integration-core-dto';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {cn, formatDate} from '@shipfox/react-ui/utils';\nimport type {UseQueryResult} from '@tanstack/react-query';\nimport {Link} from '@tanstack/react-router';\nimport {ConnectionStatusBadge} from '#connection-status-badge.js';\nimport {IntegrationIcon} from '#integration-icon.js';\nimport {usageEventsForConnection} from './integration-usage-events.js';\n\ninterface InstalledIntegrationsSectionProps {\n connectionsQuery: UseQueryResult<ListIntegrationConnectionsResponseDto, Error>;\n connections: IntegrationConnectionDto[];\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (connection: IntegrationConnectionDto, active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}\n\nconst INSTALLED_SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function InstalledIntegrationsSection({\n connectionsQuery,\n connections,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: InstalledIntegrationsSectionProps) {\n return (\n <section className=\"flex flex-col gap-16\" aria-label=\"Installed integrations\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Installed integrations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Provider accounts installed in this workspace.\n </Text>\n </div>\n\n {connectionsQuery.isPending ? <InstalledSkeleton label=\"Loading integrations\" /> : null}\n\n {connectionsQuery.isError && connectionsQuery.data === undefined ? (\n <div className={cn(INSTALLED_SURFACE_CLASS, 'px-16')}>\n <QueryLoadError query={connectionsQuery} subject=\"integrations\" />\n </div>\n ) : null}\n\n {connectionsQuery.data !== undefined && connections.length === 0 ? (\n <div className={cn(INSTALLED_SURFACE_CLASS, 'px-16')}>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations installed yet\"\n description=\"Install a provider below to get started.\"\n />\n </div>\n ) : null}\n\n {connections.length > 0 ? (\n <ul className={cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS)}>\n {connections.map((connection) => (\n <InstalledRow\n key={connection.id}\n connection={connection}\n isMutating={isMutating}\n onUse={onUse}\n onSetActive={(nextActive) => onSetActive(connection, nextActive)}\n onDelete={onDelete}\n providerDisplayName={providerDisplayName}\n />\n ))}\n </ul>\n ) : null}\n </section>\n );\n}\n\nfunction InstalledRow({\n connection,\n isMutating,\n onUse,\n onSetActive,\n onDelete,\n providerDisplayName,\n}: {\n connection: IntegrationConnectionDto;\n isMutating: boolean;\n onUse: (connectionId: string) => void;\n onSetActive: (active: boolean) => void;\n onDelete: (connectionId: string) => void;\n providerDisplayName: (provider: string) => string | undefined;\n}) {\n const muted = connection.lifecycle_status === 'disabled';\n const active = connection.lifecycle_status === 'active';\n const recentEventsEvent = usageEventsForConnection(connection)[0]?.value ?? 'received';\n const providerName = providerDisplayName(connection.provider);\n\n return (\n <li className=\"flex items-center gap-12 px-16 py-12 transition-colors hover:bg-background-components-hover\">\n <IntegrationIcon\n source={connection.provider}\n aria-hidden\n className={cn(\n 'size-24 shrink-0',\n muted ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-base',\n )}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <div className=\"flex min-w-0 items-center gap-8\">\n <Text\n size=\"md\"\n bold\n className={cn('truncate', muted ? 'text-foreground-neutral-disabled' : undefined)}\n >\n {connection.display_name}\n </Text>\n <ConnectionStatusBadge status={connection.lifecycle_status} className=\"shrink-0\" />\n </div>\n <Text size=\"sm\" className=\"truncate text-foreground-neutral-muted\">\n Added {formatDate(connection.created_at)}\n </Text>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Open ${connection.display_name} integration actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {connection.external_url ? (\n <DropdownMenuItem asChild>\n <a href={connection.external_url} target=\"_blank\" rel=\"noreferrer noopener\">\n {providerName ? `Open in ${providerName}` : 'Open provider settings'}\n </a>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem onSelect={() => onUse(connection.id)}>\n Use this integration\n </DropdownMenuItem>\n <DropdownMenuItem asChild>\n <Link\n to=\"/workspaces/$wid/settings/events\"\n params={{wid: connection.workspace_id}}\n search={{source: [connection.slug], event: [recentEventsEvent]}}\n >\n View recent events\n </Link>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled={isMutating} onSelect={() => onSetActive(!active)}>\n {active ? 'Disable integration' : 'Enable integration'}\n </DropdownMenuItem>\n <DropdownMenuItem disabled={isMutating} onSelect={() => onDelete(connection.id)}>\n Delete integration\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </li>\n );\n}\n\nfunction InstalledSkeleton({label}: {label: string}) {\n return (\n <ul\n role=\"status\"\n aria-label={label}\n className={cn('divide-y divide-border-neutral-base', INSTALLED_SURFACE_CLASS)}\n >\n {[0, 1, 2].map((row) => (\n <li key={row} className=\"flex items-center gap-12 px-16 py-12\">\n <Skeleton className=\"size-24 shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-12 w-80\" />\n </div>\n <Skeleton className=\"h-20 w-72 shrink-0\" />\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IconButton","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","EmptyState","Skeleton","Header","Text","cn","formatDate","Link","ConnectionStatusBadge","IntegrationIcon","usageEventsForConnection","INSTALLED_SURFACE_CLASS","InstalledIntegrationsSection","connectionsQuery","connections","isMutating","onUse","onSetActive","onDelete","providerDisplayName","section","className","aria-label","div","variant","size","isPending","InstalledSkeleton","label","isError","data","undefined","query","subject","length","icon","title","description","ul","map","connection","InstalledRow","nextActive","id","muted","lifecycle_status","active","recentEventsEvent","value","providerName","provider","li","source","aria-hidden","bold","display_name","status","created_at","asChild","align","external_url","a","href","target","rel","onSelect","to","params","wid","workspace_id","search","slug","event","disabled","role","row"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ interface IntegrationDeleteConfirmModalProps {
2
+ connectionName: string | undefined;
3
+ open: boolean;
4
+ isPending: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ onConfirm: () => void;
7
+ }
8
+ export declare function IntegrationDeleteConfirmModal({ connectionName, open, isPending, onOpenChange, onConfirm, }: IntegrationDeleteConfirmModalProps): import("react").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=integration-delete-confirm-modal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-delete-confirm-modal.d.ts","sourceRoot":"","sources":["../../src/components/integration-delete-confirm-modal.tsx"],"names":[],"mappings":"AAWA,UAAU,kCAAkC;IAC1C,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,cAAc,EACd,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE,kCAAkC,+BAoCpC"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@shipfox/react-ui/button';
3
+ import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
4
+ import { Text } from '@shipfox/react-ui/typography';
5
+ export function IntegrationDeleteConfirmModal({ connectionName, open, isPending, onOpenChange, onConfirm }) {
6
+ const name = connectionName ?? 'this integration';
7
+ return /*#__PURE__*/ _jsx(Modal, {
8
+ open: open,
9
+ onOpenChange: (nextOpen)=>{
10
+ if (isPending && !nextOpen) return;
11
+ onOpenChange(nextOpen);
12
+ },
13
+ children: /*#__PURE__*/ _jsxs(ModalContent, {
14
+ "aria-describedby": undefined,
15
+ children: [
16
+ /*#__PURE__*/ _jsx(ModalTitle, {
17
+ className: "sr-only",
18
+ children: "Delete integration"
19
+ }),
20
+ /*#__PURE__*/ _jsx(ModalHeader, {
21
+ title: "Delete integration",
22
+ showClose: !isPending
23
+ }),
24
+ /*#__PURE__*/ _jsx(ModalBody, {
25
+ className: "gap-16",
26
+ children: /*#__PURE__*/ _jsxs(Text, {
27
+ size: "sm",
28
+ children: [
29
+ "Are you sure you want to delete ",
30
+ /*#__PURE__*/ _jsx("strong", {
31
+ className: "font-medium",
32
+ children: name
33
+ }),
34
+ "? Once deleted, Shipfox will immediately stop processing events from this integration. This cannot be undone."
35
+ ]
36
+ })
37
+ }),
38
+ /*#__PURE__*/ _jsxs(ModalFooter, {
39
+ children: [
40
+ /*#__PURE__*/ _jsx(Button, {
41
+ type: "button",
42
+ variant: "secondary",
43
+ disabled: isPending,
44
+ onClick: ()=>onOpenChange(false),
45
+ children: "Cancel"
46
+ }),
47
+ /*#__PURE__*/ _jsx(Button, {
48
+ type: "button",
49
+ variant: "danger",
50
+ isLoading: isPending,
51
+ onClick: onConfirm,
52
+ children: "Delete integration"
53
+ })
54
+ ]
55
+ })
56
+ ]
57
+ })
58
+ });
59
+ }
60
+
61
+ //# sourceMappingURL=integration-delete-confirm-modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/integration-delete-confirm-modal.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {Text} from '@shipfox/react-ui/typography';\n\ninterface IntegrationDeleteConfirmModalProps {\n connectionName: string | undefined;\n open: boolean;\n isPending: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void;\n}\n\nexport function IntegrationDeleteConfirmModal({\n connectionName,\n open,\n isPending,\n onOpenChange,\n onConfirm,\n}: IntegrationDeleteConfirmModalProps) {\n const name = connectionName ?? 'this integration';\n return (\n <Modal\n open={open}\n onOpenChange={(nextOpen) => {\n if (isPending && !nextOpen) return;\n onOpenChange(nextOpen);\n }}\n >\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Delete integration</ModalTitle>\n <ModalHeader title=\"Delete integration\" showClose={!isPending} />\n <ModalBody className=\"gap-16\">\n <Text size=\"sm\">\n Are you sure you want to delete <strong className=\"font-medium\">{name}</strong>? Once\n deleted, Shipfox will immediately stop processing events from this integration. This\n cannot be undone.\n </Text>\n </ModalBody>\n <ModalFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={isPending}\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button type=\"button\" variant=\"danger\" isLoading={isPending} onClick={onConfirm}>\n Delete integration\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n );\n}\n"],"names":["Button","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Text","IntegrationDeleteConfirmModal","connectionName","open","isPending","onOpenChange","onConfirm","name","nextOpen","aria-describedby","undefined","className","title","showClose","size","strong","type","variant","disabled","onClick","isLoading"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SACEC,KAAK,EACLC,SAAS,EACTC,YAAY,EACZC,WAAW,EACXC,WAAW,EACXC,UAAU,QACL,0BAA0B;AACjC,SAAQC,IAAI,QAAO,+BAA+B;AAUlD,OAAO,SAASC,8BAA8B,EAC5CC,cAAc,EACdC,IAAI,EACJC,SAAS,EACTC,YAAY,EACZC,SAAS,EAC0B;IACnC,MAAMC,OAAOL,kBAAkB;IAC/B,qBACE,KAACR;QACCS,MAAMA;QACNE,cAAc,CAACG;YACb,IAAIJ,aAAa,CAACI,UAAU;YAC5BH,aAAaG;QACf;kBAEA,cAAA,MAACZ;YAAaa,oBAAkBC;;8BAC9B,KAACX;oBAAWY,WAAU;8BAAU;;8BAChC,KAACb;oBAAYc,OAAM;oBAAqBC,WAAW,CAACT;;8BACpD,KAACT;oBAAUgB,WAAU;8BACnB,cAAA,MAACX;wBAAKc,MAAK;;4BAAK;0CACkB,KAACC;gCAAOJ,WAAU;0CAAeJ;;4BAAc;;;;8BAKnF,MAACV;;sCACC,KAACJ;4BACCuB,MAAK;4BACLC,SAAQ;4BACRC,UAAUd;4BACVe,SAAS,IAAMd,aAAa;sCAC7B;;sCAGD,KAACZ;4BAAOuB,MAAK;4BAASC,SAAQ;4BAASG,WAAWhB;4BAAWe,SAASb;sCAAW;;;;;;;AAO3F"}
@@ -0,0 +1,9 @@
1
+ import type { IntegrationCapabilityDto } from '@shipfox/api-integration-core-dto';
2
+ interface IntegrationGalleryForWorkspaceProps {
3
+ capability: IntegrationCapabilityDto | undefined;
4
+ emptyProvidersMessage: string;
5
+ workspaceId: string;
6
+ }
7
+ export declare function IntegrationGalleryForWorkspace({ capability, emptyProvidersMessage, workspaceId, }: IntegrationGalleryForWorkspaceProps): import("react").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=integration-gallery-for-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-gallery-for-workspace.d.ts","sourceRoot":"","sources":["../../src/components/integration-gallery-for-workspace.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,mCAAmC,CAAC;AAsB3C,UAAU,mCAAmC;IAC3C,UAAU,EAAE,wBAAwB,GAAG,SAAS,CAAC;IACjD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,8BAA8B,CAAC,EAC7C,UAAU,EACV,qBAAqB,EACrB,WAAW,GACZ,EAAE,mCAAmC,+BA4JrC"}
@@ -0,0 +1,169 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { toast } from '@shipfox/react-ui/toast';
3
+ import { Header, Text } from '@shipfox/react-ui/typography';
4
+ import { useState } from 'react';
5
+ import { useDeleteIntegrationConnectionMutation, useIntegrationConnectionsQuery, useIntegrationProvidersQuery, useUpdateIntegrationConnectionMutation } from '#hooks/api/integrations.js';
6
+ import { useDeleteWebhookConnectionMutation, useUpdateWebhookConnectionMutation } from '#hooks/api/webhook-connections.js';
7
+ import { InstalledIntegrationsSection } from './installed-integrations-section.js';
8
+ import { IntegrationDeleteConfirmModal } from './integration-delete-confirm-modal.js';
9
+ import { usageEventsForConnection } from './integration-usage-events.js';
10
+ import { IntegrationUsageModal } from './integration-usage-modal.js';
11
+ import { ProviderGrid } from './provider-grid.js';
12
+ import { WebhookCreateModal } from './webhook/webhook-create-modal.js';
13
+ import { WebhookUsageDetails } from './webhook/webhook-usage-details.js';
14
+ export function IntegrationGalleryForWorkspace({ capability, emptyProvidersMessage, workspaceId }) {
15
+ const [createProvider, setCreateProvider] = useState();
16
+ const [usageConnectionId, setUsageConnectionId] = useState();
17
+ const [createdUsageConnection, setCreatedUsageConnection] = useState();
18
+ const [deleteConnectionId, setDeleteConnectionId] = useState();
19
+ const providersQuery = useIntegrationProvidersQuery(capability ? {
20
+ capability
21
+ } : undefined);
22
+ const connectionsQuery = useIntegrationConnectionsQuery(workspaceId);
23
+ const updateConnection = useUpdateIntegrationConnectionMutation();
24
+ const deleteConnection = useDeleteIntegrationConnectionMutation();
25
+ const updateWebhookConnection = useUpdateWebhookConnectionMutation();
26
+ const deleteWebhookConnection = useDeleteWebhookConnectionMutation();
27
+ const providers = providersQuery.data?.providers ?? [];
28
+ const providersMap = new Map(providers.map((provider)=>[
29
+ provider.provider,
30
+ provider
31
+ ]));
32
+ const allConnections = connectionsQuery.data?.connections ?? [];
33
+ // Filter in memory so the all-status cache key stays shared; passing capability
34
+ // into the hook would collide with the active-only `source_control` key used
35
+ // elsewhere.
36
+ const connections = capability ? allConnections.filter((connection)=>connection.capabilities.includes(capability)) : allConnections;
37
+ const providerDisplayName = (provider)=>providersMap.get(provider)?.display_name;
38
+ const providerLabel = (provider)=>providerDisplayName(provider) ?? provider;
39
+ const sortedConnections = [
40
+ ...connections
41
+ ].sort((a, b)=>{
42
+ const byProvider = providerLabel(a.provider).localeCompare(providerLabel(b.provider));
43
+ if (byProvider !== 0) return byProvider;
44
+ return a.created_at.localeCompare(b.created_at);
45
+ });
46
+ const usageConnection = sortedConnections.find((connection)=>connection.id === usageConnectionId) ?? (createdUsageConnection?.id === usageConnectionId ? createdUsageConnection : null) ?? null;
47
+ const deleteConnectionTarget = sortedConnections.find((connection)=>connection.id === deleteConnectionId);
48
+ async function setConnectionActive(connection, active) {
49
+ try {
50
+ const body = {
51
+ lifecycle_status: active ? 'active' : 'disabled'
52
+ };
53
+ if (connection.provider === 'webhook') {
54
+ await updateWebhookConnection.mutateAsync({
55
+ workspaceId,
56
+ connectionId: connection.id,
57
+ body
58
+ });
59
+ } else {
60
+ await updateConnection.mutateAsync({
61
+ connectionId: connection.id,
62
+ body
63
+ });
64
+ }
65
+ toast.success(active ? 'Integration enabled.' : 'Integration disabled.');
66
+ } catch (error) {
67
+ toast.error(error instanceof Error ? error.message : 'Could not update integration.');
68
+ }
69
+ }
70
+ async function confirmDeleteConnection() {
71
+ if (!deleteConnectionTarget) return;
72
+ try {
73
+ if (deleteConnectionTarget.provider === 'webhook') {
74
+ await deleteWebhookConnection.mutateAsync({
75
+ workspaceId,
76
+ connectionId: deleteConnectionTarget.id
77
+ });
78
+ } else {
79
+ await deleteConnection.mutateAsync({
80
+ workspaceId,
81
+ connectionId: deleteConnectionTarget.id
82
+ });
83
+ }
84
+ toast.success('Integration deleted.');
85
+ setDeleteConnectionId(undefined);
86
+ } catch (error) {
87
+ toast.error(error instanceof Error ? error.message : 'Could not delete integration.');
88
+ }
89
+ }
90
+ return /*#__PURE__*/ _jsxs("div", {
91
+ className: "flex flex-col gap-24",
92
+ children: [
93
+ /*#__PURE__*/ _jsx(InstalledIntegrationsSection, {
94
+ connectionsQuery: connectionsQuery,
95
+ connections: sortedConnections,
96
+ isMutating: updateConnection.isPending || deleteConnection.isPending || updateWebhookConnection.isPending || deleteWebhookConnection.isPending,
97
+ onUse: setUsageConnectionId,
98
+ onSetActive: (connection, active)=>{
99
+ void setConnectionActive(connection, active);
100
+ },
101
+ onDelete: setDeleteConnectionId,
102
+ providerDisplayName: providerDisplayName
103
+ }),
104
+ /*#__PURE__*/ _jsxs("section", {
105
+ className: "flex flex-col gap-16",
106
+ "aria-label": "Available integrations",
107
+ children: [
108
+ /*#__PURE__*/ _jsxs("div", {
109
+ className: "flex flex-col gap-4",
110
+ children: [
111
+ /*#__PURE__*/ _jsx(Header, {
112
+ variant: "h3",
113
+ children: "Available integrations"
114
+ }),
115
+ /*#__PURE__*/ _jsx(Text, {
116
+ size: "sm",
117
+ className: "text-foreground-neutral-muted",
118
+ children: "Providers available to install in this workspace."
119
+ })
120
+ ]
121
+ }),
122
+ /*#__PURE__*/ _jsx(ProviderGrid, {
123
+ providersQuery: providersQuery,
124
+ workspaceId: workspaceId,
125
+ emptyMessage: emptyProvidersMessage,
126
+ onOpenProvider: setCreateProvider
127
+ })
128
+ ]
129
+ }),
130
+ /*#__PURE__*/ _jsx(WebhookCreateModal, {
131
+ workspaceId: workspaceId,
132
+ open: createProvider === 'webhook',
133
+ onOpenChange: (open)=>setCreateProvider(open ? 'webhook' : undefined),
134
+ onCreated: (connection)=>{
135
+ setCreatedUsageConnection(connection);
136
+ setUsageConnectionId(connection.id);
137
+ }
138
+ }),
139
+ /*#__PURE__*/ _jsx(IntegrationUsageModal, {
140
+ connection: usageConnection,
141
+ events: usageConnection ? usageEventsForConnection(usageConnection) : [],
142
+ open: usageConnection !== null,
143
+ onOpenChange: (open)=>{
144
+ if (!open) {
145
+ setUsageConnectionId(undefined);
146
+ setCreatedUsageConnection(undefined);
147
+ }
148
+ },
149
+ children: usageConnection?.provider === 'webhook' ? /*#__PURE__*/ _jsx(WebhookUsageDetails, {
150
+ workspaceId: workspaceId,
151
+ connectionId: usageConnection.id
152
+ }) : null
153
+ }),
154
+ /*#__PURE__*/ _jsx(IntegrationDeleteConfirmModal, {
155
+ connectionName: deleteConnectionTarget?.display_name,
156
+ open: deleteConnectionId !== undefined,
157
+ isPending: deleteConnection.isPending || deleteWebhookConnection.isPending,
158
+ onOpenChange: (open)=>{
159
+ if (!open) setDeleteConnectionId(undefined);
160
+ },
161
+ onConfirm: ()=>{
162
+ void confirmDeleteConnection();
163
+ }
164
+ })
165
+ ]
166
+ });
167
+ }
168
+
169
+ //# sourceMappingURL=integration-gallery-for-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/integration-gallery-for-workspace.tsx"],"sourcesContent":["import type {\n IntegrationCapabilityDto,\n IntegrationConnectionDto,\n IntegrationProviderDto,\n} from '@shipfox/api-integration-core-dto';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport {\n useDeleteIntegrationConnectionMutation,\n useIntegrationConnectionsQuery,\n useIntegrationProvidersQuery,\n useUpdateIntegrationConnectionMutation,\n} from '#hooks/api/integrations.js';\nimport {\n useDeleteWebhookConnectionMutation,\n useUpdateWebhookConnectionMutation,\n} from '#hooks/api/webhook-connections.js';\nimport {InstalledIntegrationsSection} from './installed-integrations-section.js';\nimport {IntegrationDeleteConfirmModal} from './integration-delete-confirm-modal.js';\nimport {usageEventsForConnection} from './integration-usage-events.js';\nimport {IntegrationUsageModal} from './integration-usage-modal.js';\nimport {ProviderGrid} from './provider-grid.js';\nimport {WebhookCreateModal} from './webhook/webhook-create-modal.js';\nimport {WebhookUsageDetails} from './webhook/webhook-usage-details.js';\n\ninterface IntegrationGalleryForWorkspaceProps {\n capability: IntegrationCapabilityDto | undefined;\n emptyProvidersMessage: string;\n workspaceId: string;\n}\n\nexport function IntegrationGalleryForWorkspace({\n capability,\n emptyProvidersMessage,\n workspaceId,\n}: IntegrationGalleryForWorkspaceProps) {\n const [createProvider, setCreateProvider] = useState<string | undefined>();\n const [usageConnectionId, setUsageConnectionId] = useState<string | undefined>();\n const [createdUsageConnection, setCreatedUsageConnection] = useState<\n IntegrationConnectionDto | undefined\n >();\n const [deleteConnectionId, setDeleteConnectionId] = useState<string | undefined>();\n const providersQuery = useIntegrationProvidersQuery(capability ? {capability} : undefined);\n const connectionsQuery = useIntegrationConnectionsQuery(workspaceId);\n const updateConnection = useUpdateIntegrationConnectionMutation();\n const deleteConnection = useDeleteIntegrationConnectionMutation();\n const updateWebhookConnection = useUpdateWebhookConnectionMutation();\n const deleteWebhookConnection = useDeleteWebhookConnectionMutation();\n\n const providers = providersQuery.data?.providers ?? [];\n const providersMap = new Map<string, IntegrationProviderDto>(\n providers.map((provider) => [provider.provider, provider]),\n );\n\n const allConnections = connectionsQuery.data?.connections ?? [];\n // Filter in memory so the all-status cache key stays shared; passing capability\n // into the hook would collide with the active-only `source_control` key used\n // elsewhere.\n const connections = capability\n ? allConnections.filter((connection) => connection.capabilities.includes(capability))\n : allConnections;\n\n const providerDisplayName = (provider: string) => providersMap.get(provider)?.display_name;\n const providerLabel = (provider: string) => providerDisplayName(provider) ?? provider;\n\n const sortedConnections = [...connections].sort((a, b) => {\n const byProvider = providerLabel(a.provider).localeCompare(providerLabel(b.provider));\n if (byProvider !== 0) return byProvider;\n return a.created_at.localeCompare(b.created_at);\n });\n const usageConnection =\n sortedConnections.find((connection) => connection.id === usageConnectionId) ??\n (createdUsageConnection?.id === usageConnectionId ? createdUsageConnection : null) ??\n null;\n const deleteConnectionTarget = sortedConnections.find(\n (connection) => connection.id === deleteConnectionId,\n );\n\n async function setConnectionActive(connection: IntegrationConnectionDto, active: boolean) {\n try {\n const body = {lifecycle_status: active ? 'active' : 'disabled'} as const;\n if (connection.provider === 'webhook') {\n await updateWebhookConnection.mutateAsync({\n workspaceId,\n connectionId: connection.id,\n body,\n });\n } else {\n await updateConnection.mutateAsync({\n connectionId: connection.id,\n body,\n });\n }\n toast.success(active ? 'Integration enabled.' : 'Integration disabled.');\n } catch (error) {\n toast.error(error instanceof Error ? error.message : 'Could not update integration.');\n }\n }\n\n async function confirmDeleteConnection() {\n if (!deleteConnectionTarget) return;\n try {\n if (deleteConnectionTarget.provider === 'webhook') {\n await deleteWebhookConnection.mutateAsync({\n workspaceId,\n connectionId: deleteConnectionTarget.id,\n });\n } else {\n await deleteConnection.mutateAsync({\n workspaceId,\n connectionId: deleteConnectionTarget.id,\n });\n }\n toast.success('Integration deleted.');\n setDeleteConnectionId(undefined);\n } catch (error) {\n toast.error(error instanceof Error ? error.message : 'Could not delete integration.');\n }\n }\n\n return (\n <div className=\"flex flex-col gap-24\">\n <InstalledIntegrationsSection\n connectionsQuery={connectionsQuery}\n connections={sortedConnections}\n isMutating={\n updateConnection.isPending ||\n deleteConnection.isPending ||\n updateWebhookConnection.isPending ||\n deleteWebhookConnection.isPending\n }\n onUse={setUsageConnectionId}\n onSetActive={(connection, active) => {\n void setConnectionActive(connection, active);\n }}\n onDelete={setDeleteConnectionId}\n providerDisplayName={providerDisplayName}\n />\n\n <section className=\"flex flex-col gap-16\" aria-label=\"Available integrations\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Available integrations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Providers available to install in this workspace.\n </Text>\n </div>\n\n <ProviderGrid\n providersQuery={providersQuery}\n workspaceId={workspaceId}\n emptyMessage={emptyProvidersMessage}\n onOpenProvider={setCreateProvider}\n />\n </section>\n <WebhookCreateModal\n workspaceId={workspaceId}\n open={createProvider === 'webhook'}\n onOpenChange={(open) => setCreateProvider(open ? 'webhook' : undefined)}\n onCreated={(connection) => {\n setCreatedUsageConnection(connection);\n setUsageConnectionId(connection.id);\n }}\n />\n <IntegrationUsageModal\n connection={usageConnection}\n events={usageConnection ? usageEventsForConnection(usageConnection) : []}\n open={usageConnection !== null}\n onOpenChange={(open) => {\n if (!open) {\n setUsageConnectionId(undefined);\n setCreatedUsageConnection(undefined);\n }\n }}\n >\n {usageConnection?.provider === 'webhook' ? (\n <WebhookUsageDetails workspaceId={workspaceId} connectionId={usageConnection.id} />\n ) : null}\n </IntegrationUsageModal>\n <IntegrationDeleteConfirmModal\n connectionName={deleteConnectionTarget?.display_name}\n open={deleteConnectionId !== undefined}\n isPending={deleteConnection.isPending || deleteWebhookConnection.isPending}\n onOpenChange={(open) => {\n if (!open) setDeleteConnectionId(undefined);\n }}\n onConfirm={() => {\n void confirmDeleteConnection();\n }}\n />\n </div>\n );\n}\n"],"names":["toast","Header","Text","useState","useDeleteIntegrationConnectionMutation","useIntegrationConnectionsQuery","useIntegrationProvidersQuery","useUpdateIntegrationConnectionMutation","useDeleteWebhookConnectionMutation","useUpdateWebhookConnectionMutation","InstalledIntegrationsSection","IntegrationDeleteConfirmModal","usageEventsForConnection","IntegrationUsageModal","ProviderGrid","WebhookCreateModal","WebhookUsageDetails","IntegrationGalleryForWorkspace","capability","emptyProvidersMessage","workspaceId","createProvider","setCreateProvider","usageConnectionId","setUsageConnectionId","createdUsageConnection","setCreatedUsageConnection","deleteConnectionId","setDeleteConnectionId","providersQuery","undefined","connectionsQuery","updateConnection","deleteConnection","updateWebhookConnection","deleteWebhookConnection","providers","data","providersMap","Map","map","provider","allConnections","connections","filter","connection","capabilities","includes","providerDisplayName","get","display_name","providerLabel","sortedConnections","sort","a","b","byProvider","localeCompare","created_at","usageConnection","find","id","deleteConnectionTarget","setConnectionActive","active","body","lifecycle_status","mutateAsync","connectionId","success","error","Error","message","confirmDeleteConnection","div","className","isMutating","isPending","onUse","onSetActive","onDelete","section","aria-label","variant","size","emptyMessage","onOpenProvider","open","onOpenChange","onCreated","events","connectionName","onConfirm"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { IntegrationCapabilityDto } from '@shipfox/api-integration-core-dto';
2
+ export interface IntegrationGalleryProps {
3
+ capability?: IntegrationCapabilityDto;
4
+ emptyProvidersMessage?: string;
5
+ workspaceId?: string;
6
+ }
7
+ export declare function IntegrationGallery({ capability, emptyProvidersMessage, workspaceId, }: IntegrationGalleryProps): import("react").JSX.Element;
8
+ //# sourceMappingURL=integration-gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-gallery.d.ts","sourceRoot":"","sources":["../../src/components/integration-gallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,mCAAmC,CAAC;AAIhF,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,qBAAmF,EACnF,WAAW,GACZ,EAAE,uBAAuB,+BAiBzB"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useActiveWorkspace } from '@shipfox/client-auth';
3
+ import { IntegrationGalleryForWorkspace } from './integration-gallery-for-workspace.js';
4
+ export function IntegrationGallery({ capability, emptyProvidersMessage = 'Enable at least one provider in the application settings.', workspaceId }) {
5
+ if (workspaceId) {
6
+ return /*#__PURE__*/ _jsx(IntegrationGalleryForWorkspace, {
7
+ workspaceId: workspaceId,
8
+ capability: capability,
9
+ emptyProvidersMessage: emptyProvidersMessage
10
+ });
11
+ }
12
+ return /*#__PURE__*/ _jsx(RoutedIntegrationGallery, {
13
+ capability: capability,
14
+ emptyProvidersMessage: emptyProvidersMessage
15
+ });
16
+ }
17
+ function RoutedIntegrationGallery({ capability, emptyProvidersMessage }) {
18
+ const workspace = useActiveWorkspace();
19
+ return /*#__PURE__*/ _jsx(IntegrationGalleryForWorkspace, {
20
+ workspaceId: workspace.id,
21
+ capability: capability,
22
+ emptyProvidersMessage: emptyProvidersMessage
23
+ });
24
+ }
25
+
26
+ //# sourceMappingURL=integration-gallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/integration-gallery.tsx"],"sourcesContent":["import type {IntegrationCapabilityDto} from '@shipfox/api-integration-core-dto';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {IntegrationGalleryForWorkspace} from './integration-gallery-for-workspace.js';\n\nexport interface IntegrationGalleryProps {\n capability?: IntegrationCapabilityDto;\n emptyProvidersMessage?: string;\n workspaceId?: string;\n}\n\nexport function IntegrationGallery({\n capability,\n emptyProvidersMessage = 'Enable at least one provider in the application settings.',\n workspaceId,\n}: IntegrationGalleryProps) {\n if (workspaceId) {\n return (\n <IntegrationGalleryForWorkspace\n workspaceId={workspaceId}\n capability={capability}\n emptyProvidersMessage={emptyProvidersMessage}\n />\n );\n }\n\n return (\n <RoutedIntegrationGallery\n capability={capability}\n emptyProvidersMessage={emptyProvidersMessage}\n />\n );\n}\n\nfunction RoutedIntegrationGallery({\n capability,\n emptyProvidersMessage,\n}: {\n capability: IntegrationCapabilityDto | undefined;\n emptyProvidersMessage: string;\n}) {\n const workspace = useActiveWorkspace();\n return (\n <IntegrationGalleryForWorkspace\n workspaceId={workspace.id}\n capability={capability}\n emptyProvidersMessage={emptyProvidersMessage}\n />\n );\n}\n"],"names":["useActiveWorkspace","IntegrationGalleryForWorkspace","IntegrationGallery","capability","emptyProvidersMessage","workspaceId","RoutedIntegrationGallery","workspace","id"],"mappings":";AACA,SAAQA,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,8BAA8B,QAAO,yCAAyC;AAQtF,OAAO,SAASC,mBAAmB,EACjCC,UAAU,EACVC,wBAAwB,2DAA2D,EACnFC,WAAW,EACa;IACxB,IAAIA,aAAa;QACf,qBACE,KAACJ;YACCI,aAAaA;YACbF,YAAYA;YACZC,uBAAuBA;;IAG7B;IAEA,qBACE,KAACE;QACCH,YAAYA;QACZC,uBAAuBA;;AAG7B;AAEA,SAASE,yBAAyB,EAChCH,UAAU,EACVC,qBAAqB,EAItB;IACC,MAAMG,YAAYP;IAClB,qBACE,KAACC;QACCI,aAAaE,UAAUC,EAAE;QACzBL,YAAYA;QACZC,uBAAuBA;;AAG7B"}
@@ -0,0 +1,26 @@
1
+ import type { StoryObj } from '@storybook/react';
2
+ type Scenario = 'mixed' | 'empty-connections' | 'loading' | 'connections-error' | 'providers-error' | 'no-providers' | 'long-names';
3
+ interface IntegrationGalleryStoryProps {
4
+ scenario: Scenario;
5
+ }
6
+ declare function IntegrationGalleryStory({ scenario }: IntegrationGalleryStoryProps): import("react").JSX.Element;
7
+ declare const meta: {
8
+ title: string;
9
+ component: typeof IntegrationGalleryStory;
10
+ parameters: {
11
+ layout: string;
12
+ };
13
+ args: {
14
+ scenario: "mixed";
15
+ };
16
+ };
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+ export declare const Playground: Story;
20
+ export declare const EmptyConnections: Story;
21
+ export declare const Loading: Story;
22
+ export declare const ConnectionsError: Story;
23
+ export declare const ProvidersError: Story;
24
+ export declare const NoProvidersAvailable: Story;
25
+ export declare const LongNames: Story;
26
+ //# sourceMappingURL=integration-gallery.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-gallery.stories.d.ts","sourceRoot":"","sources":["../../src/components/integration-gallery.stories.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAO,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAwBrD,KAAK,QAAQ,GACT,OAAO,GACP,mBAAmB,GACnB,SAAS,GACT,mBAAmB,GACnB,iBAAiB,GACjB,cAAc,GACd,YAAY,CAAC;AAEjB,UAAU,4BAA4B;IACpC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAUD,iBAAS,uBAAuB,CAAC,EAAC,QAAQ,EAAC,EAAE,4BAA4B,+BA0DxE;AAED,QAAA,MAAM,IAAI;;;;;;;;;CAKsC,CAAC;AAEjD,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,UAAU,EAAE,KAAU,CAAC;AAEpC,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAElC,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC"}