@shipfox/client-integrations 13.0.0 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/dist/application/complete-integration-callback.d.ts +11 -0
  4. package/dist/application/complete-integration-callback.d.ts.map +1 -1
  5. package/dist/application/complete-integration-callback.js +38 -8
  6. package/dist/application/complete-integration-callback.js.map +1 -1
  7. package/dist/components/callback-status-shell.d.ts +1 -1
  8. package/dist/components/callback-status-shell.d.ts.map +1 -1
  9. package/dist/components/callback-status-shell.js +3 -3
  10. package/dist/components/callback-status-shell.js.map +1 -1
  11. package/dist/components/connection-picker.js +3 -3
  12. package/dist/components/connection-picker.js.map +1 -1
  13. package/dist/components/installed-integrations-section.js +9 -9
  14. package/dist/components/installed-integrations-section.js.map +1 -1
  15. package/dist/components/integration-delete-confirm-modal.js +1 -1
  16. package/dist/components/integration-delete-confirm-modal.js.map +1 -1
  17. package/dist/components/integration-gallery-for-workspace.js +3 -3
  18. package/dist/components/integration-gallery-for-workspace.js.map +1 -1
  19. package/dist/components/integration-usage-modal.js +6 -6
  20. package/dist/components/integration-usage-modal.js.map +1 -1
  21. package/dist/components/provider-grid.d.ts +1 -1
  22. package/dist/components/provider-grid.d.ts.map +1 -1
  23. package/dist/components/provider-grid.js +10 -10
  24. package/dist/components/provider-grid.js.map +1 -1
  25. package/dist/components/redirect-install-page.js +1 -1
  26. package/dist/components/redirect-install-page.js.map +1 -1
  27. package/dist/components/repository-picker.js +4 -4
  28. package/dist/components/repository-picker.js.map +1 -1
  29. package/dist/components/webhook/copyable-value.js +3 -3
  30. package/dist/components/webhook/copyable-value.js.map +1 -1
  31. package/dist/components/webhook/webhook-create-modal.js +1 -1
  32. package/dist/components/webhook/webhook-create-modal.js.map +1 -1
  33. package/dist/components/webhook/webhook-usage-details.js +2 -2
  34. package/dist/components/webhook/webhook-usage-details.js.map +1 -1
  35. package/dist/core/models.d.ts +6 -0
  36. package/dist/core/models.d.ts.map +1 -1
  37. package/dist/core/models.js +12 -0
  38. package/dist/core/models.js.map +1 -1
  39. package/dist/feature.d.ts +8 -0
  40. package/dist/feature.d.ts.map +1 -1
  41. package/dist/feature.js +10 -0
  42. package/dist/feature.js.map +1 -1
  43. package/dist/hooks/api/integration-mapper.d.ts +3 -1
  44. package/dist/hooks/api/integration-mapper.d.ts.map +1 -1
  45. package/dist/hooks/api/integration-mapper.js +8 -0
  46. package/dist/hooks/api/integration-mapper.js.map +1 -1
  47. package/dist/hooks/api/integrations.d.ts +25 -1
  48. package/dist/hooks/api/integrations.d.ts.map +1 -1
  49. package/dist/hooks/api/integrations.js +44 -1
  50. package/dist/hooks/api/integrations.js.map +1 -1
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +4 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/jira-callback-state.d.ts +9 -0
  56. package/dist/jira-callback-state.d.ts.map +1 -0
  57. package/dist/jira-callback-state.js +16 -0
  58. package/dist/jira-callback-state.js.map +1 -0
  59. package/dist/jira-callback.d.ts +11 -0
  60. package/dist/jira-callback.d.ts.map +1 -0
  61. package/dist/jira-callback.js +59 -0
  62. package/dist/jira-callback.js.map +1 -0
  63. package/dist/jira-form-errors.d.ts +8 -0
  64. package/dist/jira-form-errors.d.ts.map +1 -0
  65. package/dist/jira-form-errors.js +55 -0
  66. package/dist/jira-form-errors.js.map +1 -0
  67. package/dist/pages/gitea-install-page.js +4 -4
  68. package/dist/pages/gitea-install-page.js.map +1 -1
  69. package/dist/pages/jira-callback-page.d.ts +2 -0
  70. package/dist/pages/jira-callback-page.d.ts.map +1 -0
  71. package/dist/pages/jira-callback-page.js +341 -0
  72. package/dist/pages/jira-callback-page.js.map +1 -0
  73. package/dist/pages/jira-install-page.d.ts +2 -0
  74. package/dist/pages/jira-install-page.d.ts.map +1 -0
  75. package/dist/pages/jira-install-page.js +26 -0
  76. package/dist/pages/jira-install-page.js.map +1 -0
  77. package/dist/pages/sentry-callback-page.js +7 -7
  78. package/dist/pages/sentry-callback-page.js.map +1 -1
  79. package/dist/pages/source-control-onboarding-page.js +2 -2
  80. package/dist/pages/source-control-onboarding-page.js.map +1 -1
  81. package/dist/provider-catalog.d.ts +1 -1
  82. package/dist/provider-catalog.d.ts.map +1 -1
  83. package/dist/provider-catalog.js +5 -0
  84. package/dist/provider-catalog.js.map +1 -1
  85. package/dist/routes/jira-callback.d.ts +8 -0
  86. package/dist/routes/jira-callback.d.ts.map +1 -0
  87. package/dist/routes/jira-callback.js +9 -0
  88. package/dist/routes/jira-callback.js.map +1 -0
  89. package/dist/routes/jira.d.ts +6 -0
  90. package/dist/routes/jira.d.ts.map +1 -0
  91. package/dist/routes/jira.js +7 -0
  92. package/dist/routes/jira.js.map +1 -0
  93. package/dist/tsconfig.test.tsbuildinfo +1 -1
  94. package/package.json +9 -8
  95. package/src/application/complete-integration-callback.test.ts +44 -1
  96. package/src/application/complete-integration-callback.ts +61 -8
  97. package/src/components/callback-status-shell.tsx +7 -4
  98. package/src/components/connection-picker.tsx +3 -3
  99. package/src/components/installed-integrations-section.tsx +9 -9
  100. package/src/components/integration-delete-confirm-modal.tsx +1 -1
  101. package/src/components/integration-gallery-for-workspace.tsx +3 -3
  102. package/src/components/integration-usage-events.test.ts +13 -0
  103. package/src/components/integration-usage-modal.tsx +6 -6
  104. package/src/components/provider-grid.tsx +10 -10
  105. package/src/components/redirect-install-page.tsx +1 -1
  106. package/src/components/repository-picker.test.tsx +2 -2
  107. package/src/components/repository-picker.tsx +6 -4
  108. package/src/components/webhook/copyable-value.tsx +3 -3
  109. package/src/components/webhook/webhook-create-modal.tsx +1 -1
  110. package/src/components/webhook/webhook-usage-details.tsx +2 -2
  111. package/src/core/models.ts +18 -0
  112. package/src/feature.ts +10 -0
  113. package/src/hooks/api/integration-mapper.ts +6 -0
  114. package/src/hooks/api/integrations.test.ts +92 -0
  115. package/src/hooks/api/integrations.ts +71 -0
  116. package/src/index.ts +4 -0
  117. package/src/jira-callback-state.ts +19 -0
  118. package/src/jira-callback.test.ts +76 -0
  119. package/src/jira-callback.ts +67 -0
  120. package/src/jira-form-errors.test.ts +57 -0
  121. package/src/jira-form-errors.ts +94 -0
  122. package/src/pages/gitea-install-page.tsx +4 -4
  123. package/src/pages/jira-callback-page.test.tsx +250 -0
  124. package/src/pages/jira-callback-page.tsx +366 -0
  125. package/src/pages/jira-install-page.tsx +28 -0
  126. package/src/pages/sentry-callback-page.tsx +7 -7
  127. package/src/pages/source-control-onboarding-page.tsx +2 -2
  128. package/src/provider-catalog.test.ts +6 -0
  129. package/src/provider-catalog.ts +7 -1
  130. package/src/routes/jira-callback.tsx +8 -0
  131. package/src/routes/jira.tsx +4 -0
  132. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/integration-usage-modal.tsx"],"sourcesContent":["import {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';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport interface IntegrationUsageEvent {\n value: string;\n label: string;\n}\n\ninterface IntegrationUsageModalProps {\n connection: IntegrationConnection | 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.displayName}` : '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.lifecycleStatus} 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","displayName","aria-describedby","undefined","className","div","size","aria-hidden","status","lifecycleStatus","section","bold","length","label","htmlFor","onValueChange","id","placeholder","item","syntaxHighlighting","highlightedLineRange","startLine","endLine","type","onClick"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/integration-usage-modal.tsx"],"sourcesContent":["import {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';\nimport type {IntegrationConnection} from '#core/models.js';\n\nexport interface IntegrationUsageEvent {\n value: string;\n label: string;\n}\n\ninterface IntegrationUsageModalProps {\n connection: IntegrationConnection | 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.displayName}` : '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-tight\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Text size=\"lg\" aria-hidden=\"true\" className=\"truncate\">\n {title}\n </Text>\n {connection ? (\n <ConnectionStatusBadge status={connection.lifecycleStatus} 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-section overflow-y-auto overflow-x-clip scrollbar\">\n <section className=\"flex w-full flex-col gap-cluster\">\n <Text size=\"md\" bold>\n Usage\n </Text>\n {events.length > 1 ? (\n <div className=\"flex flex-col gap-inline\">\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-cluster\">\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","displayName","aria-describedby","undefined","className","div","size","aria-hidden","status","lifecycleStatus","section","bold","length","label","htmlFor","onValueChange","id","placeholder","item","syntaxHighlighting","highlightedLineRange","startLine","endLine","type","onClick"],"mappings":"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"}
@@ -11,7 +11,7 @@ export interface ProviderGridProps {
11
11
  errorSubject?: string;
12
12
  onOpenProvider?: ((provider: string) => void) | undefined;
13
13
  }
14
- export declare const PROVIDER_GRID_CLASS = "grid grid-cols-2 gap-12 max-[760px]:grid-cols-1";
14
+ export declare const PROVIDER_GRID_CLASS = "grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1";
15
15
  export declare const PROVIDER_SURFACE_CLASS = "overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base";
16
16
  export declare function ProviderGrid({ workspaceSlug, providers, isPending, isFetching, error, onRetry, emptyMessage, loadingLabel, errorSubject, onOpenProvider, }: ProviderGridProps): import("react").JSX.Element;
17
17
  //# sourceMappingURL=provider-grid.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider-grid.d.ts","sourceRoot":"","sources":["../../src/components/provider-grid.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,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,aAAa,EACb,SAAS,EACT,SAAS,EACT,UAAkB,EAClB,KAAK,EACL,OAAO,EACP,YAAY,EACZ,YAAkC,EAClC,YAAuC,EACvC,cAAc,GACf,EAAE,iBAAiB,+BA+CnB"}
