@friggframework/devtools 2.0.0--canary.400.bed3308.0 → 2.0.0--canary.400.545e7a8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/frigg-cli/build-command/index.js +2 -15
  2. package/frigg-cli/deploy-command/index.js +2 -15
  3. package/frigg-cli/index.js +4 -66
  4. package/frigg-cli/install-command/index.js +2 -15
  5. package/frigg-cli/start-command/index.js +2 -17
  6. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +19 -43
  7. package/infrastructure/IAM-POLICY-TEMPLATES.md +1 -1
  8. package/infrastructure/frigg-deployment-iam-stack.yaml +2 -16
  9. package/infrastructure/iam-generator.js +6 -129
  10. package/infrastructure/iam-policy-basic.json +5 -29
  11. package/infrastructure/iam-policy-full.json +5 -28
  12. package/infrastructure/serverless-template.js +3 -190
  13. package/infrastructure/serverless-template.test.js +0 -12
  14. package/management-ui/dist/assets/index-CbM64Oba.js +1221 -0
  15. package/management-ui/dist/assets/index-CkvseXTC.css +1 -0
  16. package/management-ui/{index.html → dist/index.html} +2 -1
  17. package/package.json +5 -5
  18. package/frigg-cli/.eslintrc.js +0 -141
  19. package/frigg-cli/__tests__/jest.config.js +0 -102
  20. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -483
  21. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -418
  22. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -592
  23. package/frigg-cli/__tests__/utils/command-tester.js +0 -170
  24. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  25. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  26. package/frigg-cli/__tests__/utils/test-setup.js +0 -286
  27. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -312
  28. package/frigg-cli/generate-command/azure-generator.js +0 -43
  29. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  30. package/frigg-cli/generate-command/index.js +0 -350
  31. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  32. package/frigg-cli/package.json +0 -75
  33. package/frigg-cli/ui-command/index.js +0 -167
  34. package/frigg-cli/utils/app-resolver.js +0 -319
  35. package/frigg-cli/utils/backend-path.js +0 -38
  36. package/frigg-cli/utils/process-manager.js +0 -199
  37. package/frigg-cli/utils/repo-detection.js +0 -405
  38. package/management-ui/.eslintrc.js +0 -22
  39. package/management-ui/README.md +0 -203
  40. package/management-ui/components.json +0 -21
  41. package/management-ui/merge-conflict-cleaner.py +0 -371
  42. package/management-ui/package-lock.json +0 -10997
  43. package/management-ui/package.json +0 -76
  44. package/management-ui/postcss.config.js +0 -6
  45. package/management-ui/server/api/backend.js +0 -256
  46. package/management-ui/server/api/cli.js +0 -315
  47. package/management-ui/server/api/codegen.js +0 -663
  48. package/management-ui/server/api/connections.js +0 -857
  49. package/management-ui/server/api/discovery.js +0 -185
  50. package/management-ui/server/api/environment/index.js +0 -1
  51. package/management-ui/server/api/environment/router.js +0 -378
  52. package/management-ui/server/api/environment.js +0 -328
  53. package/management-ui/server/api/integrations.js +0 -479
  54. package/management-ui/server/api/logs.js +0 -248
  55. package/management-ui/server/api/monitoring.js +0 -282
  56. package/management-ui/server/api/open-ide.js +0 -31
  57. package/management-ui/server/api/project.js +0 -553
  58. package/management-ui/server/api/users/sessions.js +0 -371
  59. package/management-ui/server/api/users/simulation.js +0 -254
  60. package/management-ui/server/api/users.js +0 -362
  61. package/management-ui/server/api-contract.md +0 -275
  62. package/management-ui/server/index.js +0 -428
  63. package/management-ui/server/middleware/errorHandler.js +0 -70
  64. package/management-ui/server/middleware/security.js +0 -32
  65. package/management-ui/server/processManager.js +0 -296
  66. package/management-ui/server/server.js +0 -188
  67. package/management-ui/server/services/aws-monitor.js +0 -413
  68. package/management-ui/server/services/npm-registry.js +0 -347
  69. package/management-ui/server/services/template-engine.js +0 -538
  70. package/management-ui/server/utils/cliIntegration.js +0 -220
  71. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  72. package/management-ui/server/utils/environment/awsParameterStore.js +0 -264
  73. package/management-ui/server/utils/environment/encryption.js +0 -278
  74. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  75. package/management-ui/server/utils/import-commonjs.js +0 -28
  76. package/management-ui/server/utils/response.js +0 -83
  77. package/management-ui/server/websocket/handler.js +0 -325
  78. package/management-ui/src/App.jsx +0 -51
  79. package/management-ui/src/components/AppRouter.jsx +0 -65
  80. package/management-ui/src/components/Button.jsx +0 -2
  81. package/management-ui/src/components/Card.jsx +0 -9
  82. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  83. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  84. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  85. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  86. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  87. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  88. package/management-ui/src/components/IntegrationCard.jsx +0 -199
  89. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -490
  90. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  91. package/management-ui/src/components/IntegrationStatus.jsx +0 -235
  92. package/management-ui/src/components/Layout.jsx +0 -250
  93. package/management-ui/src/components/LoadingSpinner.jsx +0 -45
  94. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  95. package/management-ui/src/components/SessionMonitor.jsx +0 -255
  96. package/management-ui/src/components/StatusBadge.jsx +0 -70
  97. package/management-ui/src/components/UserContextSwitcher.jsx +0 -154
  98. package/management-ui/src/components/UserSimulation.jsx +0 -299
  99. package/management-ui/src/components/Welcome.jsx +0 -434
  100. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  101. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  102. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  103. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  104. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  105. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  106. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  107. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  108. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  109. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  110. package/management-ui/src/components/codegen/index.js +0 -10
  111. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  112. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  113. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  114. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  115. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  116. package/management-ui/src/components/connections/index.js +0 -5
  117. package/management-ui/src/components/index.js +0 -21
  118. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  119. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  120. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  121. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  122. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  123. package/management-ui/src/components/monitoring/index.js +0 -6
  124. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  125. package/management-ui/src/components/theme-provider.jsx +0 -52
  126. package/management-ui/src/components/theme-toggle.jsx +0 -39
  127. package/management-ui/src/components/ui/badge.tsx +0 -36
  128. package/management-ui/src/components/ui/button.test.jsx +0 -56
  129. package/management-ui/src/components/ui/button.tsx +0 -57
  130. package/management-ui/src/components/ui/card.tsx +0 -76
  131. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  132. package/management-ui/src/components/ui/select.tsx +0 -157
  133. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  134. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  135. package/management-ui/src/hooks/useSocket.jsx +0 -58
  136. package/management-ui/src/index.css +0 -194
  137. package/management-ui/src/lib/utils.ts +0 -6
  138. package/management-ui/src/main.jsx +0 -10
  139. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  140. package/management-ui/src/pages/Connections.jsx +0 -252
  141. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -427
  142. package/management-ui/src/pages/Dashboard.jsx +0 -311
  143. package/management-ui/src/pages/Environment.jsx +0 -314
  144. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -544
  145. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -479
  146. package/management-ui/src/pages/IntegrationTest.jsx +0 -494
  147. package/management-ui/src/pages/Integrations.jsx +0 -254
  148. package/management-ui/src/pages/Monitoring.jsx +0 -17
  149. package/management-ui/src/pages/Simulation.jsx +0 -155
  150. package/management-ui/src/pages/Users.jsx +0 -492
  151. package/management-ui/src/services/api.js +0 -41
  152. package/management-ui/src/services/apiModuleService.js +0 -193
  153. package/management-ui/src/services/websocket-handlers.js +0 -120
  154. package/management-ui/src/test/api/project.test.js +0 -273
  155. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  156. package/management-ui/src/test/mocks/server.js +0 -178
  157. package/management-ui/src/test/setup.js +0 -61
  158. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  159. package/management-ui/src/utils/repository.js +0 -98
  160. package/management-ui/src/utils/repository.test.js +0 -118
  161. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  162. package/management-ui/tailwind.config.js +0 -63
  163. package/management-ui/tsconfig.json +0 -37
  164. package/management-ui/tsconfig.node.json +0 -10
  165. package/management-ui/vite.config.js +0 -26
  166. package/management-ui/vitest.config.js +0 -38
  167. /package/management-ui/{src/assets/FriggLogo.svg → dist/assets/FriggLogo-B7Xx8ZW1.svg} +0 -0
