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