@friggframework/devtools 2.0.0-next.61 → 2.0.0-next.63

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 (142) hide show
  1. package/infrastructure/domains/shared/utilities/base-definition-factory.js +15 -1
  2. package/package.json +15 -7
  3. package/.eslintrc.json +0 -3
  4. package/CHANGELOG.md +0 -132
  5. package/layers/prisma/.build-complete +0 -3
  6. package/layers/prisma/nodejs/package.json +0 -8
  7. package/management-ui/.eslintrc.js +0 -22
  8. package/management-ui/components.json +0 -21
  9. package/management-ui/docs/phase2-integration-guide.md +0 -320
  10. package/management-ui/index.html +0 -13
  11. package/management-ui/package.json +0 -76
  12. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +0 -302
  13. package/management-ui/postcss.config.js +0 -6
  14. package/management-ui/server/api/backend.js +0 -256
  15. package/management-ui/server/api/cli.js +0 -315
  16. package/management-ui/server/api/codegen.js +0 -663
  17. package/management-ui/server/api/connections.js +0 -857
  18. package/management-ui/server/api/discovery.js +0 -185
  19. package/management-ui/server/api/environment/index.js +0 -1
  20. package/management-ui/server/api/environment/router.js +0 -378
  21. package/management-ui/server/api/environment.js +0 -328
  22. package/management-ui/server/api/integrations.js +0 -876
  23. package/management-ui/server/api/logs.js +0 -248
  24. package/management-ui/server/api/monitoring.js +0 -282
  25. package/management-ui/server/api/open-ide.js +0 -31
  26. package/management-ui/server/api/project.js +0 -1029
  27. package/management-ui/server/api/users/sessions.js +0 -371
  28. package/management-ui/server/api/users/simulation.js +0 -254
  29. package/management-ui/server/api/users.js +0 -362
  30. package/management-ui/server/api-contract.md +0 -275
  31. package/management-ui/server/index.js +0 -873
  32. package/management-ui/server/middleware/errorHandler.js +0 -93
  33. package/management-ui/server/middleware/security.js +0 -32
  34. package/management-ui/server/processManager.js +0 -296
  35. package/management-ui/server/server.js +0 -346
  36. package/management-ui/server/services/aws-monitor.js +0 -413
  37. package/management-ui/server/services/npm-registry.js +0 -347
  38. package/management-ui/server/services/template-engine.js +0 -538
  39. package/management-ui/server/utils/cliIntegration.js +0 -220
  40. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  41. package/management-ui/server/utils/environment/awsParameterStore.js +0 -275
  42. package/management-ui/server/utils/environment/encryption.js +0 -278
  43. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  44. package/management-ui/server/utils/import-commonjs.js +0 -28
  45. package/management-ui/server/utils/response.js +0 -83
  46. package/management-ui/server/websocket/handler.js +0 -325
  47. package/management-ui/src/App.jsx +0 -25
  48. package/management-ui/src/assets/FriggLogo.svg +0 -1
  49. package/management-ui/src/components/AppRouter.jsx +0 -65
  50. package/management-ui/src/components/Button.jsx +0 -70
  51. package/management-ui/src/components/Card.jsx +0 -97
  52. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  53. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  54. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  55. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  56. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  57. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  58. package/management-ui/src/components/IntegrationCard.jsx +0 -481
  59. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -770
  60. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  61. package/management-ui/src/components/IntegrationStatus.jsx +0 -336
  62. package/management-ui/src/components/Layout.jsx +0 -716
  63. package/management-ui/src/components/LoadingSpinner.jsx +0 -113
  64. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  65. package/management-ui/src/components/SessionMonitor.jsx +0 -350
  66. package/management-ui/src/components/StatusBadge.jsx +0 -208
  67. package/management-ui/src/components/UserContextSwitcher.jsx +0 -212
  68. package/management-ui/src/components/UserSimulation.jsx +0 -327
  69. package/management-ui/src/components/Welcome.jsx +0 -434
  70. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  71. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  72. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  73. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  74. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  75. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  76. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  77. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  78. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  79. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  80. package/management-ui/src/components/codegen/index.js +0 -10
  81. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  82. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  83. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  84. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  85. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  86. package/management-ui/src/components/connections/index.js +0 -5
  87. package/management-ui/src/components/index.js +0 -21
  88. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  89. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  90. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  91. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  92. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  93. package/management-ui/src/components/monitoring/index.js +0 -6
  94. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  95. package/management-ui/src/components/theme-provider.jsx +0 -52
  96. package/management-ui/src/components/theme-toggle.jsx +0 -39
  97. package/management-ui/src/components/ui/badge.tsx +0 -36
  98. package/management-ui/src/components/ui/button.test.jsx +0 -56
  99. package/management-ui/src/components/ui/button.tsx +0 -57
  100. package/management-ui/src/components/ui/card.tsx +0 -76
  101. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  102. package/management-ui/src/components/ui/select.tsx +0 -157
  103. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  104. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  105. package/management-ui/src/hooks/useSocket.jsx +0 -58
  106. package/management-ui/src/index.css +0 -193
  107. package/management-ui/src/lib/utils.ts +0 -6
  108. package/management-ui/src/main.jsx +0 -10
  109. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  110. package/management-ui/src/pages/Connections.jsx +0 -252
  111. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -633
  112. package/management-ui/src/pages/Dashboard.jsx +0 -311
  113. package/management-ui/src/pages/Environment.jsx +0 -314
  114. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -669
  115. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -567
  116. package/management-ui/src/pages/IntegrationTest.jsx +0 -742
  117. package/management-ui/src/pages/Integrations.jsx +0 -253
  118. package/management-ui/src/pages/Monitoring.jsx +0 -17
  119. package/management-ui/src/pages/Simulation.jsx +0 -155
  120. package/management-ui/src/pages/Users.jsx +0 -492
  121. package/management-ui/src/services/api.js +0 -41
  122. package/management-ui/src/services/apiModuleService.js +0 -193
  123. package/management-ui/src/services/websocket-handlers.js +0 -120
  124. package/management-ui/src/test/api/project.test.js +0 -273
  125. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  126. package/management-ui/src/test/mocks/server.js +0 -178
  127. package/management-ui/src/test/setup.js +0 -61
  128. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  129. package/management-ui/src/utils/repository.js +0 -98
  130. package/management-ui/src/utils/repository.test.js +0 -118
  131. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  132. package/management-ui/tailwind.config.js +0 -63
  133. package/management-ui/tsconfig.json +0 -37
  134. package/management-ui/tsconfig.node.json +0 -10
  135. package/management-ui/vite.config.js +0 -26
  136. package/management-ui/vitest.config.js +0 -38
  137. package/test/auther-definition-method-tester.js +0 -45
  138. package/test/index.js +0 -9
  139. package/test/integration-validator.js +0 -2
  140. package/test/mock-api-readme.md +0 -102
  141. package/test/mock-api.js +0 -284
  142. package/test/mock-integration.js +0 -78
@@ -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'