@@ -1,200 +0,0 @@
1
- import React, { useState } from 'react'
2
- import { Button } from '../Button'
3
- import LoadingSpinner from '../LoadingSpinner'
4
- import StatusBadge from '../StatusBadge'
5
- import api from '../../services/api'
6
-
7
- const ConnectionTester = ({ connection, onTestComplete }) => {
8
- const [testing, setTesting] = useState(false)
9
- const [testResult, setTestResult] = useState(null)
10
- const [testDetails, setTestDetails] = useState([])
11
-
12
- const runConnectionTest = async () => {
13
- setTesting(true)
14
- setTestResult(null)
15
- setTestDetails([])
16
-
17
- const steps = [
18
- { id: 'auth', name: 'Validating authentication', status: 'pending' },
19
- { id: 'api', name: 'Testing API connectivity', status: 'pending' },
20
- { id: 'permissions', name: 'Checking permissions', status: 'pending' },
21
- { id: 'data', name: 'Fetching sample data', status: 'pending' }
22
- ]
23
-
24
- setTestDetails(steps)
25
-
26
- try {
27
- // Run comprehensive connection test
28
- const response = await api.post(`/api/connections/${connection.id}/test`, {
29
- comprehensive: true
30
- })
31
-
32
- const { results, summary } = response.data
33
-
34
- // Update test details with results
35
- const updatedSteps = steps.map(step => {
36
- const result = results[step.id]
37
- return {
38
- ...step,
39
- status: result?.success ? 'success' : 'failed',
40
- message: result?.message,
41
- latency: result?.latency,
42
- error: result?.error
43
- }
44
- })
45
-
46
- setTestDetails(updatedSteps)
47
- setTestResult(summary)
48
-
49
- if (onTestComplete) {
50
- onTestComplete(summary)
51
- }
52
-
53
- } catch (error) {
54
- setTestResult({
55
- success: false,
56
- error: error.response?.data?.error || 'Connection test failed'
57
- })
58
-
59
- // Mark all steps as failed
60
- setTestDetails(steps.map(step => ({
61
- ...step,
62
- status: 'failed',
63
- error: 'Test aborted due to error'
64
- })))
65
- } finally {
66
- setTesting(false)
67
- }
68
- }
69
-
70
- const getStatusColor = (status) => {
71
- switch (status) {
72
- case 'success':
73
- return 'text-green-600'
74
- case 'failed':
75
- return 'text-red-600'
76
- case 'pending':
77
- return 'text-gray-400'
78
- default:
79
- return 'text-gray-600'
80
- }
81
- }
82
-
83
- const getStatusIcon = (status) => {
84
- switch (status) {
85
- case 'success':
86
- return (
87
- <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
88
- <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
89
- </svg>
90
- )
91
- case 'failed':
92
- return (
93
- <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
94
- <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
95
- </svg>
96
- )
97
- case 'pending':
98
- return <LoadingSpinner size="sm" />
99
- default:
100
- return null
101
- }
102
- }
103
-
104
- return (
105
- <div className="bg-white rounded-lg shadow p-6">
106
- <div className="flex items-center justify-between mb-6">
107
- <h3 className="text-lg font-semibold text-gray-900">Connection Test</h3>
108
- {testResult && (
109
- <StatusBadge
110
- status={testResult.success ? 'success' : 'error'}
111
- text={testResult.success ? 'Passed' : 'Failed'}
112
- />
113
- )}
114
- </div>
115
-
116
- {!testing && !testResult && (
117
- <div>
118
- <p className="text-sm text-gray-600 mb-4">
119
- Run a comprehensive test to validate this connection's authentication,
120
- API access, and permissions.
121
- </p>
122
- <Button onClick={runConnectionTest} variant="primary">
123
- Run Connection Test
124
- </Button>
125
- </div>
126
- )}
127
-
128
- {(testing || testDetails.length > 0) && (
129
- <div className="space-y-3">
130
- {testDetails.map((step) => (
131
- <div key={step.id} className="flex items-start space-x-3">
132
- <div className={`flex-shrink-0 ${getStatusColor(step.status)}`}>
133
- {getStatusIcon(step.status)}
134
- </div>
135
- <div className="flex-1">
136
- <p className="text-sm font-medium text-gray-900">{step.name}</p>
137
- {step.message && (
138
- <p className="text-sm text-gray-600 mt-1">{step.message}</p>
139
- )}
140
- {step.error && (
141
- <p className="text-sm text-red-600 mt-1">{step.error}</p>
142
- )}
143
- {step.latency && (
144
- <p className="text-xs text-gray-500 mt-1">
145
- Response time: {step.latency}ms
146
- </p>
147
- )}
148
- </div>
149
- </div>
150
- ))}
151
- </div>
152
- )}
153
-
154
- {testResult && (
155
- <div className="mt-6 pt-6 border-t border-gray-200">
156
- <h4 className="text-sm font-semibold text-gray-900 mb-3">Test Summary</h4>
157
-
158
- {testResult.success ? (
159
- <div className="bg-green-50 border border-green-200 rounded-md p-4">
160
- <p className="text-sm text-green-800">
161
- All tests passed successfully. The connection is working properly.
162
- </p>
163
- {testResult.avgLatency && (
164
- <p className="text-xs text-green-700 mt-2">
165
- Average response time: {testResult.avgLatency}ms
166
- </p>
167
- )}
168
- </div>
169
- ) : (
170
- <div className="bg-red-50 border border-red-200 rounded-md p-4">
171
- <p className="text-sm text-red-800">
172
- {testResult.error || 'One or more tests failed. Please check the connection configuration.'}
173
- </p>
174
- {testResult.suggestion && (
175
- <p className="text-xs text-red-700 mt-2">
176
- Suggestion: {testResult.suggestion}
177
- </p>
178
- )}
179
- </div>
180
- )}
181
-
182
- {!testing && (
183
- <div className="mt-4 flex space-x-3">
184
- <Button onClick={runConnectionTest} variant="secondary" size="sm">
185
- Run Again
186
- </Button>
187
- {testResult.success && testResult.canRefreshToken && (
188
- <Button variant="secondary" size="sm">
189
- Refresh Token
190
- </Button>
191
- )}
192
- </div>
193
- )}
194
- </div>
195
- )}
196
- </div>
197
- )
198
- }
199
-
200
- export default ConnectionTester
@@ -1,292 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react'
2
- import { Button } from '../Button'
3
- import LoadingSpinner from '../LoadingSpinner'
4
- import api from '../../services/api'
5
-
6
- const EntityRelationshipMapper = ({ connectionId }) => {
7
- const [entities, setEntities] = useState([])
8
- const [relationships, setRelationships] = useState([])
9
- const [loading, setLoading] = useState(true)
10
- const [selectedEntity, setSelectedEntity] = useState(null)
11
- const [viewMode, setViewMode] = useState('graph') // graph or list
12
- const canvasRef = useRef(null)
13
-
14
- useEffect(() => {
15
- fetchEntityData()
16
- }, [connectionId])
17
-
18
- useEffect(() => {
19
- if (viewMode === 'graph' && entities.length > 0) {
20
- drawEntityGraph()
21
- }
22
- }, [entities, relationships, viewMode, selectedEntity])
23
-
24
- const fetchEntityData = async () => {
25
- setLoading(true)
26
- try {
27
- const [entitiesRes, relationshipsRes] = await Promise.all([
28
- api.get(`/api/connections/${connectionId}/entities`),
29
- api.get(`/api/connections/${connectionId}/relationships`)
30
- ])
31
-
32
- setEntities(entitiesRes.data.entities || [])
33
- setRelationships(relationshipsRes.data.relationships || [])
34
- } catch (error) {
35
- console.error('Failed to fetch entity data:', error)
36
- } finally {
37
- setLoading(false)
38
- }
39
- }
40
-
41
- const drawEntityGraph = () => {
42
- const canvas = canvasRef.current
43
- if (!canvas) return
44
-
45
- const ctx = canvas.getContext('2d')
46
- const width = canvas.width = canvas.offsetWidth
47
- const height = canvas.height = canvas.offsetHeight
48
-
49
- // Clear canvas
50
- ctx.clearRect(0, 0, width, height)
51
-
52
- // Calculate positions for entities (simple circular layout)
53
- const centerX = width / 2
54
- const centerY = height / 2
55
- const radius = Math.min(width, height) * 0.35
56
-
57
- const entityPositions = {}
58
- entities.forEach((entity, index) => {
59
- const angle = (index / entities.length) * 2 * Math.PI
60
- entityPositions[entity.id] = {
61
- x: centerX + radius * Math.cos(angle),
62
- y: centerY + radius * Math.sin(angle),
63
- entity
64
- }
65
- })
66
-
67
- // Draw relationships (lines)
68
- ctx.strokeStyle = '#e5e7eb'
69
- ctx.lineWidth = 1
70
- relationships.forEach(rel => {
71
- const from = entityPositions[rel.fromId]
72
- const to = entityPositions[rel.toId]
73
- if (from && to) {
74
- ctx.beginPath()
75
- ctx.moveTo(from.x, from.y)
76
- ctx.lineTo(to.x, to.y)
77
- ctx.stroke()
78
-
79
- // Draw relationship label
80
- const midX = (from.x + to.x) / 2
81
- const midY = (from.y + to.y) / 2
82
- ctx.fillStyle = '#6b7280'
83
- ctx.font = '10px sans-serif'
84
- ctx.textAlign = 'center'
85
- ctx.fillText(rel.type, midX, midY)
86
- }
87
- })
88
-
89
- // Draw entities (circles)
90
- Object.values(entityPositions).forEach(({ x, y, entity }) => {
91
- const isSelected = selectedEntity?.id === entity.id
92
-
93
- // Draw circle
94
- ctx.beginPath()
95
- ctx.arc(x, y, 30, 0, 2 * Math.PI)
96
- ctx.fillStyle = isSelected ? '#2563eb' : '#ffffff'
97
- ctx.fill()
98
- ctx.strokeStyle = isSelected ? '#2563eb' : '#d1d5db'
99
- ctx.lineWidth = 2
100
- ctx.stroke()
101
-
102
- // Draw entity name
103
- ctx.fillStyle = isSelected ? '#ffffff' : '#111827'
104
- ctx.font = '12px sans-serif'
105
- ctx.textAlign = 'center'
106
- ctx.textBaseline = 'middle'
107
- ctx.fillText(entity.name || entity.type, x, y)
108
-
109
- // Draw entity type
110
- ctx.fillStyle = isSelected ? '#dbeafe' : '#6b7280'
111
- ctx.font = '10px sans-serif'
112
- ctx.fillText(entity.type, x, y + 40)
113
- })
114
- }
115
-
116
- const handleCanvasClick = (e) => {
117
- const canvas = canvasRef.current
118
- const rect = canvas.getBoundingClientRect()
119
- const x = e.clientX - rect.left
120
- const y = e.clientY - rect.top
121
-
122
- // Check if click is on an entity
123
- const centerX = canvas.width / 2
124
- const centerY = canvas.height / 2
125
- const radius = Math.min(canvas.width, canvas.height) * 0.35
126
-
127
- entities.forEach((entity, index) => {
128
- const angle = (index / entities.length) * 2 * Math.PI
129
- const entityX = centerX + radius * Math.cos(angle)
130
- const entityY = centerY + radius * Math.sin(angle)
131
-
132
- const distance = Math.sqrt(Math.pow(x - entityX, 2) + Math.pow(y - entityY, 2))
133
- if (distance <= 30) {
134
- setSelectedEntity(entity)
135
- }
136
- })
137
- }
138
-
139
- const syncEntities = async () => {
140
- setLoading(true)
141
- try {
142
- await api.post(`/api/connections/${connectionId}/sync`)
143
- await fetchEntityData()
144
- } catch (error) {
145
- console.error('Failed to sync entities:', error)
146
- } finally {
147
- setLoading(false)
148
- }
149
- }
150
-
151
- if (loading) {
152
- return (
153
- <div className="flex items-center justify-center p-8">
154
- <LoadingSpinner size="lg" />
155
- </div>
156
- )
157
- }
158
-
159
- return (
160
- <div className="bg-white rounded-lg shadow p-6">
161
- <div className="flex items-center justify-between mb-6">
162
- <h3 className="text-lg font-semibold text-gray-900">Entity Relationships</h3>
163
- <div className="flex space-x-2">
164
- <div className="flex rounded-md shadow-sm">
165
- <button
166
- onClick={() => setViewMode('graph')}
167
- className={`px-3 py-1 text-sm font-medium rounded-l-md ${
168
- viewMode === 'graph'
169
- ? 'bg-blue-600 text-white'
170
- : 'bg-white text-gray-700 hover:bg-gray-50'
171
- }`}
172
- >
173
- Graph
174
- </button>
175
- <button
176
- onClick={() => setViewMode('list')}
177
- className={`px-3 py-1 text-sm font-medium rounded-r-md ${
178
- viewMode === 'list'
179
- ? 'bg-blue-600 text-white'
180
- : 'bg-white text-gray-700 hover:bg-gray-50'
181
- }`}
182
- >
183
- List
184
- </button>
185
- </div>
186
- <Button onClick={syncEntities} size="sm" variant="secondary">
187
- Sync Entities
188
- </Button>
189
- </div>
190
- </div>
191
-
192
- {entities.length === 0 ? (
193
- <div className="text-center py-8">
194
- <p className="text-gray-500 mb-4">No entities found for this connection.</p>
195
- <Button onClick={syncEntities} variant="primary">
196
- Sync Entities Now
197
- </Button>
198
- </div>
199
- ) : (
200
- <>
201
- {viewMode === 'graph' ? (
202
- <div className="relative">
203
- <canvas
204
- ref={canvasRef}
205
- width={600}
206
- height={400}
207
- className="w-full h-96 border border-gray-200 rounded-lg cursor-pointer"
208
- onClick={handleCanvasClick}
209
- />
210
-
211
- {selectedEntity && (
212
- <div className="mt-4 p-4 bg-gray-50 rounded-lg">
213
- <h4 className="font-medium text-gray-900 mb-2">
214
- {selectedEntity.name || selectedEntity.id}
215
- </h4>
216
- <dl className="grid grid-cols-2 gap-2 text-sm">
217
- <dt className="text-gray-500">Type:</dt>
218
- <dd className="text-gray-900">{selectedEntity.type}</dd>
219
- <dt className="text-gray-500">External ID:</dt>
220
- <dd className="text-gray-900 font-mono text-xs">
221
- {selectedEntity.externalId}
222
- </dd>
223
- <dt className="text-gray-500">Created:</dt>
224
- <dd className="text-gray-900">
225
- {new Date(selectedEntity.createdAt).toLocaleDateString()}
226
- </dd>
227
- </dl>
228
- </div>
229
- )}
230
- </div>
231
- ) : (
232
- <div className="space-y-4">
233
- {entities.map((entity) => (
234
- <div
235
- key={entity.id}
236
- className="border border-gray-200 rounded-lg p-4 hover:bg-gray-50 cursor-pointer"
237
- onClick={() => setSelectedEntity(entity)}
238
- >
239
- <div className="flex items-center justify-between">
240
- <div>
241
- <h4 className="font-medium text-gray-900">
242
- {entity.name || entity.id}
243
- </h4>
244
- <p className="text-sm text-gray-500">
245
- Type: {entity.type} | External ID: {entity.externalId}
246
- </p>
247
- </div>
248
- <div className="text-sm text-gray-500">
249
- {relationships.filter(r =>
250
- r.fromId === entity.id || r.toId === entity.id
251
- ).length} relationships
252
- </div>
253
- </div>
254
-
255
- {selectedEntity?.id === entity.id && (
256
- <div className="mt-4 pt-4 border-t border-gray-200">
257
- <h5 className="text-sm font-medium text-gray-900 mb-2">
258
- Relationships:
259
- </h5>
260
- <div className="space-y-1">
261
- {relationships
262
- .filter(r => r.fromId === entity.id || r.toId === entity.id)
263
- .map((rel, index) => (
264
- <p key={index} className="text-sm text-gray-600">
265
- {rel.fromId === entity.id ? 'Has' : 'Is'} {rel.type}
266
- {' '}
267
- {rel.fromId === entity.id
268
- ? entities.find(e => e.id === rel.toId)?.name || rel.toId
269
- : entities.find(e => e.id === rel.fromId)?.name || rel.fromId
270
- }
271
- </p>
272
- ))}
273
- </div>
274
- </div>
275
- )}
276
- </div>
277
- ))}
278
- </div>
279
- )}
280
-
281
- <div className="mt-6 pt-4 border-t border-gray-200">
282
- <p className="text-sm text-gray-500">
283
- Total: {entities.length} entities, {relationships.length} relationships
284
- </p>
285
- </div>
286
- </>
287
- )}
288
- </div>
289
- )
290
- }
291
-
292
- export default EntityRelationshipMapper
@@ -1,204 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { Button } from '../Button'
3
- import LoadingSpinner from '../LoadingSpinner'
4
- import api from '../../services/api'
5
-
6
- const OAuthFlow = ({ integration, onSuccess, onCancel }) => {
7
- const [loading, setLoading] = useState(false)
8
- const [error, setError] = useState(null)
9
- const [authUrl, setAuthUrl] = useState(null)
10
- const [pollingForToken, setPollingForToken] = useState(false)
11
-
12
- // OAuth configuration for different providers
13
- const oauthConfigs = {
14
- slack: {
15
- authEndpoint: 'https://slack.com/oauth/v2/authorize',
16
- scopes: ['channels:read', 'chat:write', 'users:read'],
17
- responseType: 'code'
18
- },
19
- google: {
20
- authEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
21
- scopes: ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/drive.readonly'],
22
- responseType: 'code'
23
- },
24
- salesforce: {
25
- authEndpoint: 'https://login.salesforce.com/services/oauth2/authorize',
26
- scopes: ['api', 'refresh_token'],
27
- responseType: 'code'
28
- },
29
- hubspot: {
30
- authEndpoint: 'https://app.hubspot.com/oauth/authorize',
31
- scopes: ['contacts', 'oauth'],
32
- responseType: 'code'
33
- }
34
- }
35
-
36
- const startOAuthFlow = async () => {
37
- setLoading(true)
38
- setError(null)
39
-
40
- try {
41
- // Get OAuth initialization data from server
42
- const response = await api.post(`/api/connections/oauth/init`, {
43
- integration: integration.name,
44
- provider: integration.provider || integration.name.toLowerCase()
45
- })
46
-
47
- const { authUrl: serverAuthUrl, state, codeVerifier } = response.data
48
-
49
- // Store state and code verifier for later verification
50
- sessionStorage.setItem('oauth_state', state)
51
- if (codeVerifier) {
52
- sessionStorage.setItem('oauth_verifier', codeVerifier)
53
- }
54
-
55
- // Open OAuth window
56
- const authWindow = window.open(
57
- serverAuthUrl,
58
- 'OAuth Authorization',
59
- 'width=600,height=700,left=200,top=100'
60
- )
61
-
62
- // Start polling for completion
63
- setPollingForToken(true)
64
- pollForAuthCompletion(state, authWindow)
65
-
66
- } catch (err) {
67
- setError(err.response?.data?.error || 'Failed to initialize OAuth flow')
68
- setLoading(false)
69
- }
70
- }
71
-
72
- const pollForAuthCompletion = async (state, authWindow) => {
73
- const pollInterval = setInterval(async () => {
74
- // Check if window was closed
75
- if (authWindow && authWindow.closed) {
76
- clearInterval(pollInterval)
77
- setPollingForToken(false)
78
- setLoading(false)
79
- setError('Authorization window was closed')
80
- return
81
- }
82
-
83
- try {
84
- // Check if auth is complete
85
- const response = await api.get(`/api/connections/oauth/status/${state}`)
86
-
87
- if (response.data.status === 'completed') {
88
- clearInterval(pollInterval)
89
- setPollingForToken(false)
90
- setLoading(false)
91
-
92
- if (authWindow && !authWindow.closed) {
93
- authWindow.close()
94
- }
95
-
96
- // Clean up session storage
97
- sessionStorage.removeItem('oauth_state')
98
- sessionStorage.removeItem('oauth_verifier')
99
-
100
- onSuccess(response.data.connection)
101
- } else if (response.data.status === 'error') {
102
- clearInterval(pollInterval)
103
- setPollingForToken(false)
104
- setLoading(false)
105
- setError(response.data.error || 'OAuth authorization failed')
106
-
107
- if (authWindow && !authWindow.closed) {
108
- authWindow.close()
109
- }
110
- }
111
- } catch (err) {
112
- // Continue polling on network errors
113
- console.error('Polling error:', err)
114
- }
115
- }, 1500)
116
-
117
- // Stop polling after 5 minutes
118
- setTimeout(() => {
119
- clearInterval(pollInterval)
120
- setPollingForToken(false)
121
- setLoading(false)
122
- setError('OAuth authorization timed out')
123
-
124
- if (authWindow && !authWindow.closed) {
125
- authWindow.close()
126
- }
127
- }, 300000)
128
- }
129
-
130
- const handleManualEntry = () => {
131
- // TODO: Implement manual credential entry
132
- console.log('Manual entry not yet implemented')
133
- }
134
-
135
- return (
136
- <div className="p-6 bg-white rounded-lg shadow-lg max-w-md mx-auto">
137
- <h3 className="text-lg font-semibold mb-4">
138
- Connect to {integration.displayName || integration.name}
139
- </h3>
140
-
141
- {error && (
142
- <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-md">
143
- <p className="text-sm text-red-800">{error}</p>
144
- </div>
145
- )}
146
-
147
- {!loading && !pollingForToken && (
148
- <>
149
- <p className="text-sm text-gray-600 mb-6">
150
- Click the button below to authorize access to your {integration.displayName || integration.name} account.
151
- You'll be redirected to {integration.displayName || integration.name} to complete the authorization.
152
- </p>
153
-
154
- <div className="space-y-3">
155
- <Button
156
- onClick={startOAuthFlow}
157
- className="w-full"
158
- variant="primary"
159
- >
160
- Authorize with {integration.displayName || integration.name}
161
- </Button>
162
-
163
- <Button
164
- onClick={onCancel}
165
- className="w-full"
166
- variant="secondary"
167
- >
168
- Cancel
169
- </Button>
170
-
171
- {integration.supportsApiKey && (
172
- <button
173
- onClick={handleManualEntry}
174
- className="w-full text-sm text-gray-600 hover:text-gray-800 underline"
175
- >
176
- Enter credentials manually
177
- </button>
178
- )}
179
- </div>
180
- </>
181
- )}
182
-
183
- {(loading || pollingForToken) && (
184
- <div className="text-center py-8">
185
- <LoadingSpinner size="lg" />
186
- <p className="mt-4 text-sm text-gray-600">
187
- {pollingForToken
188
- ? 'Waiting for authorization... Please complete the process in the popup window.'
189
- : 'Initializing OAuth flow...'}
190
- </p>
191
- </div>
192
- )}
193
-
194
- <div className="mt-6 pt-4 border-t border-gray-200">
195
- <p className="text-xs text-gray-500">
196
- By connecting, you agree to share the requested permissions with this application.
197
- Your credentials are securely stored and can be revoked at any time.
198
- </p>
199
- </div>
200
- </div>
201
- )
202
- }
203
-
204
- export default OAuthFlow
@@ -1,5 +0,0 @@
1
- export { default as OAuthFlow } from './OAuthFlow'
2
- export { default as ConnectionTester } from './ConnectionTester'
3
- export { default as ConnectionHealthMonitor } from './ConnectionHealthMonitor'
4
- export { default as EntityRelationshipMapper } from './EntityRelationshipMapper'
5
- export { default as ConnectionConfigForm } from './ConnectionConfigForm'