1
+ {"version":3,"file":"provider-grid.d.ts","sourceRoot":"","sources":["../../src/components/provider-grid.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,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,yDAAyD,CAAC;AAE1F,eAAO,MAAM,sBAAsB,2FACuD,CAAC;AAE3F,wBAAgB,YAAY,CAAC,EAC3B,aAAa,EACb,SAAS,EACT,SAAS,EACT,UAAkB,EAClB,KAAK,EACL,OAAO,EACP,YAAY,EACZ,YAAkC,EAClC,YAAuC,EACvC,cAAc,GACf,EAAE,iBAAiB,+BA+CnB"}
@@ -9,7 +9,7 @@ import { Text } from '@shipfox/react-ui/typography';
9
9
  import { cn } from '@shipfox/react-ui/utils';
10
10
  import { Link } from '@tanstack/react-router';
11
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';
12
+ export const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1';
13
13
  export const PROVIDER_SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
14
14
  export function ProviderGrid({ workspaceSlug, providers, isPending, isFetching = false, error, onRetry, emptyMessage, loadingLabel = 'Loading providers', errorSubject = 'available integrations', onOpenProvider }) {
15
15
  const installableProviders = providers.filter((provider)=>PROVIDER_CATALOG[provider.provider]);
@@ -18,7 +18,7 @@ export function ProviderGrid({ workspaceSlug, providers, isPending, isFetching =
18
18
  });
19
19
  if (error) {
20
20
  return /*#__PURE__*/ _jsx("div", {
21
- className: cn(PROVIDER_SURFACE_CLASS, 'px-16'),
21
+ className: cn(PROVIDER_SURFACE_CLASS, 'px-row'),
22
22
  children: /*#__PURE__*/ _jsx(QueryLoadError, {
23
23
  query: {
24
24
  isError: true,
@@ -33,7 +33,7 @@ export function ProviderGrid({ workspaceSlug, providers, isPending, isFetching =
33
33
  }
34
34
  if (installableProviders.length === 0) {
35
35
  return /*#__PURE__*/ _jsx("div", {
36
- className: cn(PROVIDER_SURFACE_CLASS, 'px-16'),
36
+ className: cn(PROVIDER_SURFACE_CLASS, 'px-row'),
37
37
  children: /*#__PURE__*/ _jsx(EmptyState, {
38
38
  icon: "componentLine",
39
39
  title: "No integrations available",
@@ -60,7 +60,7 @@ function ProviderCard({ provider, workspaceSlug, onOpenProvider }) {
60
60
  type: "button",
61
61
  "aria-label": `Add ${provider.displayName}`,
62
62
  onClick: ()=>onOpenProvider?.(provider.provider),
63
- 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",
63
+ className: "group flex h-full w-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
64
64
  children: /*#__PURE__*/ _jsx(ProviderCardContent, {
65
65
  provider: provider,
66
66
  action: "Add"
@@ -73,7 +73,7 @@ function ProviderCard({ provider, workspaceSlug, onOpenProvider }) {
73
73
  workspaceSlug
74
74
  },
75
75
  "aria-label": `Install ${provider.displayName}`,
76
- 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",
76
+ className: "group flex h-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
77
77
  children: /*#__PURE__*/ _jsx(ProviderCardContent, {
78
78
  provider: provider,
79
79
  action: "Install"
@@ -84,7 +84,7 @@ function ProviderCardContent({ provider, action }) {
84
84
  return /*#__PURE__*/ _jsxs(_Fragment, {
85
85
  children: [
86
86
  /*#__PURE__*/ _jsxs("span", {
87
- className: "flex min-w-0 items-center gap-12",
87
+ className: "flex min-w-0 items-center gap-cluster",
88
88
  children: [
89
89
  /*#__PURE__*/ _jsx(IntegrationIcon, {
90
90
  source: provider.provider,
@@ -101,7 +101,7 @@ function ProviderCardContent({ provider, action }) {
101
101
  ]
102
102
  }),
103
103
  /*#__PURE__*/ _jsxs("span", {
104
- className: "flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive",
104
+ className: "flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive",
105
105
  children: [
106
106
  /*#__PURE__*/ _jsx(Text, {
107
107
  as: "span",
@@ -129,12 +129,12 @@ function ProviderGridSkeleton({ label }) {
129
129
  3
130
130
  ].map((tile)=>/*#__PURE__*/ _jsx("li", {
131
131
  children: /*#__PURE__*/ _jsx(Card, {
132
- className: "h-full p-16",
132
+ className: "h-full p-panel-compact",
133
133
  children: /*#__PURE__*/ _jsxs("div", {
134
- className: "flex items-center justify-between gap-12",
134
+ className: "flex items-center justify-between gap-cluster",
135
135
  children: [
136
136
  /*#__PURE__*/ _jsxs("div", {
137
- className: "flex min-w-0 items-center gap-12",
137
+ className: "flex min-w-0 items-center gap-cluster",
138
138
  children: [
139
139
  /*#__PURE__*/ _jsx(Skeleton, {
140
140
  className: "size-24 shrink-0"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/provider-grid.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {IntegrationProvider} from '#core/models.js';\nimport {PROVIDER_CATALOG} from '#provider-catalog.js';\n\nexport interface ProviderGridProps {\n workspaceSlug: string;\n providers: IntegrationProvider[];\n isPending: boolean;\n isFetching?: boolean;\n error?: Error | null | undefined;\n onRetry?: () => void;\n emptyMessage: string;\n loadingLabel?: string;\n errorSubject?: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}\n\nexport const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-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 workspaceSlug,\n providers,\n isPending,\n isFetching = false,\n error,\n onRetry,\n emptyMessage,\n loadingLabel = 'Loading providers',\n errorSubject = 'available integrations',\n onOpenProvider,\n}: ProviderGridProps) {\n const installableProviders = providers.filter((provider) => PROVIDER_CATALOG[provider.provider]);\n\n if (isPending) return <ProviderGridSkeleton label={loadingLabel} />;\n\n if (error) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-16')}>\n <QueryLoadError\n query={{\n isError: true,\n isFetching,\n data: undefined,\n error,\n refetch: onRetry ?? (() => undefined),\n }}\n subject={errorSubject}\n />\n </div>\n );\n }\n\n if (installableProviders.length === 0) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-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 workspaceSlug={workspaceSlug}\n onOpenProvider={onOpenProvider}\n />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction ProviderCard({\n provider,\n workspaceSlug,\n onOpenProvider,\n}: {\n provider: IntegrationProvider;\n workspaceSlug: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}) {\n const catalog = PROVIDER_CATALOG[provider.provider];\n if (!catalog) return null;\n\n if (catalog.kind === 'modal-connect') {\n return (\n <button\n type=\"button\"\n aria-label={`Add ${provider.displayName}`}\n onClick={() => onOpenProvider?.(provider.provider)}\n className=\"group flex h-full w-full min-w-0 items-center justify-between gap-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={{workspaceSlug}}\n aria-label={`Install ${provider.displayName}`}\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: IntegrationProvider;\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.displayName}\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","IntegrationIcon","Card","EmptyState","Icon","Skeleton","Text","cn","Link","PROVIDER_CATALOG","PROVIDER_GRID_CLASS","PROVIDER_SURFACE_CLASS","ProviderGrid","workspaceSlug","providers","isPending","isFetching","error","onRetry","emptyMessage","loadingLabel","errorSubject","onOpenProvider","installableProviders","filter","provider","ProviderGridSkeleton","label","div","className","query","isError","data","undefined","refetch","subject","length","icon","title","description","ul","map","li","ProviderCard","catalog","kind","button","type","aria-label","displayName","onClick","ProviderCardContent","action","to","setupPath","params","span","source","aria-hidden","as","size","bold","name","role","tile"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/provider-grid.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IntegrationIcon} from '@shipfox/integration-icons';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {IntegrationProvider} from '#core/models.js';\nimport {PROVIDER_CATALOG} from '#provider-catalog.js';\n\nexport interface ProviderGridProps {\n workspaceSlug: string;\n providers: IntegrationProvider[];\n isPending: boolean;\n isFetching?: boolean;\n error?: Error | null | undefined;\n onRetry?: () => void;\n emptyMessage: string;\n loadingLabel?: string;\n errorSubject?: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}\n\nexport const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1';\n\nexport const PROVIDER_SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function ProviderGrid({\n workspaceSlug,\n providers,\n isPending,\n isFetching = false,\n error,\n onRetry,\n emptyMessage,\n loadingLabel = 'Loading providers',\n errorSubject = 'available integrations',\n onOpenProvider,\n}: ProviderGridProps) {\n const installableProviders = providers.filter((provider) => PROVIDER_CATALOG[provider.provider]);\n\n if (isPending) return <ProviderGridSkeleton label={loadingLabel} />;\n\n if (error) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-row')}>\n <QueryLoadError\n query={{\n isError: true,\n isFetching,\n data: undefined,\n error,\n refetch: onRetry ?? (() => undefined),\n }}\n subject={errorSubject}\n />\n </div>\n );\n }\n\n if (installableProviders.length === 0) {\n return (\n <div className={cn(PROVIDER_SURFACE_CLASS, 'px-row')}>\n <EmptyState\n icon=\"componentLine\"\n title=\"No integrations available\"\n description={emptyMessage}\n />\n </div>\n );\n }\n\n return (\n <ul className={PROVIDER_GRID_CLASS}>\n {installableProviders.map((provider) => (\n <li key={provider.provider}>\n <ProviderCard\n provider={provider}\n workspaceSlug={workspaceSlug}\n onOpenProvider={onOpenProvider}\n />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction ProviderCard({\n provider,\n workspaceSlug,\n onOpenProvider,\n}: {\n provider: IntegrationProvider;\n workspaceSlug: string;\n onOpenProvider?: ((provider: string) => void) | undefined;\n}) {\n const catalog = PROVIDER_CATALOG[provider.provider];\n if (!catalog) return null;\n\n if (catalog.kind === 'modal-connect') {\n return (\n <button\n type=\"button\"\n aria-label={`Add ${provider.displayName}`}\n onClick={() => onOpenProvider?.(provider.provider)}\n className=\"group flex h-full w-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Add\" />\n </button>\n );\n }\n\n return (\n <Link\n to={catalog.setupPath}\n params={{workspaceSlug}}\n aria-label={`Install ${provider.displayName}`}\n className=\"group flex h-full min-w-0 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base bg-background-neutral-base p-panel-compact transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <ProviderCardContent provider={provider} action=\"Install\" />\n </Link>\n );\n}\n\nfunction ProviderCardContent({\n provider,\n action,\n}: {\n provider: IntegrationProvider;\n action: 'Add' | 'Install';\n}) {\n return (\n <>\n <span className=\"flex min-w-0 items-center gap-cluster\">\n <IntegrationIcon\n source={provider.provider}\n aria-hidden\n className=\"size-24 shrink-0 text-foreground-neutral-base\"\n />\n <Text as=\"span\" size=\"md\" bold className=\"truncate\">\n {provider.displayName}\n </Text>\n </span>\n <span className=\"flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive\">\n <Text as=\"span\" size=\"sm\">\n {action}\n </Text>\n <Icon name=\"chevronRight\" className=\"size-16\" />\n </span>\n </>\n );\n}\n\nfunction ProviderGridSkeleton({label}: {label: string}) {\n return (\n <ul role=\"status\" aria-label={label} className={PROVIDER_GRID_CLASS}>\n {[0, 1, 2, 3].map((tile) => (\n <li key={tile}>\n <Card className=\"h-full p-panel-compact\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-cluster\">\n <Skeleton className=\"size-24 shrink-0\" />\n <Skeleton className=\"h-16 w-100\" />\n </div>\n <Skeleton className=\"h-16 w-64 shrink-0\" />\n </div>\n </Card>\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IntegrationIcon","Card","EmptyState","Icon","Skeleton","Text","cn","Link","PROVIDER_CATALOG","PROVIDER_GRID_CLASS","PROVIDER_SURFACE_CLASS","ProviderGrid","workspaceSlug","providers","isPending","isFetching","error","onRetry","emptyMessage","loadingLabel","errorSubject","onOpenProvider","installableProviders","filter","provider","ProviderGridSkeleton","label","div","className","query","isError","data","undefined","refetch","subject","length","icon","title","description","ul","map","li","ProviderCard","catalog","kind","button","type","aria-label","displayName","onClick","ProviderCardContent","action","to","setupPath","params","span","source","aria-hidden","as","size","bold","name","role","tile"],"mappings":"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"}
@@ -37,7 +37,7 @@ export function RedirectInstallPage({ installRequest, errorFallbackMessage, load
37
37
  ]);
38
38
  if (errorMessage) {
39
39
  return /*#__PURE__*/ _jsxs("div", {
40
- className: "mx-auto flex w-full max-w-[480px] flex-col gap-16",
40
+ className: "mx-auto flex w-full max-w-[480px] flex-col gap-group",
41
41
  children: [
42
42
  /*#__PURE__*/ _jsx(Callout, {
43
43
  role: "alert",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/redirect-install-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef, useState} from 'react';\nimport type {InstallRedirect} from '#core/models.js';\n\ninterface RedirectInstallPageProps {\n installRequest: (body: {workspace_id: string}) => Promise<InstallRedirect>;\n errorFallbackMessage: string;\n loadingLabel?: string;\n /**\n * Runs before leaving the app (e.g. to persist the workspace id for a\n * state-less provider callback. It must not throw. A failed side effect\n * should never block the redirect.\n */\n beforeRedirect?: (workspaceId: string) => void;\n /** Injectable for tests: jsdom's window.location cannot be stubbed. */\n assignLocation?: (url: string) => void;\n}\n\nexport function RedirectInstallPage({\n installRequest,\n errorFallbackMessage,\n loadingLabel = 'Starting installation',\n beforeRedirect,\n assignLocation = (url) => window.location.assign(url),\n}: RedirectInstallPageProps) {\n const workspace = useActiveWorkspace();\n const startedRef = useRef(false);\n const [errorMessage, setErrorMessage] = useState<string | undefined>();\n\n useEffect(() => {\n if (startedRef.current) return;\n startedRef.current = true;\n // The side effect is best-effort persistence; a throw here must never\n // block the install redirect, so swallow it and continue.\n try {\n beforeRedirect?.(workspace.id);\n } catch {\n // ignore\n }\n installRequest({workspace_id: workspace.id})\n .then((response) => {\n assignLocation(response.installUrl);\n })\n .catch((error: unknown) => {\n setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);\n });\n }, [workspace, installRequest, beforeRedirect, errorFallbackMessage, assignLocation]);\n\n if (errorMessage) {\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-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=\"/w/$workspaceSlug/integrations\" params={{workspaceSlug: workspace.slug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n </div>\n );\n }\n\n return <FullPageLoader aria-label={loadingLabel} />;\n}\n"],"names":["ApiError","useActiveWorkspace","ButtonLink","Callout","FullPageLoader","Text","Link","useEffect","useRef","useState","RedirectInstallPage","installRequest","errorFallbackMessage","loadingLabel","beforeRedirect","assignLocation","url","window","location","assign","workspace","startedRef","errorMessage","setErrorMessage","current","id","workspace_id","then","response","installUrl","catch","error","message","div","className","role","type","size","asChild","variant","to","params","workspaceSlug","slug","aria-label"],"mappings":";AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAiBlD,OAAO,SAASC,oBAAoB,EAClCC,cAAc,EACdC,oBAAoB,EACpBC,eAAe,uBAAuB,EACtCC,cAAc,EACdC,iBAAiB,CAACC,MAAQC,OAAOC,QAAQ,CAACC,MAAM,CAACH,IAAI,EAC5B;IACzB,MAAMI,YAAYnB;IAClB,MAAMoB,aAAab,OAAO;IAC1B,MAAM,CAACc,cAAcC,gBAAgB,GAAGd;IAExCF,UAAU;QACR,IAAIc,WAAWG,OAAO,EAAE;QACxBH,WAAWG,OAAO,GAAG;QACrB,sEAAsE;QACtE,0DAA0D;QAC1D,IAAI;YACFV,iBAAiBM,UAAUK,EAAE;QAC/B,EAAE,OAAM;QACN,SAAS;QACX;QACAd,eAAe;YAACe,cAAcN,UAAUK,EAAE;QAAA,GACvCE,IAAI,CAAC,CAACC;YACLb,eAAea,SAASC,UAAU;QACpC,GACCC,KAAK,CAAC,CAACC;YACNR,gBAAgBQ,iBAAiB/B,WAAW+B,MAAMC,OAAO,GAAGpB;QAC9D;IACJ,GAAG;QAACQ;QAAWT;QAAgBG;QAAgBF;QAAsBG;KAAe;IAEpF,IAAIO,cAAc;QAChB,qBACE,MAACW;YAAIC,WAAU;;8BACb,KAAC/B;oBAAQgC,MAAK;oBAAQC,MAAK;8BACzB,cAAA,KAAC/B;wBAAKgC,MAAK;kCAAMf;;;8BAEnB,KAACpB;oBAAWoC,OAAO;oBAACC,SAAQ;oBAAQL,WAAU;8BAC5C,cAAA,KAAC5B;wBAAKkC,IAAG;wBAAiCC,QAAQ;4BAACC,eAAetB,UAAUuB,IAAI;wBAAA;kCAAG;;;;;IAM3F;IAEA,qBAAO,KAACvC;QAAewC,cAAY/B;;AACrC"}
1
+ {"version":3,"sources":["../../src/components/redirect-install-page.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useActiveWorkspace} from '@shipfox/client-auth';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useRef, useState} from 'react';\nimport type {InstallRedirect} from '#core/models.js';\n\ninterface RedirectInstallPageProps {\n installRequest: (body: {workspace_id: string}) => Promise<InstallRedirect>;\n errorFallbackMessage: string;\n loadingLabel?: string;\n /**\n * Runs before leaving the app (e.g. to persist the workspace id for a\n * state-less provider callback. It must not throw. A failed side effect\n * should never block the redirect.\n */\n beforeRedirect?: (workspaceId: string) => void;\n /** Injectable for tests: jsdom's window.location cannot be stubbed. */\n assignLocation?: (url: string) => void;\n}\n\nexport function RedirectInstallPage({\n installRequest,\n errorFallbackMessage,\n loadingLabel = 'Starting installation',\n beforeRedirect,\n assignLocation = (url) => window.location.assign(url),\n}: RedirectInstallPageProps) {\n const workspace = useActiveWorkspace();\n const startedRef = useRef(false);\n const [errorMessage, setErrorMessage] = useState<string | undefined>();\n\n useEffect(() => {\n if (startedRef.current) return;\n startedRef.current = true;\n // The side effect is best-effort persistence; a throw here must never\n // block the install redirect, so swallow it and continue.\n try {\n beforeRedirect?.(workspace.id);\n } catch {\n // ignore\n }\n installRequest({workspace_id: workspace.id})\n .then((response) => {\n assignLocation(response.installUrl);\n })\n .catch((error: unknown) => {\n setErrorMessage(error instanceof ApiError ? error.message : errorFallbackMessage);\n });\n }, [workspace, installRequest, beforeRedirect, errorFallbackMessage, assignLocation]);\n\n if (errorMessage) {\n return (\n <div className=\"mx-auto flex w-full max-w-[480px] flex-col gap-group\">\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{errorMessage}</Text>\n </Callout>\n <ButtonLink asChild variant=\"muted\" className=\"w-fit\">\n <Link to=\"/w/$workspaceSlug/integrations\" params={{workspaceSlug: workspace.slug}}>\n Back to integrations\n </Link>\n </ButtonLink>\n </div>\n );\n }\n\n return <FullPageLoader aria-label={loadingLabel} />;\n}\n"],"names":["ApiError","useActiveWorkspace","ButtonLink","Callout","FullPageLoader","Text","Link","useEffect","useRef","useState","RedirectInstallPage","installRequest","errorFallbackMessage","loadingLabel","beforeRedirect","assignLocation","url","window","location","assign","workspace","startedRef","errorMessage","setErrorMessage","current","id","workspace_id","then","response","installUrl","catch","error","message","div","className","role","type","size","asChild","variant","to","params","workspaceSlug","slug","aria-label"],"mappings":";AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAiBlD,OAAO,SAASC,oBAAoB,EAClCC,cAAc,EACdC,oBAAoB,EACpBC,eAAe,uBAAuB,EACtCC,cAAc,EACdC,iBAAiB,CAACC,MAAQC,OAAOC,QAAQ,CAACC,MAAM,CAACH,IAAI,EAC5B;IACzB,MAAMI,YAAYnB;IAClB,MAAMoB,aAAab,OAAO;IAC1B,MAAM,CAACc,cAAcC,gBAAgB,GAAGd;IAExCF,UAAU;QACR,IAAIc,WAAWG,OAAO,EAAE;QACxBH,WAAWG,OAAO,GAAG;QACrB,sEAAsE;QACtE,0DAA0D;QAC1D,IAAI;YACFV,iBAAiBM,UAAUK,EAAE;QAC/B,EAAE,OAAM;QACN,SAAS;QACX;QACAd,eAAe;YAACe,cAAcN,UAAUK,EAAE;QAAA,GACvCE,IAAI,CAAC,CAACC;YACLb,eAAea,SAASC,UAAU;QACpC,GACCC,KAAK,CAAC,CAACC;YACNR,gBAAgBQ,iBAAiB/B,WAAW+B,MAAMC,OAAO,GAAGpB;QAC9D;IACJ,GAAG;QAACQ;QAAWT;QAAgBG;QAAgBF;QAAsBG;KAAe;IAEpF,IAAIO,cAAc;QAChB,qBACE,MAACW;YAAIC,WAAU;;8BACb,KAAC/B;oBAAQgC,MAAK;oBAAQC,MAAK;8BACzB,cAAA,KAAC/B;wBAAKgC,MAAK;kCAAMf;;;8BAEnB,KAACpB;oBAAWoC,OAAO;oBAACC,SAAQ;oBAAQL,WAAU;8BAC5C,cAAA,KAAC5B;wBAAKkC,IAAG;wBAAiCC,QAAQ;4BAACC,eAAetB,UAAUuB,IAAI;wBAAA;kCAAG;;;;;IAM3F;IAEA,qBAAO,KAACvC;QAAewC,cAAY/B;;AACrC"}
@@ -8,7 +8,7 @@ import { Skeleton } from '@shipfox/react-ui/skeleton';
8
8
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
9
9
  import { Text } from '@shipfox/react-ui/typography';
10
10
  import { useId } from 'react';
11
- const REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-8 max-[760px]:grid-cols-1';
11
+ const REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-inline max-[760px]:grid-cols-1';
12
12
  const REPOSITORY_SKELETON_WIDTHS = [
13
13
  'w-64',
14
14
  'w-96',
@@ -19,7 +19,7 @@ export function RepositoryPicker({ repositories, selectedRepositoryId, onSelect,
19
19
  const labelId = useId();
20
20
  const showSearch = onSearchChange !== undefined;
21
21
  return /*#__PURE__*/ _jsxs("div", {
22
- className: "flex flex-col gap-10",
22
+ className: "flex flex-col gap-inline",
23
23
  children: [
24
24
  /*#__PURE__*/ _jsx(Label, {
25
25
  id: labelId,
@@ -36,7 +36,7 @@ export function RepositoryPicker({ repositories, selectedRepositoryId, onSelect,
36
36
  }) : null,
37
37
  isLoading ? /*#__PURE__*/ _jsx(RepositoryLoadingState, {}) : null,
38
38
  !isLoading && repositories.length === 0 ? /*#__PURE__*/ _jsx("div", {
39
- className: "rounded-8 border border-border-neutral-base bg-background-subtle-base p-14",
39
+ className: "rounded-8 border border-border-neutral-base bg-background-subtle-base p-panel-compact",
40
40
  children: /*#__PURE__*/ _jsx(Text, {
41
41
  size: "sm",
42
42
  children: emptyMessage
@@ -101,7 +101,7 @@ function RepositoryLoadingState() {
101
101
  "aria-hidden": "true",
102
102
  className: REPOSITORY_GRID_CLASS_NAME,
103
103
  children: REPOSITORY_SKELETON_WIDTHS.map((width)=>/*#__PURE__*/ _jsx("div", {
104
- className: "h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-14",
104
+ className: "h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-[14px]",
105
105
  children: /*#__PURE__*/ _jsx(Skeleton, {
106
106
  className: `h-20 ${width}`
107
107
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/repository-picker.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {Repository} from '#core/models.js';\n\nconst REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-8 max-[760px]:grid-cols-1';\nconst REPOSITORY_SKELETON_WIDTHS = ['w-64', 'w-96', 'w-80', 'w-112'] as const;\n\nexport function RepositoryPicker({\n repositories,\n selectedRepositoryId,\n onSelect,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n onLoadMore,\n emptyMessage = 'No repositories found.',\n searchValue,\n onSearchChange,\n searchDisabled,\n}: {\n repositories: Repository[];\n selectedRepositoryId: string | undefined;\n onSelect: (repositoryId: string) => void;\n isLoading: boolean;\n isFetchingNextPage?: boolean;\n hasNextPage?: boolean;\n onLoadMore?: () => void;\n emptyMessage?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n searchDisabled?: boolean;\n}) {\n const labelId = useId();\n const showSearch = onSearchChange !== undefined;\n\n return (\n <div className=\"flex flex-col gap-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 ? <RepositoryLoadingState /> : 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={REPOSITORY_GRID_CLASS_NAME}\n >\n {repositories.map((repository) => (\n <RepositoryCard key={repository.externalRepositoryId} repository={repository} />\n ))}\n </RadioGroup>\n ) : null}\n\n {hasNextPage && onLoadMore ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n isLoading={isFetchingNextPage ?? false}\n onClick={onLoadMore}\n >\n Load more\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction RepositoryCard({repository}: {repository: Repository}) {\n const {ref: nameRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(repository.name);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <RadioGroupItem value={repository.externalRepositoryId} className=\"min-w-0\">\n <span ref={nameRef} className=\"block min-w-0 truncate\">\n <Text as=\"span\" size=\"sm\" bold>\n {repository.name}\n </Text>\n </span>\n </RadioGroupItem>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{repository.name}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction RepositoryLoadingState() {\n return (\n <>\n <div role=\"status\" className=\"sr-only\">\n Loading repositories.\n </div>\n <div aria-hidden=\"true\" className={REPOSITORY_GRID_CLASS_NAME}>\n {REPOSITORY_SKELETON_WIDTHS.map((width) => (\n <div\n key={width}\n className=\"h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-14\"\n >\n <Skeleton className={`h-20 ${width}`} />\n </div>\n ))}\n </div>\n </>\n );\n}\n"],"names":["Button","useIsTextTruncated","Input","Label","RadioGroup","RadioGroupItem","Skeleton","Tooltip","TooltipContent","TooltipTrigger","Text","useId","REPOSITORY_GRID_CLASS_NAME","REPOSITORY_SKELETON_WIDTHS","RepositoryPicker","repositories","selectedRepositoryId","onSelect","isLoading","isFetchingNextPage","hasNextPage","onLoadMore","emptyMessage","searchValue","onSearchChange","searchDisabled","labelId","showSearch","undefined","div","className","id","type","placeholder","aria-label","value","onChange","event","target","disabled","RepositoryLoadingState","length","size","aria-labelledby","onValueChange","map","repository","RepositoryCard","externalRepositoryId","variant","onClick","ref","nameRef","isTruncated","name","asChild","span","as","bold","role","aria-hidden","width"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAG5B,MAAMC,6BAA6B;AACnC,MAAMC,6BAA6B;IAAC;IAAQ;IAAQ;IAAQ;CAAQ;AAEpE,OAAO,SAASC,iBAAiB,EAC/BC,YAAY,EACZC,oBAAoB,EACpBC,QAAQ,EACRC,SAAS,EACTC,kBAAkB,EAClBC,WAAW,EACXC,UAAU,EACVC,eAAe,wBAAwB,EACvCC,WAAW,EACXC,cAAc,EACdC,cAAc,EAaf;IACC,MAAMC,UAAUf;IAChB,MAAMgB,aAAaH,mBAAmBI;IAEtC,qBACE,MAACC;QAAIC,WAAU;;0BACb,KAAC3B;gBAAM4B,IAAIL;gBAASI,WAAU;0BAAU;;YAIvCH,2BACC,KAACzB;gBACC8B,MAAK;gBACLC,aAAY;gBACZC,cAAW;gBACXC,OAAOZ,eAAe;gBACtBa,UAAU,CAACC,QAAUb,iBAAiBa,MAAMC,MAAM,CAACH,KAAK;gBACxDI,UAAUd;iBAEV;YAEHP,0BAAY,KAACsB,8BAA4B;YAEzC,CAACtB,aAAaH,aAAa0B,MAAM,KAAK,kBACrC,KAACZ;gBAAIC,WAAU;0BACb,cAAA,KAACpB;oBAAKgC,MAAK;8BAAMpB;;iBAEjB;YAEHP,aAAa0B,MAAM,GAAG,kBACrB,KAACrC;gBACCuC,mBAAiBjB;gBACjBS,OAAOnB,wBAAwB;gBAC/B4B,eAAe3B;gBACfa,WAAWlB;0BAEVG,aAAa8B,GAAG,CAAC,CAACC,2BACjB,KAACC;wBAAqDD,YAAYA;uBAA7CA,WAAWE,oBAAoB;iBAGtD;YAEH5B,eAAeC,2BACd,KAACrB;gBACCgC,MAAK;gBACLiB,SAAQ;gBACRP,MAAK;gBACLxB,WAAWC,sBAAsB;gBACjC+B,SAAS7B;0BACV;iBAGC;;;AAGV;AAEA,SAAS0B,eAAe,EAACD,UAAU,EAA2B;IAC5D,MAAM,EAACK,KAAKC,OAAO,EAAEC,WAAW,EAAC,GAAGpD,mBAAoC6C,WAAWQ,IAAI;IAEvF,qBACE,MAAC/C;;0BACC,KAACE;gBAAe8C,OAAO;0BACrB,cAAA,KAAClD;oBAAe8B,OAAOW,WAAWE,oBAAoB;oBAAElB,WAAU;8BAChE,cAAA,KAAC0B;wBAAKL,KAAKC;wBAAStB,WAAU;kCAC5B,cAAA,KAACpB;4BAAK+C,IAAG;4BAAOf,MAAK;4BAAKgB,IAAI;sCAC3BZ,WAAWQ,IAAI;;;;;YAKvBD,4BAAc,KAAC7C;0BAAgBsC,WAAWQ,IAAI;iBAAqB;;;AAG1E;AAEA,SAASd;IACP,qBACE;;0BACE,KAACX;gBAAI8B,MAAK;gBAAS7B,WAAU;0BAAU;;0BAGvC,KAACD;gBAAI+B,eAAY;gBAAO9B,WAAWlB;0BAChCC,2BAA2BgC,GAAG,CAAC,CAACgB,sBAC/B,KAAChC;wBAECC,WAAU;kCAEV,cAAA,KAACxB;4BAASwB,WAAW,CAAC,KAAK,EAAE+B,OAAO;;uBAH/BA;;;;AASjB"}
1
+ {"version":3,"sources":["../../src/components/repository-picker.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Label} from '@shipfox/react-ui/label';\nimport {RadioGroup, RadioGroupItem} from '@shipfox/react-ui/radio-group';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useId} from 'react';\nimport type {Repository} from '#core/models.js';\n\nconst REPOSITORY_GRID_CLASS_NAME = 'grid grid-cols-2 gap-inline max-[760px]:grid-cols-1';\nconst REPOSITORY_SKELETON_WIDTHS = ['w-64', 'w-96', 'w-80', 'w-112'] as const;\n\nexport function RepositoryPicker({\n repositories,\n selectedRepositoryId,\n onSelect,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n onLoadMore,\n emptyMessage = 'No repositories found.',\n searchValue,\n onSearchChange,\n searchDisabled,\n}: {\n repositories: Repository[];\n selectedRepositoryId: string | undefined;\n onSelect: (repositoryId: string) => void;\n isLoading: boolean;\n isFetchingNextPage?: boolean;\n hasNextPage?: boolean;\n onLoadMore?: () => void;\n emptyMessage?: string;\n searchValue?: string;\n onSearchChange?: (value: string) => void;\n searchDisabled?: boolean;\n}) {\n const labelId = useId();\n const showSearch = onSearchChange !== undefined;\n\n return (\n <div className=\"flex flex-col gap-inline\">\n <Label id={labelId} className=\"sr-only\">\n Repository\n </Label>\n\n {showSearch ? (\n <Input\n type=\"search\"\n placeholder=\"Search repositories…\"\n aria-label=\"Search repositories\"\n value={searchValue ?? ''}\n onChange={(event) => onSearchChange?.(event.target.value)}\n disabled={searchDisabled}\n />\n ) : null}\n\n {isLoading ? <RepositoryLoadingState /> : null}\n\n {!isLoading && repositories.length === 0 ? (\n <div className=\"rounded-8 border border-border-neutral-base bg-background-subtle-base p-panel-compact\">\n <Text size=\"sm\">{emptyMessage}</Text>\n </div>\n ) : null}\n\n {repositories.length > 0 ? (\n <RadioGroup\n aria-labelledby={labelId}\n value={selectedRepositoryId ?? ''}\n onValueChange={onSelect}\n className={REPOSITORY_GRID_CLASS_NAME}\n >\n {repositories.map((repository) => (\n <RepositoryCard key={repository.externalRepositoryId} repository={repository} />\n ))}\n </RadioGroup>\n ) : null}\n\n {hasNextPage && onLoadMore ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n isLoading={isFetchingNextPage ?? false}\n onClick={onLoadMore}\n >\n Load more\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction RepositoryCard({repository}: {repository: Repository}) {\n const {ref: nameRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(repository.name);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <RadioGroupItem value={repository.externalRepositoryId} className=\"min-w-0\">\n <span ref={nameRef} className=\"block min-w-0 truncate\">\n <Text as=\"span\" size=\"sm\" bold>\n {repository.name}\n </Text>\n </span>\n </RadioGroupItem>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{repository.name}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction RepositoryLoadingState() {\n return (\n <>\n <div role=\"status\" className=\"sr-only\">\n Loading repositories.\n </div>\n <div aria-hidden=\"true\" className={REPOSITORY_GRID_CLASS_NAME}>\n {/* The skeleton mirrors RadioGroupItem's own 14px padding contract so the\n placeholder and the loaded card stay the same height. */}\n {REPOSITORY_SKELETON_WIDTHS.map((width) => (\n <div\n key={width}\n className=\"h-50 min-w-0 rounded-8 border border-border-neutral-base bg-background-neutral-base p-[14px]\"\n >\n <Skeleton className={`h-20 ${width}`} />\n </div>\n ))}\n </div>\n </>\n );\n}\n"],"names":["Button","useIsTextTruncated","Input","Label","RadioGroup","RadioGroupItem","Skeleton","Tooltip","TooltipContent","TooltipTrigger","Text","useId","REPOSITORY_GRID_CLASS_NAME","REPOSITORY_SKELETON_WIDTHS","RepositoryPicker","repositories","selectedRepositoryId","onSelect","isLoading","isFetchingNextPage","hasNextPage","onLoadMore","emptyMessage","searchValue","onSearchChange","searchDisabled","labelId","showSearch","undefined","div","className","id","type","placeholder","aria-label","value","onChange","event","target","disabled","RepositoryLoadingState","length","size","aria-labelledby","onValueChange","map","repository","RepositoryCard","externalRepositoryId","variant","onClick","ref","nameRef","isTruncated","name","asChild","span","as","bold","role","aria-hidden","width"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,EAAEC,cAAc,QAAO,gCAAgC;AACzE,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,KAAK,QAAO,QAAQ;AAG5B,MAAMC,6BAA6B;AACnC,MAAMC,6BAA6B;IAAC;IAAQ;IAAQ;IAAQ;CAAQ;AAEpE,OAAO,SAASC,iBAAiB,EAC/BC,YAAY,EACZC,oBAAoB,EACpBC,QAAQ,EACRC,SAAS,EACTC,kBAAkB,EAClBC,WAAW,EACXC,UAAU,EACVC,eAAe,wBAAwB,EACvCC,WAAW,EACXC,cAAc,EACdC,cAAc,EAaf;IACC,MAAMC,UAAUf;IAChB,MAAMgB,aAAaH,mBAAmBI;IAEtC,qBACE,MAACC;QAAIC,WAAU;;0BACb,KAAC3B;gBAAM4B,IAAIL;gBAASI,WAAU;0BAAU;;YAIvCH,2BACC,KAACzB;gBACC8B,MAAK;gBACLC,aAAY;gBACZC,cAAW;gBACXC,OAAOZ,eAAe;gBACtBa,UAAU,CAACC,QAAUb,iBAAiBa,MAAMC,MAAM,CAACH,KAAK;gBACxDI,UAAUd;iBAEV;YAEHP,0BAAY,KAACsB,8BAA4B;YAEzC,CAACtB,aAAaH,aAAa0B,MAAM,KAAK,kBACrC,KAACZ;gBAAIC,WAAU;0BACb,cAAA,KAACpB;oBAAKgC,MAAK;8BAAMpB;;iBAEjB;YAEHP,aAAa0B,MAAM,GAAG,kBACrB,KAACrC;gBACCuC,mBAAiBjB;gBACjBS,OAAOnB,wBAAwB;gBAC/B4B,eAAe3B;gBACfa,WAAWlB;0BAEVG,aAAa8B,GAAG,CAAC,CAACC,2BACjB,KAACC;wBAAqDD,YAAYA;uBAA7CA,WAAWE,oBAAoB;iBAGtD;YAEH5B,eAAeC,2BACd,KAACrB;gBACCgC,MAAK;gBACLiB,SAAQ;gBACRP,MAAK;gBACLxB,WAAWC,sBAAsB;gBACjC+B,SAAS7B;0BACV;iBAGC;;;AAGV;AAEA,SAAS0B,eAAe,EAACD,UAAU,EAA2B;IAC5D,MAAM,EAACK,KAAKC,OAAO,EAAEC,WAAW,EAAC,GAAGpD,mBAAoC6C,WAAWQ,IAAI;IAEvF,qBACE,MAAC/C;;0BACC,KAACE;gBAAe8C,OAAO;0BACrB,cAAA,KAAClD;oBAAe8B,OAAOW,WAAWE,oBAAoB;oBAAElB,WAAU;8BAChE,cAAA,KAAC0B;wBAAKL,KAAKC;wBAAStB,WAAU;kCAC5B,cAAA,KAACpB;4BAAK+C,IAAG;4BAAOf,MAAK;4BAAKgB,IAAI;sCAC3BZ,WAAWQ,IAAI;;;;;YAKvBD,4BAAc,KAAC7C;0BAAgBsC,WAAWQ,IAAI;iBAAqB;;;AAG1E;AAEA,SAASd;IACP,qBACE;;0BACE,KAACX;gBAAI8B,MAAK;gBAAS7B,WAAU;0BAAU;;0BAGvC,KAACD;gBAAI+B,eAAY;gBAAO9B,WAAWlB;0BAGhCC,2BAA2BgC,GAAG,CAAC,CAACgB,sBAC/B,KAAChC;wBAECC,WAAU;kCAEV,cAAA,KAACxB;4BAASwB,WAAW,CAAC,KAAK,EAAE+B,OAAO;;uBAH/BA;;;;AASjB"}
@@ -29,15 +29,15 @@ export function CopyableValue({ label, value, note, className }) {
29
29
  }
30
30
  }
31
31
  return /*#__PURE__*/ _jsxs("div", {
32
- className: cn('flex w-full min-w-0 flex-col gap-8', className),
32
+ className: cn('flex w-full min-w-0 flex-col gap-inline', className),
33
33
  children: [
34
34
  /*#__PURE__*/ _jsxs("div", {
35
- className: "flex min-w-0 items-center gap-8",
35
+ className: "flex min-w-0 items-center gap-inline",
36
36
  children: [
37
37
  /*#__PURE__*/ _jsx(Code, {
38
38
  as: "span",
39
39
  variant: "paragraph",
40
- className: "min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-8 py-6 text-foreground-neutral-base",
40
+ className: "min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-tight py-[6px] text-foreground-neutral-base",
41
41
  children: value
42
42
  }),
43
43
  /*#__PURE__*/ _jsx(Button, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/webhook/copyable-value.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useCopyToClipboard} from '@shipfox/react-ui/hooks';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface CopyableValueProps {\n label: string;\n value: string;\n note?: ReactNode;\n className?: string;\n}\n\nexport function CopyableValue({label, value, note, className}: CopyableValueProps) {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const {copy} = useCopyToClipboard({\n text: value,\n onCopy: () => {\n setCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n },\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n async function handleCopy() {\n try {\n await copy();\n } catch {\n toast.error('Could not copy.');\n }\n }\n\n return (\n <div className={cn('flex w-full min-w-0 flex-col gap-8', className)}>\n <div className=\"flex min-w-0 items-center gap-8\">\n <Code\n as=\"span\"\n variant=\"paragraph\"\n className=\"min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-8 py-6 text-foreground-neutral-base\"\n >\n {value}\n </Code>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n iconLeft={copied ? 'check' : 'copy'}\n aria-label={`Copy ${label}`}\n onClick={() => {\n void handleCopy();\n }}\n >\n Copy\n </Button>\n </div>\n {note ? (\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {note}\n </Text>\n ) : null}\n </div>\n );\n}\n"],"names":["Button","useCopyToClipboard","toast","Code","Text","cn","useEffect","useRef","useState","CopyableValue","label","value","note","className","copied","setCopied","timeoutRef","undefined","copy","text","onCopy","current","clearTimeout","setTimeout","handleCopy","error","div","as","variant","type","size","iconLeft","aria-label","onClick"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,IAAI,EAAEC,IAAI,QAAO,+BAA+B;AACxD,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AASlD,OAAO,SAASC,cAAc,EAACC,KAAK,EAAEC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAqB;IAC/E,MAAM,CAACC,QAAQC,UAAU,GAAGP,SAAS;IACrC,MAAMQ,aAAaT,OAAkDU;IACrE,MAAM,EAACC,IAAI,EAAC,GAAGjB,mBAAmB;QAChCkB,MAAMR;QACNS,QAAQ;YACNL,UAAU;YACV,IAAIC,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;YACvDL,WAAWK,OAAO,GAAGE,WAAW,IAAMR,UAAU,QAAQ;QAC1D;IACF;IAEAT,UAAU;QACR,OAAO;YACL,IAAIU,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;QACzD;IACF,GAAG,EAAE;IAEL,eAAeG;QACb,IAAI;YACF,MAAMN;QACR,EAAE,OAAM;YACNhB,MAAMuB,KAAK,CAAC;QACd;IACF;IAEA,qBACE,MAACC;QAAIb,WAAWR,GAAG,sCAAsCQ;;0BACvD,MAACa;gBAAIb,WAAU;;kCACb,KAACV;wBACCwB,IAAG;wBACHC,SAAQ;wBACRf,WAAU;kCAETF;;kCAEH,KAACX;wBACC6B,MAAK;wBACLD,SAAQ;wBACRE,MAAK;wBACLC,UAAUjB,SAAS,UAAU;wBAC7BkB,cAAY,CAAC,KAAK,EAAEtB,OAAO;wBAC3BuB,SAAS;4BACP,KAAKT;wBACP;kCACD;;;;YAIFZ,qBACC,KAACR;gBAAK0B,MAAK;gBAAKjB,WAAU;0BACvBD;iBAED;;;AAGV"}
1
+ {"version":3,"sources":["../../../src/components/webhook/copyable-value.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {useCopyToClipboard} from '@shipfox/react-ui/hooks';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface CopyableValueProps {\n label: string;\n value: string;\n note?: ReactNode;\n className?: string;\n}\n\nexport function CopyableValue({label, value, note, className}: CopyableValueProps) {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const {copy} = useCopyToClipboard({\n text: value,\n onCopy: () => {\n setCopied(true);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n },\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n async function handleCopy() {\n try {\n await copy();\n } catch {\n toast.error('Could not copy.');\n }\n }\n\n return (\n <div className={cn('flex w-full min-w-0 flex-col gap-inline', className)}>\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Code\n as=\"span\"\n variant=\"paragraph\"\n className=\"min-w-0 flex-1 break-all rounded-4 border border-border-neutral-base bg-background-components-base px-tight py-[6px] text-foreground-neutral-base\"\n >\n {value}\n </Code>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n iconLeft={copied ? 'check' : 'copy'}\n aria-label={`Copy ${label}`}\n onClick={() => {\n void handleCopy();\n }}\n >\n Copy\n </Button>\n </div>\n {note ? (\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {note}\n </Text>\n ) : null}\n </div>\n );\n}\n"],"names":["Button","useCopyToClipboard","toast","Code","Text","cn","useEffect","useRef","useState","CopyableValue","label","value","note","className","copied","setCopied","timeoutRef","undefined","copy","text","onCopy","current","clearTimeout","setTimeout","handleCopy","error","div","as","variant","type","size","iconLeft","aria-label","onClick"],"mappings":";AAAA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,IAAI,EAAEC,IAAI,QAAO,+BAA+B;AACxD,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AASlD,OAAO,SAASC,cAAc,EAACC,KAAK,EAAEC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAqB;IAC/E,MAAM,CAACC,QAAQC,UAAU,GAAGP,SAAS;IACrC,MAAMQ,aAAaT,OAAkDU;IACrE,MAAM,EAACC,IAAI,EAAC,GAAGjB,mBAAmB;QAChCkB,MAAMR;QACNS,QAAQ;YACNL,UAAU;YACV,IAAIC,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;YACvDL,WAAWK,OAAO,GAAGE,WAAW,IAAMR,UAAU,QAAQ;QAC1D;IACF;IAEAT,UAAU;QACR,OAAO;YACL,IAAIU,WAAWK,OAAO,EAAEC,aAAaN,WAAWK,OAAO;QACzD;IACF,GAAG,EAAE;IAEL,eAAeG;QACb,IAAI;YACF,MAAMN;QACR,EAAE,OAAM;YACNhB,MAAMuB,KAAK,CAAC;QACd;IACF;IAEA,qBACE,MAACC;QAAIb,WAAWR,GAAG,2CAA2CQ;;0BAC5D,MAACa;gBAAIb,WAAU;;kCACb,KAACV;wBACCwB,IAAG;wBACHC,SAAQ;wBACRf,WAAU;kCAETF;;kCAEH,KAACX;wBACC6B,MAAK;wBACLD,SAAQ;wBACRE,MAAK;wBACLC,UAAUjB,SAAS,UAAU;wBAC7BkB,cAAY,CAAC,KAAK,EAAEtB,OAAO;wBAC3BuB,SAAS;4BACP,KAAKT;wBACP;kCACD;;;;YAIFZ,qBACC,KAACR;gBAAK0B,MAAK;gBAAKjB,WAAU;0BACvBD;iBAED;;;AAGV"}
@@ -88,7 +88,7 @@ export function WebhookCreateModal({ workspaceId, open, onOpenChange, onCreated
88
88
  },
89
89
  children: [
90
90
  /*#__PURE__*/ _jsxs(ModalBody, {
91
- className: "gap-16",
91
+ className: "gap-group",
92
92
  children: [
93
93
  /*#__PURE__*/ _jsx(Text, {
94
94
  size: "sm",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/webhook/webhook-create-modal.tsx"],"sourcesContent":["import {\n createWebhookConnectionBodySchema,\n WEBHOOK_RESERVED_SLUGS,\n webhookSlugSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport type {IntegrationConnection, WebhookConnection} from '#core/models.js';\nimport {useCreateWebhookConnectionMutation} from '#hooks/api/webhook-connections.js';\nimport {webhookCreateErrorToFormError} from './webhook-form-errors.js';\n\ninterface WebhookCreateModalProps {\n workspaceId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCreated: (connection: IntegrationConnection) => void;\n}\n\nexport function WebhookCreateModal({\n workspaceId,\n open,\n onOpenChange,\n onCreated,\n}: WebhookCreateModalProps) {\n const createWebhook = useCreateWebhookConnectionMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [hasManualSlug, setHasManualSlug] = useState(false);\n const nameInputRef = useRef<HTMLInputElement | null>(null);\n\n const form = useForm({\n defaultValues: {name: '', slug: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = createWebhookConnectionBodySchema.parse({\n workspace_id: workspaceId,\n name: value.name.trim(),\n slug: value.slug.trim(),\n });\n const connection = await createWebhook.mutateAsync(body);\n onCreated(webhookConnectionToIntegrationConnection(connection));\n toast.success('Webhook created.');\n onOpenChange(false);\n } catch (error) {\n const mapped = webhookCreateErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n useEffect(() => {\n if (!open) {\n setFormError(undefined);\n setHasManualSlug(false);\n form.reset();\n return;\n }\n requestAnimationFrame(() => nameInputRef.current?.focus());\n }, [form, open]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (createWebhook.isPending && !nextOpen) return;\n onOpenChange(nextOpen);\n };\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Add webhook</ModalTitle>\n <ModalHeader title=\"Add webhook\" showClose={!createWebhook.isPending} />\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <ModalBody className=\"gap-16\">\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Create a named inbound webhook URL for this workspace.\n </Text>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => webhookNameFieldError(value),\n onSubmit: ({value}) => webhookNameFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"webhook-name\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n ref={nameInputRef}\n name=\"name\"\n value={field.state.value}\n onChange={(event) => {\n const nextName = event.target.value;\n field.handleChange(nextName);\n if (!hasManualSlug) {\n form.setFieldValue(\n 'slug',\n nextName.trim() ? suggestWebhookSlug(nextName) : '',\n );\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"Stripe production\"\n disabled={createWebhook.isPending}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"slug\"\n validators={{\n onBlur: ({value}) => webhookSlugFieldError(value),\n onSubmit: ({value}) => webhookSlugFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Slug\"\n id=\"webhook-slug\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n name=\"slug\"\n value={field.state.value}\n onChange={(event) => {\n const nextSlug = event.target.value;\n setHasManualSlug(nextSlug.trim().length > 0);\n field.handleChange(nextSlug);\n if (nextSlug.trim().length === 0 && form.state.values.name.trim()) {\n form.setFieldValue('slug', suggestWebhookSlug(form.state.values.name));\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"webhook-stripe-production\"\n disabled={createWebhook.isPending}\n />\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference in workflows with{' '}\n <Code as=\"span\">\n source: {field.state.value || 'webhook-stripe-production'}\n </Code>\n .\n </Text>\n </FormField>\n )}\n </form.Field>\n </ModalBody>\n <ModalFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={createWebhook.isPending}\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button type=\"submit\" isLoading={createWebhook.isPending}>\n Create\n </Button>\n </ModalFooter>\n </form>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction webhookNameFieldError(value: string): string | undefined {\n return displayNameFieldError(\n value.trim(),\n 'Webhook name',\n createWebhookConnectionBodySchema.shape.name,\n );\n}\n\nfunction webhookSlugFieldError(value: string): string | undefined {\n const trimmed = value.trim();\n if (trimmed.length === 0) return 'Slug is required.';\n if (WEBHOOK_RESERVED_SLUGS.includes(trimmed as (typeof WEBHOOK_RESERVED_SLUGS)[number])) {\n return 'That slug is reserved.';\n }\n if (webhookSlugSchema.safeParse(trimmed).success) return undefined;\n return 'Use lowercase letters, numbers, hyphens, or underscores.';\n}\n\nfunction suggestWebhookSlug(name: string): string {\n const prefix = 'webhook';\n const suffix = name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '')\n .slice(0, 100 - prefix.length - 1)\n .replaceAll(/-+$/g, '');\n\n return suffix ? `${prefix}-${suffix}` : prefix;\n}\n\nfunction webhookConnectionToIntegrationConnection(\n connection: WebhookConnection,\n): IntegrationConnection {\n return {\n id: connection.id,\n workspaceId: connection.workspaceId,\n provider: 'webhook',\n externalAccountId: connection.slug,\n slug: connection.slug,\n displayName: connection.name,\n lifecycleStatus: connection.lifecycleStatus,\n capabilities: [],\n createdAt: connection.createdAt,\n updatedAt: connection.updatedAt,\n };\n}\n"],"names":["createWebhookConnectionBodySchema","WEBHOOK_RESERVED_SLUGS","webhookSlugSchema","displayNameFieldError","Button","Callout","FormField","FormFieldInput","fieldError","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","toast","Code","Text","useForm","useEffect","useRef","useState","useCreateWebhookConnectionMutation","webhookCreateErrorToFormError","WebhookCreateModal","workspaceId","open","onOpenChange","onCreated","createWebhook","formError","setFormError","hasManualSlug","setHasManualSlug","nameInputRef","form","defaultValues","name","slug","onSubmit","value","undefined","body","parse","workspace_id","trim","connection","mutateAsync","webhookConnectionToIntegrationConnection","success","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","reset","requestAnimationFrame","current","focus","handleOpenChange","nextOpen","isPending","aria-describedby","className","title","showClose","noValidate","event","preventDefault","stopPropagation","handleSubmit","size","role","type","Field","validators","onBlur","webhookNameFieldError","label","id","ref","state","onChange","nextName","target","handleChange","setFieldValue","suggestWebhookSlug","handleBlur","placeholder","disabled","webhookSlugFieldError","nextSlug","length","values","as","variant","onClick","isLoading","shape","trimmed","includes","safeParse","prefix","suffix","toLowerCase","replaceAll","slice","provider","externalAccountId","displayName","lifecycleStatus","capabilities","createdAt","updatedAt"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/webhook/webhook-create-modal.tsx"],"sourcesContent":["import {\n createWebhookConnectionBodySchema,\n WEBHOOK_RESERVED_SLUGS,\n webhookSlugSchema,\n} from '@shipfox/api-integration-webhook-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {useEffect, useRef, useState} from 'react';\nimport type {IntegrationConnection, WebhookConnection} from '#core/models.js';\nimport {useCreateWebhookConnectionMutation} from '#hooks/api/webhook-connections.js';\nimport {webhookCreateErrorToFormError} from './webhook-form-errors.js';\n\ninterface WebhookCreateModalProps {\n workspaceId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCreated: (connection: IntegrationConnection) => void;\n}\n\nexport function WebhookCreateModal({\n workspaceId,\n open,\n onOpenChange,\n onCreated,\n}: WebhookCreateModalProps) {\n const createWebhook = useCreateWebhookConnectionMutation();\n const [formError, setFormError] = useState<string | undefined>();\n const [hasManualSlug, setHasManualSlug] = useState(false);\n const nameInputRef = useRef<HTMLInputElement | null>(null);\n\n const form = useForm({\n defaultValues: {name: '', slug: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = createWebhookConnectionBodySchema.parse({\n workspace_id: workspaceId,\n name: value.name.trim(),\n slug: value.slug.trim(),\n });\n const connection = await createWebhook.mutateAsync(body);\n onCreated(webhookConnectionToIntegrationConnection(connection));\n toast.success('Webhook created.');\n onOpenChange(false);\n } catch (error) {\n const mapped = webhookCreateErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n useEffect(() => {\n if (!open) {\n setFormError(undefined);\n setHasManualSlug(false);\n form.reset();\n return;\n }\n requestAnimationFrame(() => nameInputRef.current?.focus());\n }, [form, open]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (createWebhook.isPending && !nextOpen) return;\n onOpenChange(nextOpen);\n };\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Add webhook</ModalTitle>\n <ModalHeader title=\"Add webhook\" showClose={!createWebhook.isPending} />\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <ModalBody className=\"gap-group\">\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Create a named inbound webhook URL for this workspace.\n </Text>\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => webhookNameFieldError(value),\n onSubmit: ({value}) => webhookNameFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Name\"\n id=\"webhook-name\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n ref={nameInputRef}\n name=\"name\"\n value={field.state.value}\n onChange={(event) => {\n const nextName = event.target.value;\n field.handleChange(nextName);\n if (!hasManualSlug) {\n form.setFieldValue(\n 'slug',\n nextName.trim() ? suggestWebhookSlug(nextName) : '',\n );\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"Stripe production\"\n disabled={createWebhook.isPending}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"slug\"\n validators={{\n onBlur: ({value}) => webhookSlugFieldError(value),\n onSubmit: ({value}) => webhookSlugFieldError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Slug\"\n id=\"webhook-slug\"\n error={fieldError(field)}\n className=\"w-full\"\n >\n <FormFieldInput\n name=\"slug\"\n value={field.state.value}\n onChange={(event) => {\n const nextSlug = event.target.value;\n setHasManualSlug(nextSlug.trim().length > 0);\n field.handleChange(nextSlug);\n if (nextSlug.trim().length === 0 && form.state.values.name.trim()) {\n form.setFieldValue('slug', suggestWebhookSlug(form.state.values.name));\n }\n }}\n onBlur={field.handleBlur}\n placeholder=\"webhook-stripe-production\"\n disabled={createWebhook.isPending}\n />\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Reference in workflows with{' '}\n <Code as=\"span\">\n source: {field.state.value || 'webhook-stripe-production'}\n </Code>\n .\n </Text>\n </FormField>\n )}\n </form.Field>\n </ModalBody>\n <ModalFooter>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={createWebhook.isPending}\n onClick={() => onOpenChange(false)}\n >\n Cancel\n </Button>\n <Button type=\"submit\" isLoading={createWebhook.isPending}>\n Create\n </Button>\n </ModalFooter>\n </form>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction webhookNameFieldError(value: string): string | undefined {\n return displayNameFieldError(\n value.trim(),\n 'Webhook name',\n createWebhookConnectionBodySchema.shape.name,\n );\n}\n\nfunction webhookSlugFieldError(value: string): string | undefined {\n const trimmed = value.trim();\n if (trimmed.length === 0) return 'Slug is required.';\n if (WEBHOOK_RESERVED_SLUGS.includes(trimmed as (typeof WEBHOOK_RESERVED_SLUGS)[number])) {\n return 'That slug is reserved.';\n }\n if (webhookSlugSchema.safeParse(trimmed).success) return undefined;\n return 'Use lowercase letters, numbers, hyphens, or underscores.';\n}\n\nfunction suggestWebhookSlug(name: string): string {\n const prefix = 'webhook';\n const suffix = name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '')\n .slice(0, 100 - prefix.length - 1)\n .replaceAll(/-+$/g, '');\n\n return suffix ? `${prefix}-${suffix}` : prefix;\n}\n\nfunction webhookConnectionToIntegrationConnection(\n connection: WebhookConnection,\n): IntegrationConnection {\n return {\n id: connection.id,\n workspaceId: connection.workspaceId,\n provider: 'webhook',\n externalAccountId: connection.slug,\n slug: connection.slug,\n displayName: connection.name,\n lifecycleStatus: connection.lifecycleStatus,\n capabilities: [],\n createdAt: connection.createdAt,\n updatedAt: connection.updatedAt,\n };\n}\n"],"names":["createWebhookConnectionBodySchema","WEBHOOK_RESERVED_SLUGS","webhookSlugSchema","displayNameFieldError","Button","Callout","FormField","FormFieldInput","fieldError","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","toast","Code","Text","useForm","useEffect","useRef","useState","useCreateWebhookConnectionMutation","webhookCreateErrorToFormError","WebhookCreateModal","workspaceId","open","onOpenChange","onCreated","createWebhook","formError","setFormError","hasManualSlug","setHasManualSlug","nameInputRef","form","defaultValues","name","slug","onSubmit","value","undefined","body","parse","workspace_id","trim","connection","mutateAsync","webhookConnectionToIntegrationConnection","success","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","reset","requestAnimationFrame","current","focus","handleOpenChange","nextOpen","isPending","aria-describedby","className","title","showClose","noValidate","event","preventDefault","stopPropagation","handleSubmit","size","role","type","Field","validators","onBlur","webhookNameFieldError","label","id","ref","state","onChange","nextName","target","handleChange","setFieldValue","suggestWebhookSlug","handleBlur","placeholder","disabled","webhookSlugFieldError","nextSlug","length","values","as","variant","onClick","isLoading","shape","trimmed","includes","safeParse","prefix","suffix","toLowerCase","replaceAll","slice","provider","externalAccountId","displayName","lifecycleStatus","capabilities","createdAt","updatedAt"],"mappings":"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"}
@@ -27,9 +27,9 @@ export function WebhookUsageDetails({ workspaceId, connectionId }) {
27
27
  }
28
28
  if (!connection) return null;
29
29
  return /*#__PURE__*/ _jsx("div", {
30
- className: "flex w-full flex-col gap-16",
30
+ className: "flex w-full flex-col gap-group",
31
31
  children: /*#__PURE__*/ _jsxs("div", {
32
- className: "flex w-full min-w-0 flex-col gap-8",
32
+ className: "flex w-full min-w-0 flex-col gap-inline",
33
33
  children: [
34
34
  /*#__PURE__*/ _jsx(Text, {
35
35
  size: "sm",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/webhook/webhook-usage-details.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {ShipfoxLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useWebhookConnectionsQuery} from '#hooks/api/webhook-connections.js';\nimport {CopyableValue} from './copyable-value.js';\nimport {WebhookPublicEndpointAlert} from './webhook-public-endpoint-alert.js';\n\ninterface WebhookUsageDetailsProps {\n workspaceId: string;\n connectionId: string;\n}\n\nexport function WebhookUsageDetails({workspaceId, connectionId}: WebhookUsageDetailsProps) {\n const connectionsQuery = useWebhookConnectionsQuery(workspaceId);\n const connection = connectionsQuery.data?.find((candidate) => candidate.id === connectionId);\n\n if (connectionsQuery.isPending) {\n return (\n <div className=\"flex min-h-80 items-center justify-center\">\n <ShipfoxLoader size={32} animation=\"circular\" color=\"orange\" background=\"light\" />\n </div>\n );\n }\n\n if (connectionsQuery.isError && connectionsQuery.data === undefined) {\n return <QueryLoadError query={connectionsQuery} subject=\"webhook details\" />;\n }\n\n if (!connection) return null;\n\n return (\n <div className=\"flex w-full flex-col gap-16\">\n <div className=\"flex w-full min-w-0 flex-col gap-8\">\n <Text size=\"sm\" bold>\n Inbound URL\n </Text>\n <CopyableValue label=\"inbound URL\" value={connection.inboundUrl} />\n <WebhookPublicEndpointAlert />\n </div>\n </div>\n );\n}\n"],"names":["QueryLoadError","ShipfoxLoader","Text","useWebhookConnectionsQuery","CopyableValue","WebhookPublicEndpointAlert","WebhookUsageDetails","workspaceId","connectionId","connectionsQuery","connection","data","find","candidate","id","isPending","div","className","size","animation","color","background","isError","undefined","query","subject","bold","label","value","inboundUrl"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,aAAa,QAAO,2BAA2B;AACvD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,aAAa,QAAO,sBAAsB;AAClD,SAAQC,0BAA0B,QAAO,qCAAqC;AAO9E,OAAO,SAASC,oBAAoB,EAACC,WAAW,EAAEC,YAAY,EAA2B;IACvF,MAAMC,mBAAmBN,2BAA2BI;IACpD,MAAMG,aAAaD,iBAAiBE,IAAI,EAAEC,KAAK,CAACC,YAAcA,UAAUC,EAAE,KAAKN;IAE/E,IAAIC,iBAAiBM,SAAS,EAAE;QAC9B,qBACE,KAACC;YAAIC,WAAU;sBACb,cAAA,KAAChB;gBAAciB,MAAM;gBAAIC,WAAU;gBAAWC,OAAM;gBAASC,YAAW;;;IAG9E;IAEA,IAAIZ,iBAAiBa,OAAO,IAAIb,iBAAiBE,IAAI,KAAKY,WAAW;QACnE,qBAAO,KAACvB;YAAewB,OAAOf;YAAkBgB,SAAQ;;IAC1D;IAEA,IAAI,CAACf,YAAY,OAAO;IAExB,qBACE,KAACM;QAAIC,WAAU;kBACb,cAAA,MAACD;YAAIC,WAAU;;8BACb,KAACf;oBAAKgB,MAAK;oBAAKQ,IAAI;8BAAC;;8BAGrB,KAACtB;oBAAcuB,OAAM;oBAAcC,OAAOlB,WAAWmB,UAAU;;8BAC/D,KAACxB;;;;AAIT"}
1
+ {"version":3,"sources":["../../../src/components/webhook/webhook-usage-details.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {ShipfoxLoader} from '@shipfox/react-ui/loader';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useWebhookConnectionsQuery} from '#hooks/api/webhook-connections.js';\nimport {CopyableValue} from './copyable-value.js';\nimport {WebhookPublicEndpointAlert} from './webhook-public-endpoint-alert.js';\n\ninterface WebhookUsageDetailsProps {\n workspaceId: string;\n connectionId: string;\n}\n\nexport function WebhookUsageDetails({workspaceId, connectionId}: WebhookUsageDetailsProps) {\n const connectionsQuery = useWebhookConnectionsQuery(workspaceId);\n const connection = connectionsQuery.data?.find((candidate) => candidate.id === connectionId);\n\n if (connectionsQuery.isPending) {\n return (\n <div className=\"flex min-h-80 items-center justify-center\">\n <ShipfoxLoader size={32} animation=\"circular\" color=\"orange\" background=\"light\" />\n </div>\n );\n }\n\n if (connectionsQuery.isError && connectionsQuery.data === undefined) {\n return <QueryLoadError query={connectionsQuery} subject=\"webhook details\" />;\n }\n\n if (!connection) return null;\n\n return (\n <div className=\"flex w-full flex-col gap-group\">\n <div className=\"flex w-full min-w-0 flex-col gap-inline\">\n <Text size=\"sm\" bold>\n Inbound URL\n </Text>\n <CopyableValue label=\"inbound URL\" value={connection.inboundUrl} />\n <WebhookPublicEndpointAlert />\n </div>\n </div>\n );\n}\n"],"names":["QueryLoadError","ShipfoxLoader","Text","useWebhookConnectionsQuery","CopyableValue","WebhookPublicEndpointAlert","WebhookUsageDetails","workspaceId","connectionId","connectionsQuery","connection","data","find","candidate","id","isPending","div","className","size","animation","color","background","isError","undefined","query","subject","bold","label","value","inboundUrl"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,aAAa,QAAO,2BAA2B;AACvD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,0BAA0B,QAAO,oCAAoC;AAC7E,SAAQC,aAAa,QAAO,sBAAsB;AAClD,SAAQC,0BAA0B,QAAO,qCAAqC;AAO9E,OAAO,SAASC,oBAAoB,EAACC,WAAW,EAAEC,YAAY,EAA2B;IACvF,MAAMC,mBAAmBN,2BAA2BI;IACpD,MAAMG,aAAaD,iBAAiBE,IAAI,EAAEC,KAAK,CAACC,YAAcA,UAAUC,EAAE,KAAKN;IAE/E,IAAIC,iBAAiBM,SAAS,EAAE;QAC9B,qBACE,KAACC;YAAIC,WAAU;sBACb,cAAA,KAAChB;gBAAciB,MAAM;gBAAIC,WAAU;gBAAWC,OAAM;gBAASC,YAAW;;;IAG9E;IAEA,IAAIZ,iBAAiBa,OAAO,IAAIb,iBAAiBE,IAAI,KAAKY,WAAW;QACnE,qBAAO,KAACvB;YAAewB,OAAOf;YAAkBgB,SAAQ;;IAC1D;IAEA,IAAI,CAACf,YAAY,OAAO;IAExB,qBACE,KAACM;QAAIC,WAAU;kBACb,cAAA,MAACD;YAAIC,WAAU;;8BACb,KAACf;oBAAKgB,MAAK;oBAAKQ,IAAI;8BAAC;;8BAGrB,KAACtB;oBAAcuB,OAAM;oBAAcC,OAAOlB,WAAWmB,UAAU;;8BAC/D,KAACxB;;;;AAIT"}
@@ -5,6 +5,12 @@ export interface IntegrationProvider {
5
5
  displayName: string;
6
6
  capabilities: IntegrationCapability[];
7
7
  }
8
+ export interface JiraSite {
9
+ cloudId: string;
10
+ name: string;
11
+ url: string;
12
+ scopes: string[];
13
+ }
8
14
  export interface IntegrationConnection {
9
15
  id: string;
10
16
  workspaceId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/core/models.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,aAAa,CAAC;AACrE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,yBAAyB,CAAC;IAC3C,YAAY,EAAE,qBAAqB,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,yBAAyB,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACvC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC,GAChB;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAC,GACrC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,CAAC;AAsGpC,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,UAAU,GAAG,cAAc,CAAC,GACnE,qBAAqB,EAAE,CAazB;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,yBAAyB,GAChC,+BAA+B,CAIjC;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,qBAAqB,EACjC,UAAU,EAAE,qBAAqB,WAGlC;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,qBAAqB,WAEnE"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/core/models.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,aAAa,CAAC;AACrE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,yBAAyB,CAAC;IAC3C,YAAY,EAAE,qBAAqB,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,yBAAyB,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACvC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC,GAChB;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAC,GACrC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAC,CAAC;AA+GpC,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,UAAU,GAAG,cAAc,CAAC,GACnE,qBAAqB,EAAE,CAezB;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,yBAAyB,GAChC,+BAA+B,CAIjC;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,qBAAqB,EACjC,UAAU,EAAE,qBAAqB,WAGlC;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,qBAAqB,WAEnE"}
@@ -101,6 +101,14 @@ const sentryIssueActions = [
101
101
  'archived',
102
102
  'unresolved'
103
103
  ];
104
+ const jiraUsageEvents = [
105
+ 'jira:issue_created',
106
+ 'jira:issue_updated',
107
+ 'jira:issue_deleted',
108
+ 'comment_created',
109
+ 'comment_updated',
110
+ 'comment_deleted'
111
+ ];
104
112
  export function usageEventsForConnection(connection) {
105
113
  if (connection.provider === 'webhook') return [
106
114
  {
@@ -116,6 +124,10 @@ export function usageEventsForConnection(connection) {
116
124
  value: `issue.${action}`,
117
125
  label: `issue.${action}`
118
126
  }));
127
+ if (connection.provider === 'jira') return jiraUsageEvents.map((value)=>({
128
+ value,
129
+ label: value
130
+ }));
119
131
  if (connection.provider === 'linear') return linearUsageEvents.map((value)=>({
120
132
  value,
121
133
  label: value
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/models.ts"],"sourcesContent":["export type IntegrationCapability = 'source_control' | 'agent_tools';\nexport type ConnectionLifecycleStatus = 'active' | 'disabled' | 'error';\n\nexport interface IntegrationProvider {\n provider: string;\n displayName: string;\n capabilities: IntegrationCapability[];\n}\n\nexport interface IntegrationConnection {\n id: string;\n workspaceId: string;\n provider: string;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus: ConnectionLifecycleStatus;\n capabilities: IntegrationCapability[];\n externalUrl?: string | undefined;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Repository {\n connectionId: string;\n externalRepositoryId: string;\n owner: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n visibility: 'public' | 'private' | 'internal' | 'unknown';\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface RepositoryPage {\n repositories: Repository[];\n nextCursor?: string;\n}\n\nexport interface WebhookConnection {\n id: string;\n workspaceId: string;\n name: string;\n slug: string;\n lifecycleStatus: ConnectionLifecycleStatus;\n inboundUrl: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface InstallRedirect {\n installUrl: string;\n}\n\nexport type IntegrationUsageEvent = {\n value: string;\n label: string;\n};\n\nexport type ConnectionLifecyclePresentation =\n | {kind: 'active'}\n | {kind: 'disabled'; label: 'Disabled'}\n | {kind: 'error'; label: 'Error'};\n\nconst githubUsageEvents = [\n 'push',\n 'branch_protection_configuration',\n 'branch_protection_rule',\n 'check_run',\n 'check_suite',\n 'code_scanning_alert',\n 'commit_comment',\n 'create',\n 'custom_property',\n 'custom_property_values',\n 'delete',\n 'dependabot_alert',\n 'deploy_key',\n 'deployment',\n 'deployment_protection_rule',\n 'deployment_review',\n 'deployment_status',\n 'discussion',\n 'discussion_comment',\n 'fork',\n 'github_app_authorization',\n 'gollum',\n 'installation',\n 'installation_repositories',\n 'installation_target',\n 'issue_comment',\n 'issue_dependencies',\n 'issues',\n 'label',\n 'marketplace_purchase',\n 'member',\n 'membership',\n 'merge_group',\n 'meta',\n 'milestone',\n 'org_block',\n 'organization',\n 'package',\n 'page_build',\n 'personal_access_token_request',\n 'ping',\n 'project',\n 'project_card',\n 'project_column',\n 'projects_v2',\n 'projects_v2_item',\n 'projects_v2_status_update',\n 'public',\n 'pull_request',\n 'pull_request_review',\n 'pull_request_review_comment',\n 'pull_request_review_thread',\n 'registry_package',\n 'release',\n 'repository',\n 'repository_advisory',\n 'repository_dispatch',\n 'repository_import',\n 'repository_ruleset',\n 'repository_vulnerability_alert',\n 'secret_scanning_alert',\n 'secret_scanning_alert_location',\n 'secret_scanning_scan',\n 'security_advisory',\n 'security_and_analysis',\n 'sponsorship',\n 'star',\n 'status',\n 'sub_issues',\n 'team',\n 'team_add',\n 'watch',\n 'workflow_dispatch',\n 'workflow_job',\n 'workflow_run',\n] as const;\n\nconst linearUsageEvents = [\n 'Issue.create',\n 'Issue.update',\n 'Issue.remove',\n 'Comment.create',\n 'Comment.update',\n 'Comment.remove',\n 'IssueLabel.create',\n 'IssueLabel.update',\n 'IssueLabel.remove',\n 'Project.create',\n 'Project.update',\n 'Project.remove',\n 'Cycle.create',\n 'Cycle.update',\n 'Cycle.remove',\n 'agentSession.created',\n 'agentSession.prompted',\n] as const;\n\nconst sentryIssueActions = ['created', 'resolved', 'assigned', 'archived', 'unresolved'] as const;\n\nexport function usageEventsForConnection(\n connection: Pick<IntegrationConnection, 'provider' | 'capabilities'>,\n): IntegrationUsageEvent[] {\n if (connection.provider === 'webhook') return [{value: 'received', label: 'received'}];\n if (connection.provider === 'github')\n return githubUsageEvents.map((value) => ({value, label: value}));\n if (connection.provider === 'sentry')\n return sentryIssueActions.map((action) => ({\n value: `issue.${action}`,\n label: `issue.${action}`,\n }));\n if (connection.provider === 'linear')\n return linearUsageEvents.map((value) => ({value, label: value}));\n if (connection.capabilities.includes('source_control')) return [{value: 'push', label: 'push'}];\n return [{value: 'received', label: 'received'}];\n}\n\nexport function connectionLifecyclePresentation(\n status: ConnectionLifecycleStatus,\n): ConnectionLifecyclePresentation {\n if (status === 'disabled') return {kind: 'disabled', label: 'Disabled'};\n if (status === 'error') return {kind: 'error', label: 'Error'};\n return {kind: 'active'};\n}\n\nexport function hasCapability(\n connection: IntegrationConnection,\n capability: IntegrationCapability,\n) {\n return connection.capabilities.includes(capability);\n}\n\nexport function isUsableConnection(connection: IntegrationConnection) {\n return connection.lifecycleStatus === 'active';\n}\n"],"names":["githubUsageEvents","linearUsageEvents","sentryIssueActions","usageEventsForConnection","connection","provider","value","label","map","action","capabilities","includes","connectionLifecyclePresentation","status","kind","hasCapability","capability","isUsableConnection","lifecycleStatus"],"mappings":"AAiEA,MAAMA,oBAAoB;IACxB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMC,oBAAoB;IACxB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMC,qBAAqB;IAAC;IAAW;IAAY;IAAY;IAAY;CAAa;AAExF,OAAO,SAASC,yBACdC,UAAoE;IAEpE,IAAIA,WAAWC,QAAQ,KAAK,WAAW,OAAO;QAAC;YAACC,OAAO;YAAYC,OAAO;QAAU;KAAE;IACtF,IAAIH,WAAWC,QAAQ,KAAK,UAC1B,OAAOL,kBAAkBQ,GAAG,CAAC,CAACF,QAAW,CAAA;YAACA;YAAOC,OAAOD;QAAK,CAAA;IAC/D,IAAIF,WAAWC,QAAQ,KAAK,UAC1B,OAAOH,mBAAmBM,GAAG,CAAC,CAACC,SAAY,CAAA;YACzCH,OAAO,CAAC,MAAM,EAAEG,QAAQ;YACxBF,OAAO,CAAC,MAAM,EAAEE,QAAQ;QAC1B,CAAA;IACF,IAAIL,WAAWC,QAAQ,KAAK,UAC1B,OAAOJ,kBAAkBO,GAAG,CAAC,CAACF,QAAW,CAAA;YAACA;YAAOC,OAAOD;QAAK,CAAA;IAC/D,IAAIF,WAAWM,YAAY,CAACC,QAAQ,CAAC,mBAAmB,OAAO;QAAC;YAACL,OAAO;YAAQC,OAAO;QAAM;KAAE;IAC/F,OAAO;QAAC;YAACD,OAAO;YAAYC,OAAO;QAAU;KAAE;AACjD;AAEA,OAAO,SAASK,gCACdC,MAAiC;IAEjC,IAAIA,WAAW,YAAY,OAAO;QAACC,MAAM;QAAYP,OAAO;IAAU;IACtE,IAAIM,WAAW,SAAS,OAAO;QAACC,MAAM;QAASP,OAAO;IAAO;IAC7D,OAAO;QAACO,MAAM;IAAQ;AACxB;AAEA,OAAO,SAASC,cACdX,UAAiC,EACjCY,UAAiC;IAEjC,OAAOZ,WAAWM,YAAY,CAACC,QAAQ,CAACK;AAC1C;AAEA,OAAO,SAASC,mBAAmBb,UAAiC;IAClE,OAAOA,WAAWc,eAAe,KAAK;AACxC"}
1
+ {"version":3,"sources":["../../src/core/models.ts"],"sourcesContent":["export type IntegrationCapability = 'source_control' | 'agent_tools';\nexport type ConnectionLifecycleStatus = 'active' | 'disabled' | 'error';\n\nexport interface IntegrationProvider {\n provider: string;\n displayName: string;\n capabilities: IntegrationCapability[];\n}\n\nexport interface JiraSite {\n cloudId: string;\n name: string;\n url: string;\n scopes: string[];\n}\n\nexport interface IntegrationConnection {\n id: string;\n workspaceId: string;\n provider: string;\n externalAccountId: string;\n slug: string;\n displayName: string;\n lifecycleStatus: ConnectionLifecycleStatus;\n capabilities: IntegrationCapability[];\n externalUrl?: string | undefined;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Repository {\n connectionId: string;\n externalRepositoryId: string;\n owner: string;\n name: string;\n fullName: string;\n defaultBranch: string;\n visibility: 'public' | 'private' | 'internal' | 'unknown';\n cloneUrl: string;\n htmlUrl: string;\n}\n\nexport interface RepositoryPage {\n repositories: Repository[];\n nextCursor?: string;\n}\n\nexport interface WebhookConnection {\n id: string;\n workspaceId: string;\n name: string;\n slug: string;\n lifecycleStatus: ConnectionLifecycleStatus;\n inboundUrl: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface InstallRedirect {\n installUrl: string;\n}\n\nexport type IntegrationUsageEvent = {\n value: string;\n label: string;\n};\n\nexport type ConnectionLifecyclePresentation =\n | {kind: 'active'}\n | {kind: 'disabled'; label: 'Disabled'}\n | {kind: 'error'; label: 'Error'};\n\nconst githubUsageEvents = [\n 'push',\n 'branch_protection_configuration',\n 'branch_protection_rule',\n 'check_run',\n 'check_suite',\n 'code_scanning_alert',\n 'commit_comment',\n 'create',\n 'custom_property',\n 'custom_property_values',\n 'delete',\n 'dependabot_alert',\n 'deploy_key',\n 'deployment',\n 'deployment_protection_rule',\n 'deployment_review',\n 'deployment_status',\n 'discussion',\n 'discussion_comment',\n 'fork',\n 'github_app_authorization',\n 'gollum',\n 'installation',\n 'installation_repositories',\n 'installation_target',\n 'issue_comment',\n 'issue_dependencies',\n 'issues',\n 'label',\n 'marketplace_purchase',\n 'member',\n 'membership',\n 'merge_group',\n 'meta',\n 'milestone',\n 'org_block',\n 'organization',\n 'package',\n 'page_build',\n 'personal_access_token_request',\n 'ping',\n 'project',\n 'project_card',\n 'project_column',\n 'projects_v2',\n 'projects_v2_item',\n 'projects_v2_status_update',\n 'public',\n 'pull_request',\n 'pull_request_review',\n 'pull_request_review_comment',\n 'pull_request_review_thread',\n 'registry_package',\n 'release',\n 'repository',\n 'repository_advisory',\n 'repository_dispatch',\n 'repository_import',\n 'repository_ruleset',\n 'repository_vulnerability_alert',\n 'secret_scanning_alert',\n 'secret_scanning_alert_location',\n 'secret_scanning_scan',\n 'security_advisory',\n 'security_and_analysis',\n 'sponsorship',\n 'star',\n 'status',\n 'sub_issues',\n 'team',\n 'team_add',\n 'watch',\n 'workflow_dispatch',\n 'workflow_job',\n 'workflow_run',\n] as const;\n\nconst linearUsageEvents = [\n 'Issue.create',\n 'Issue.update',\n 'Issue.remove',\n 'Comment.create',\n 'Comment.update',\n 'Comment.remove',\n 'IssueLabel.create',\n 'IssueLabel.update',\n 'IssueLabel.remove',\n 'Project.create',\n 'Project.update',\n 'Project.remove',\n 'Cycle.create',\n 'Cycle.update',\n 'Cycle.remove',\n 'agentSession.created',\n 'agentSession.prompted',\n] as const;\n\nconst sentryIssueActions = ['created', 'resolved', 'assigned', 'archived', 'unresolved'] as const;\n\nconst jiraUsageEvents = [\n 'jira:issue_created',\n 'jira:issue_updated',\n 'jira:issue_deleted',\n 'comment_created',\n 'comment_updated',\n 'comment_deleted',\n] as const;\n\nexport function usageEventsForConnection(\n connection: Pick<IntegrationConnection, 'provider' | 'capabilities'>,\n): IntegrationUsageEvent[] {\n if (connection.provider === 'webhook') return [{value: 'received', label: 'received'}];\n if (connection.provider === 'github')\n return githubUsageEvents.map((value) => ({value, label: value}));\n if (connection.provider === 'sentry')\n return sentryIssueActions.map((action) => ({\n value: `issue.${action}`,\n label: `issue.${action}`,\n }));\n if (connection.provider === 'jira')\n return jiraUsageEvents.map((value) => ({value, label: value}));\n if (connection.provider === 'linear')\n return linearUsageEvents.map((value) => ({value, label: value}));\n if (connection.capabilities.includes('source_control')) return [{value: 'push', label: 'push'}];\n return [{value: 'received', label: 'received'}];\n}\n\nexport function connectionLifecyclePresentation(\n status: ConnectionLifecycleStatus,\n): ConnectionLifecyclePresentation {\n if (status === 'disabled') return {kind: 'disabled', label: 'Disabled'};\n if (status === 'error') return {kind: 'error', label: 'Error'};\n return {kind: 'active'};\n}\n\nexport function hasCapability(\n connection: IntegrationConnection,\n capability: IntegrationCapability,\n) {\n return connection.capabilities.includes(capability);\n}\n\nexport function isUsableConnection(connection: IntegrationConnection) {\n return connection.lifecycleStatus === 'active';\n}\n"],"names":["githubUsageEvents","linearUsageEvents","sentryIssueActions","jiraUsageEvents","usageEventsForConnection","connection","provider","value","label","map","action","capabilities","includes","connectionLifecyclePresentation","status","kind","hasCapability","capability","isUsableConnection","lifecycleStatus"],"mappings":"AAwEA,MAAMA,oBAAoB;IACxB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMC,oBAAoB;IACxB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMC,qBAAqB;IAAC;IAAW;IAAY;IAAY;IAAY;CAAa;AAExF,MAAMC,kBAAkB;IACtB;IACA;IACA;IACA;IACA;IACA;CACD;AAED,OAAO,SAASC,yBACdC,UAAoE;IAEpE,IAAIA,WAAWC,QAAQ,KAAK,WAAW,OAAO;QAAC;YAACC,OAAO;YAAYC,OAAO;QAAU;KAAE;IACtF,IAAIH,WAAWC,QAAQ,KAAK,UAC1B,OAAON,kBAAkBS,GAAG,CAAC,CAACF,QAAW,CAAA;YAACA;YAAOC,OAAOD;QAAK,CAAA;IAC/D,IAAIF,WAAWC,QAAQ,KAAK,UAC1B,OAAOJ,mBAAmBO,GAAG,CAAC,CAACC,SAAY,CAAA;YACzCH,OAAO,CAAC,MAAM,EAAEG,QAAQ;YACxBF,OAAO,CAAC,MAAM,EAAEE,QAAQ;QAC1B,CAAA;IACF,IAAIL,WAAWC,QAAQ,KAAK,QAC1B,OAAOH,gBAAgBM,GAAG,CAAC,CAACF,QAAW,CAAA;YAACA;YAAOC,OAAOD;QAAK,CAAA;IAC7D,IAAIF,WAAWC,QAAQ,KAAK,UAC1B,OAAOL,kBAAkBQ,GAAG,CAAC,CAACF,QAAW,CAAA;YAACA;YAAOC,OAAOD;QAAK,CAAA;IAC/D,IAAIF,WAAWM,YAAY,CAACC,QAAQ,CAAC,mBAAmB,OAAO;QAAC;YAACL,OAAO;YAAQC,OAAO;QAAM;KAAE;IAC/F,OAAO;QAAC;YAACD,OAAO;YAAYC,OAAO;QAAU;KAAE;AACjD;AAEA,OAAO,SAASK,gCACdC,MAAiC;IAEjC,IAAIA,WAAW,YAAY,OAAO;QAACC,MAAM;QAAYP,OAAO;IAAU;IACtE,IAAIM,WAAW,SAAS,OAAO;QAACC,MAAM;QAASP,OAAO;IAAO;IAC7D,OAAO;QAACO,MAAM;IAAQ;AACxB;AAEA,OAAO,SAASC,cACdX,UAAiC,EACjCY,UAAiC;IAEjC,OAAOZ,WAAWM,YAAY,CAACC,QAAQ,CAACK;AAC1C;AAEA,OAAO,SAASC,mBAAmBb,UAAiC;IAClE,OAAOA,WAAWc,eAAe,KAAK;AACxC"}