insforge 1.3.0 → 1.4.8

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 (269) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/auth/package.json +5 -3
  3. package/auth/src/lib/broadcastService.ts +115 -117
  4. package/auth/src/lib/insforge.ts +8 -0
  5. package/auth/src/main.tsx +2 -4
  6. package/auth/src/pages/SignInPage.tsx +60 -60
  7. package/auth/src/pages/SignUpPage.tsx +60 -60
  8. package/auth/src/pages/VerifyEmailPage.tsx +18 -0
  9. package/auth/tsconfig.json +2 -1
  10. package/backend/package.json +10 -6
  11. package/backend/src/api/middlewares/rate-limiters.ts +127 -127
  12. package/backend/src/api/routes/ai/index.routes.ts +475 -468
  13. package/backend/src/api/routes/auth/index.routes.ts +85 -32
  14. package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
  15. package/backend/src/api/routes/database/index.routes.ts +2 -0
  16. package/backend/src/api/routes/database/records.routes.ts +39 -175
  17. package/backend/src/api/routes/database/rpc.routes.ts +69 -0
  18. package/backend/src/api/routes/deployments/index.routes.ts +192 -0
  19. package/backend/src/api/routes/docs/index.routes.ts +3 -2
  20. package/backend/src/api/routes/email/index.routes.ts +35 -35
  21. package/backend/src/api/routes/functions/index.routes.ts +3 -3
  22. package/backend/src/api/routes/metadata/index.routes.ts +26 -0
  23. package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
  24. package/backend/src/infra/database/database.manager.ts +0 -10
  25. package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
  26. package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
  27. package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
  28. package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
  29. package/backend/src/infra/security/token.manager.ts +1 -4
  30. package/backend/src/providers/ai/openrouter.provider.ts +12 -3
  31. package/backend/src/providers/database/base.provider.ts +39 -0
  32. package/backend/src/providers/database/cloud.provider.ts +159 -0
  33. package/backend/src/providers/deployments/vercel.provider.ts +516 -0
  34. package/backend/src/server.ts +19 -7
  35. package/backend/src/services/ai/ai-config.service.ts +6 -6
  36. package/backend/src/services/ai/ai-model.service.ts +60 -60
  37. package/backend/src/services/ai/ai-usage.service.ts +7 -7
  38. package/backend/src/services/ai/chat-completion.service.ts +415 -220
  39. package/backend/src/services/ai/helpers.ts +64 -64
  40. package/backend/src/services/ai/index.ts +13 -13
  41. package/backend/src/services/auth/auth-config.service.ts +4 -4
  42. package/backend/src/services/auth/auth-otp.service.ts +6 -6
  43. package/backend/src/services/auth/auth.service.ts +134 -74
  44. package/backend/src/services/auth/index.ts +4 -4
  45. package/backend/src/services/auth/oauth-config.service.ts +12 -12
  46. package/backend/src/services/database/database-advance.service.ts +19 -55
  47. package/backend/src/services/database/database-table.service.ts +38 -85
  48. package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
  49. package/backend/src/services/deployments/deployment.service.ts +693 -0
  50. package/backend/src/services/functions/function.service.ts +61 -41
  51. package/backend/src/services/logs/audit.service.ts +10 -10
  52. package/backend/src/services/secrets/secret.service.ts +101 -27
  53. package/backend/src/services/storage/storage.service.ts +30 -30
  54. package/backend/src/services/usage/usage.service.ts +6 -6
  55. package/backend/src/types/ai.ts +8 -0
  56. package/backend/src/types/auth.ts +5 -1
  57. package/backend/src/types/database.ts +2 -0
  58. package/backend/src/types/deployments.ts +33 -0
  59. package/backend/src/types/storage.ts +1 -1
  60. package/backend/src/types/webhooks.ts +45 -0
  61. package/backend/src/utils/cookies.ts +34 -35
  62. package/backend/src/utils/environment.ts +0 -14
  63. package/backend/src/utils/s3-config-loader.ts +64 -64
  64. package/backend/src/utils/seed.ts +334 -301
  65. package/backend/src/utils/sql-parser.ts +126 -0
  66. package/backend/src/utils/utils.ts +114 -114
  67. package/backend/src/utils/validations.ts +10 -10
  68. package/backend/tests/local/test-rpc.sh +141 -0
  69. package/backend/tests/local/test-secrets.sh +1 -1
  70. package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
  71. package/backend/tests/manual/test-rawsql-modes.sh +24 -24
  72. package/backend/tests/unit/database-advance.test.ts +326 -0
  73. package/backend/tests/unit/helpers.test.ts +2 -2
  74. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
  75. package/docker-compose.prod.yml +1 -1
  76. package/docker-compose.yml +1 -1
  77. package/docs/agent-docs/deployment.md +79 -0
  78. package/docs/changelog.mdx +165 -72
  79. package/docs/core-concepts/ai/architecture.mdx +1 -23
  80. package/docs/core-concepts/ai/sdk.mdx +26 -1
  81. package/docs/core-concepts/authentication/architecture.mdx +6 -8
  82. package/docs/core-concepts/authentication/sdk.mdx +387 -91
  83. package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
  84. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
  85. package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
  86. package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
  87. package/docs/core-concepts/database/architecture.mdx +58 -21
  88. package/docs/core-concepts/database/pgvector.mdx +138 -0
  89. package/docs/core-concepts/database/sdk.mdx +17 -17
  90. package/docs/core-concepts/deployments/architecture.mdx +152 -0
  91. package/docs/core-concepts/email/architecture.mdx +4 -2
  92. package/docs/core-concepts/functions/architecture.mdx +1 -1
  93. package/docs/core-concepts/functions/sdk.mdx +0 -1
  94. package/docs/core-concepts/realtime/architecture.mdx +1 -1
  95. package/docs/core-concepts/storage/architecture.mdx +1 -1
  96. package/docs/core-concepts/storage/sdk.mdx +25 -25
  97. package/docs/docs.json +14 -6
  98. package/docs/favicon.png +0 -0
  99. package/docs/favicon.svg +3 -18
  100. package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
  101. package/docs/images/changelog/dec-2025/moreModels.png +0 -0
  102. package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
  103. package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
  104. package/docs/images/changelog/dec-2025/realtime2.png +0 -0
  105. package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
  106. package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
  107. package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
  108. package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
  109. package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
  110. package/docs/images/mcp-setup/claude-code-connect.png +0 -0
  111. package/docs/images/mcp-setup/cline-1.png +0 -0
  112. package/docs/images/mcp-setup/cline-2.png +0 -0
  113. package/docs/images/mcp-setup/cline-3.png +0 -0
  114. package/docs/images/mcp-setup/connect-project.png +0 -0
  115. package/docs/images/mcp-setup/copilot-1.png +0 -0
  116. package/docs/images/mcp-setup/copilot-2.png +0 -0
  117. package/docs/images/mcp-setup/copilot-3.png +0 -0
  118. package/docs/images/mcp-setup/mcp-json-1.png +0 -0
  119. package/docs/images/mcp-setup/mcp-json-2.png +0 -0
  120. package/docs/images/mcp-setup/qoder-1.png +0 -0
  121. package/docs/images/mcp-setup/qoder-2.png +0 -0
  122. package/docs/images/mcp-setup/roocode-1.png +0 -0
  123. package/docs/images/mcp-setup/roocode-2.png +0 -0
  124. package/docs/images/mcp-setup/trae-1.png +0 -0
  125. package/docs/images/mcp-setup/trae-2.png +0 -0
  126. package/docs/images/mcp-setup/trae-3.png +0 -0
  127. package/docs/images/mcp-setup/trae-4.png +0 -0
  128. package/docs/images/mcp-setup/trae-5.png +0 -0
  129. package/docs/images/mcp-setup/windsurf-1.png +0 -0
  130. package/docs/images/mcp-setup/windsurf-2.png +0 -0
  131. package/docs/insforge-instructions-sdk.md +7 -3
  132. package/docs/introduction.mdx +9 -8
  133. package/docs/mcp-setup.mdx +332 -0
  134. package/docs/oauth-server.mdx +563 -0
  135. package/docs/partnership.mdx +79 -10
  136. package/docs/quickstart.mdx +1 -1
  137. package/docs/vscode-extension.mdx +74 -0
  138. package/eslint.config.js +1 -0
  139. package/examples/response-examples.md +1 -1
  140. package/frontend/package.json +1 -1
  141. package/frontend/src/App.tsx +8 -3
  142. package/frontend/src/assets/logos/antigravity.svg +1 -0
  143. package/frontend/src/assets/logos/copilot.svg +10 -0
  144. package/frontend/src/assets/logos/deepseek.svg +139 -0
  145. package/frontend/src/assets/logos/kiro.svg +9 -0
  146. package/frontend/src/assets/logos/qoder.svg +4 -0
  147. package/frontend/src/assets/logos/qwen.svg +15 -0
  148. package/frontend/src/components/CodeBlock.tsx +2 -2
  149. package/frontend/src/components/ConnectCTA.tsx +3 -2
  150. package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
  151. package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
  152. package/frontend/src/components/datagrid/index.ts +1 -1
  153. package/frontend/src/components/index.ts +0 -1
  154. package/frontend/src/components/layout/AppHeader.tsx +4 -27
  155. package/frontend/src/components/layout/AppSidebar.tsx +85 -100
  156. package/frontend/src/components/layout/Layout.tsx +34 -32
  157. package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
  158. package/frontend/src/components/radix/Select.tsx +151 -151
  159. package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
  160. package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
  161. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
  162. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
  163. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
  164. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
  165. package/frontend/src/features/ai/components/index.ts +6 -6
  166. package/frontend/src/features/ai/helpers.ts +147 -141
  167. package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
  168. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
  169. package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
  170. package/frontend/src/features/auth/components/index.ts +5 -5
  171. package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
  172. package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
  173. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
  174. package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
  175. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
  176. package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
  177. package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
  178. package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
  179. package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
  180. package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
  181. package/frontend/src/features/database/constants.ts +16 -28
  182. package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
  183. package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
  184. package/frontend/src/features/database/hooks/useTables.ts +5 -7
  185. package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
  186. package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
  187. package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
  188. package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
  189. package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
  190. package/frontend/src/features/database/services/advance.service.ts +1 -15
  191. package/frontend/src/features/database/services/record.service.ts +4 -20
  192. package/frontend/src/features/database/services/table.service.ts +1 -4
  193. package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
  194. package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
  195. package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
  196. package/frontend/src/features/database/templates/notion-clone.ts +8 -8
  197. package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
  198. package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
  199. package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
  200. package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
  201. package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
  202. package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
  203. package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
  204. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
  205. package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
  206. package/frontend/src/features/functions/components/index.ts +5 -5
  207. package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
  208. package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
  209. package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
  210. package/frontend/src/features/functions/services/function.service.ts +8 -25
  211. package/frontend/src/features/functions/services/secret.service.ts +23 -41
  212. package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
  213. package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
  214. package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
  215. package/frontend/src/features/logs/components/index.ts +1 -0
  216. package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
  217. package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
  218. package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
  219. package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
  220. package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
  221. package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
  222. package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
  223. package/frontend/src/features/onboard/components/index.ts +9 -4
  224. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
  225. package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
  226. package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
  227. package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
  228. package/frontend/src/features/onboard/index.ts +17 -13
  229. package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
  230. package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
  231. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
  232. package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
  233. package/frontend/src/index.css +249 -249
  234. package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
  235. package/frontend/src/lib/hooks/useMetadata.ts +45 -1
  236. package/frontend/src/lib/hooks/useModal.tsx +2 -0
  237. package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
  238. package/frontend/src/lib/services/metadata.service.ts +20 -3
  239. package/frontend/src/lib/utils/menuItems.ts +223 -207
  240. package/frontend/src/lib/utils/utils.ts +196 -196
  241. package/functions/server.ts +315 -315
  242. package/functions/worker-template.js +1 -1
  243. package/openapi/ai.yaml +115 -5
  244. package/openapi/auth.yaml +97 -17
  245. package/openapi/logs.yaml +0 -2
  246. package/openapi/metadata.yaml +0 -2
  247. package/openapi/records.yaml +21 -21
  248. package/openapi/tables.yaml +1 -2
  249. package/package.json +1 -1
  250. package/shared-schemas/package.json +1 -1
  251. package/shared-schemas/src/ai-api.schema.ts +251 -143
  252. package/shared-schemas/src/ai.schema.ts +63 -63
  253. package/shared-schemas/src/auth-api.schema.ts +34 -6
  254. package/shared-schemas/src/auth.schema.ts +17 -10
  255. package/shared-schemas/src/cloud-events.schema.ts +26 -0
  256. package/shared-schemas/src/deployments-api.schema.ts +55 -0
  257. package/shared-schemas/src/deployments.schema.ts +30 -0
  258. package/shared-schemas/src/docs.schema.ts +8 -2
  259. package/shared-schemas/src/email-api.schema.ts +30 -30
  260. package/shared-schemas/src/functions-api.schema.ts +13 -4
  261. package/shared-schemas/src/functions.schema.ts +1 -1
  262. package/shared-schemas/src/index.ts +22 -18
  263. package/shared-schemas/src/metadata.schema.ts +30 -4
  264. package/shared-schemas/src/secrets-api.schema.ts +44 -0
  265. package/shared-schemas/src/secrets.schema.ts +15 -0
  266. package/zeabur/README.md +13 -0
  267. package/zeabur/template.yml +20 -51
  268. package/backend/src/types/profile.ts +0 -55
  269. package/frontend/src/components/ProjectInfoModal.tsx +0 -128
@@ -1,135 +1,135 @@
1
- import { useState, useEffect, useMemo } from 'react';
2
- import {
3
- Button,
4
- Dialog,
5
- DialogContent,
6
- DialogHeader,
7
- DialogTitle,
8
- DialogFooter,
9
- } from '@/components';
10
- import { CreateAIConfigurationRequest, ModalitySchema } from '@insforge/shared-schemas';
11
- import { useAIConfigs } from '../hooks/useAIConfigs';
12
- import { useToast } from '@/lib/hooks/useToast';
13
- import { ModalityFilterSidebar } from './ModalityFilterSidebar';
14
- import { ModelSelectionGrid } from './ModelSelectionGrid';
15
-
16
- interface ModelSelectionDialogProps {
17
- open: boolean;
18
- onOpenChange: (open: boolean) => void;
19
- onSuccess: (data: CreateAIConfigurationRequest) => void;
20
- }
21
-
22
- export function ModelSelectionDialog({ open, onOpenChange, onSuccess }: ModelSelectionDialogProps) {
23
- const { allAvailableModels, configuredModelIds, getFilteredModels } = useAIConfigs();
24
- const { showToast } = useToast();
25
-
26
- const [selectedInputModalities, setSelectedInputModalities] = useState<ModalitySchema[]>([]);
27
- const [selectedOutputModalities, setSelectedOutputModalities] = useState<ModalitySchema[]>([]);
28
- const [selectedModelId, setSelectedModelId] = useState<string>(''); // This is the modelId of the selected model
29
-
30
- // Reset state when dialog opens/closes
31
- useEffect(() => {
32
- if (!open) {
33
- setSelectedInputModalities([]);
34
- setSelectedOutputModalities([]);
35
- setSelectedModelId('');
36
- }
37
- }, [open]);
38
-
39
- // Use the existing filtered models logic from useAIConfigs
40
- const filteredModels = useMemo(() => {
41
- return getFilteredModels(selectedInputModalities, selectedOutputModalities);
42
- }, [getFilteredModels, selectedInputModalities, selectedOutputModalities]);
43
-
44
- // Reset model selection when modalities change
45
- useEffect(() => {
46
- if (selectedModelId) {
47
- const stillAvailable = filteredModels.some((model) => model.modelId === selectedModelId);
48
- if (!stillAvailable) {
49
- setSelectedModelId('');
50
- }
51
- }
52
- }, [selectedInputModalities, selectedOutputModalities, filteredModels, selectedModelId]);
53
-
54
- const handleFormSubmit = (e: React.FormEvent) => {
55
- e.preventDefault();
56
-
57
- const selectedModel = allAvailableModels.find((model) => model.modelId === selectedModelId);
58
-
59
- if (!selectedModel) {
60
- showToast('Selected model not found', 'error');
61
- return;
62
- }
63
-
64
- const createData: CreateAIConfigurationRequest = {
65
- provider: selectedModel.provider,
66
- modelId: selectedModel.modelId,
67
- inputModality: selectedModel.inputModality,
68
- outputModality: selectedModel.outputModality,
69
- };
70
-
71
- onSuccess(createData);
72
- onOpenChange(false);
73
- };
74
-
75
- const handleCancel = () => {
76
- onOpenChange(false);
77
- };
78
-
79
- return (
80
- <Dialog open={open} onOpenChange={onOpenChange}>
81
- <DialogContent
82
- aria-describedby={undefined}
83
- className="max-w-5xl h-[80vh] p-0 gap-0 border-zinc-200 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.1)] flex flex-col"
84
- >
85
- <DialogHeader className="pl-6 pr-4 py-3 flex flex-col gap-1 justify-start border-b border-zinc-200 dark:border-neutral-700 flex-shrink-0">
86
- <DialogTitle className="text-lg font-semibold text-zinc-950 dark:text-white">
87
- Select AI Model for Integration
88
- </DialogTitle>
89
- </DialogHeader>
90
-
91
- <form
92
- id="model-selection-form"
93
- onSubmit={handleFormSubmit}
94
- className="flex flex-col flex-1 overflow-hidden"
95
- >
96
- <div className="flex gap-5 py-6 pl-6 flex-1 overflow-hidden">
97
- <ModalityFilterSidebar
98
- inputModalities={selectedInputModalities}
99
- outputModalities={selectedOutputModalities}
100
- onInputChange={setSelectedInputModalities}
101
- onOutputChange={setSelectedOutputModalities}
102
- />
103
- <div className="flex-1 overflow-y-auto">
104
- <ModelSelectionGrid
105
- models={filteredModels}
106
- selectedModelId={selectedModelId}
107
- onSelectModel={setSelectedModelId}
108
- configuredModelIds={configuredModelIds}
109
- />
110
- </div>
111
- </div>
112
- </form>
113
-
114
- <DialogFooter className="p-6 gap-3 border-t border-zinc-200 dark:border-neutral-700 flex-shrink-0">
115
- <Button
116
- type="button"
117
- variant="outline"
118
- onClick={handleCancel}
119
- className="h-9 px-3 py-2 rounded-sm text-sm font-medium dark:bg-neutral-600 dark:text-zinc-300 dark:border-neutral-600 dark:hover:bg-neutral-700"
120
- >
121
- Cancel
122
- </Button>
123
- <Button
124
- type="submit"
125
- form="model-selection-form"
126
- disabled={!selectedModelId}
127
- className="h-9 px-3 py-2 rounded-sm text-sm font-medium bg-zinc-950 text-white hover:bg-zinc-800 disabled:opacity-40 dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
128
- >
129
- Add Integration
130
- </Button>
131
- </DialogFooter>
132
- </DialogContent>
133
- </Dialog>
134
- );
135
- }
1
+ import { useState, useEffect, useMemo } from 'react';
2
+ import {
3
+ Button,
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogFooter,
9
+ } from '@/components';
10
+ import { CreateAIConfigurationRequest, ModalitySchema } from '@insforge/shared-schemas';
11
+ import { useAIConfigs } from '../hooks/useAIConfigs';
12
+ import { useToast } from '@/lib/hooks/useToast';
13
+ import { ModalityFilterSidebar } from './ModalityFilterSidebar';
14
+ import { ModelSelectionGrid } from './ModelSelectionGrid';
15
+
16
+ interface ModelSelectionDialogProps {
17
+ open: boolean;
18
+ onOpenChange: (open: boolean) => void;
19
+ onSuccess: (data: CreateAIConfigurationRequest) => void;
20
+ }
21
+
22
+ export function ModelSelectionDialog({ open, onOpenChange, onSuccess }: ModelSelectionDialogProps) {
23
+ const { allAvailableModels, configuredModelIds, getFilteredModels } = useAIConfigs();
24
+ const { showToast } = useToast();
25
+
26
+ const [selectedInputModalities, setSelectedInputModalities] = useState<ModalitySchema[]>([]);
27
+ const [selectedOutputModalities, setSelectedOutputModalities] = useState<ModalitySchema[]>([]);
28
+ const [selectedModelId, setSelectedModelId] = useState<string>(''); // This is the modelId of the selected model
29
+
30
+ // Reset state when dialog opens/closes
31
+ useEffect(() => {
32
+ if (!open) {
33
+ setSelectedInputModalities([]);
34
+ setSelectedOutputModalities([]);
35
+ setSelectedModelId('');
36
+ }
37
+ }, [open]);
38
+
39
+ // Use the existing filtered models logic from useAIConfigs
40
+ const filteredModels = useMemo(() => {
41
+ return getFilteredModels(selectedInputModalities, selectedOutputModalities);
42
+ }, [getFilteredModels, selectedInputModalities, selectedOutputModalities]);
43
+
44
+ // Reset model selection when modalities change
45
+ useEffect(() => {
46
+ if (selectedModelId) {
47
+ const stillAvailable = filteredModels.some((model) => model.modelId === selectedModelId);
48
+ if (!stillAvailable) {
49
+ setSelectedModelId('');
50
+ }
51
+ }
52
+ }, [selectedInputModalities, selectedOutputModalities, filteredModels, selectedModelId]);
53
+
54
+ const handleFormSubmit = (e: React.FormEvent) => {
55
+ e.preventDefault();
56
+
57
+ const selectedModel = allAvailableModels.find((model) => model.modelId === selectedModelId);
58
+
59
+ if (!selectedModel) {
60
+ showToast('Selected model not found', 'error');
61
+ return;
62
+ }
63
+
64
+ const createData: CreateAIConfigurationRequest = {
65
+ provider: selectedModel.provider,
66
+ modelId: selectedModel.modelId,
67
+ inputModality: selectedModel.inputModality,
68
+ outputModality: selectedModel.outputModality,
69
+ };
70
+
71
+ onSuccess(createData);
72
+ onOpenChange(false);
73
+ };
74
+
75
+ const handleCancel = () => {
76
+ onOpenChange(false);
77
+ };
78
+
79
+ return (
80
+ <Dialog open={open} onOpenChange={onOpenChange}>
81
+ <DialogContent
82
+ aria-describedby={undefined}
83
+ className="max-w-5xl h-[80vh] p-0 gap-0 border-zinc-200 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.1)] flex flex-col"
84
+ >
85
+ <DialogHeader className="pl-6 pr-4 py-3 flex flex-col gap-1 justify-start border-b border-zinc-200 dark:border-neutral-700 flex-shrink-0">
86
+ <DialogTitle className="text-lg font-semibold text-zinc-950 dark:text-white">
87
+ Select AI Model for Integration
88
+ </DialogTitle>
89
+ </DialogHeader>
90
+
91
+ <form
92
+ id="model-selection-form"
93
+ onSubmit={handleFormSubmit}
94
+ className="flex flex-col flex-1 overflow-hidden"
95
+ >
96
+ <div className="flex gap-5 py-6 pl-6 flex-1 overflow-hidden">
97
+ <ModalityFilterSidebar
98
+ inputModalities={selectedInputModalities}
99
+ outputModalities={selectedOutputModalities}
100
+ onInputChange={setSelectedInputModalities}
101
+ onOutputChange={setSelectedOutputModalities}
102
+ />
103
+ <div className="flex-1 overflow-y-auto">
104
+ <ModelSelectionGrid
105
+ models={filteredModels}
106
+ selectedModelId={selectedModelId}
107
+ onSelectModel={setSelectedModelId}
108
+ configuredModelIds={configuredModelIds}
109
+ />
110
+ </div>
111
+ </div>
112
+ </form>
113
+
114
+ <DialogFooter className="p-6 gap-3 border-t border-zinc-200 dark:border-neutral-700 flex-shrink-0">
115
+ <Button
116
+ type="button"
117
+ variant="outline"
118
+ onClick={handleCancel}
119
+ className="h-9 px-3 py-2 rounded-sm text-sm font-medium dark:bg-neutral-600 dark:text-zinc-300 dark:border-neutral-600 dark:hover:bg-neutral-700"
120
+ >
121
+ Cancel
122
+ </Button>
123
+ <Button
124
+ type="submit"
125
+ form="model-selection-form"
126
+ disabled={!selectedModelId}
127
+ className="h-9 px-3 py-2 rounded-sm text-sm font-medium bg-zinc-950 text-white hover:bg-zinc-800 disabled:opacity-40 dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
128
+ >
129
+ Add Integration
130
+ </Button>
131
+ </DialogFooter>
132
+ </DialogContent>
133
+ </Dialog>
134
+ );
135
+ }
@@ -1,51 +1,51 @@
1
- import { AIModelCard } from './AIConfigCard';
2
- import { ModelOption } from '../helpers';
3
-
4
- interface ModelSelectionGridProps {
5
- models: ModelOption[];
6
- selectedModelId: string;
7
- onSelectModel: (modelId: string) => void;
8
- configuredModelIds: string[];
9
- }
10
-
11
- export function ModelSelectionGrid({
12
- models,
13
- selectedModelId,
14
- onSelectModel,
15
- configuredModelIds,
16
- }: ModelSelectionGridProps) {
17
- if (!models.length) {
18
- return (
19
- <div className="flex-1 flex items-center justify-center min-h-[300px] mr-6">
20
- <div className="text-center">
21
- <p className="text-neutral-500 dark:text-neutral-400 text-base">
22
- No models match the selected filters.
23
- </p>
24
- <p className="text-neutral-400 dark:text-neutral-500 text-sm mt-2">
25
- Try adjusting your input/output modality filters.
26
- </p>
27
- </div>
28
- </div>
29
- );
30
- }
31
-
32
- return (
33
- <div className="flex-1 grid grid-cols-3 gap-5 auto-rows-fr mr-6">
34
- {models.map((model) => {
35
- const isConfigured = configuredModelIds.includes(model.modelId);
36
- const isSelected = selectedModelId === model.modelId;
37
-
38
- return (
39
- <AIModelCard
40
- key={model.id}
41
- config={model}
42
- mode="selectable"
43
- isSelected={isSelected}
44
- isDisabled={isConfigured}
45
- onSelect={() => !isConfigured && onSelectModel(model.modelId)}
46
- />
47
- );
48
- })}
49
- </div>
50
- );
51
- }
1
+ import { AIModelCard } from './AIConfigCard';
2
+ import { ModelOption } from '../helpers';
3
+
4
+ interface ModelSelectionGridProps {
5
+ models: ModelOption[];
6
+ selectedModelId: string;
7
+ onSelectModel: (modelId: string) => void;
8
+ configuredModelIds: string[];
9
+ }
10
+
11
+ export function ModelSelectionGrid({
12
+ models,
13
+ selectedModelId,
14
+ onSelectModel,
15
+ configuredModelIds,
16
+ }: ModelSelectionGridProps) {
17
+ if (!models.length) {
18
+ return (
19
+ <div className="flex-1 flex items-center justify-center min-h-[300px] mr-6">
20
+ <div className="text-center">
21
+ <p className="text-neutral-500 dark:text-neutral-400 text-base">
22
+ No models match the selected filters.
23
+ </p>
24
+ <p className="text-neutral-400 dark:text-neutral-500 text-sm mt-2">
25
+ Try adjusting your input/output modality filters.
26
+ </p>
27
+ </div>
28
+ </div>
29
+ );
30
+ }
31
+
32
+ return (
33
+ <div className="flex-1 grid grid-cols-3 gap-5 auto-rows-fr mr-6">
34
+ {models.map((model) => {
35
+ const isConfigured = configuredModelIds.includes(model.modelId);
36
+ const isSelected = selectedModelId === model.modelId;
37
+
38
+ return (
39
+ <AIModelCard
40
+ key={model.id}
41
+ config={model}
42
+ mode="selectable"
43
+ isSelected={isSelected}
44
+ isDisabled={isConfigured}
45
+ onSelect={() => !isConfigured && onSelectModel(model.modelId)}
46
+ />
47
+ );
48
+ })}
49
+ </div>
50
+ );
51
+ }
@@ -1,118 +1,118 @@
1
- import { useEffect } from 'react';
2
- import { useForm } from 'react-hook-form';
3
- import { zodResolver } from '@hookform/resolvers/zod';
4
- import {
5
- Button,
6
- Dialog,
7
- DialogContent,
8
- DialogHeader,
9
- DialogTitle,
10
- DialogFooter,
11
- Label,
12
- Textarea,
13
- } from '@/components';
14
- import {
15
- UpdateAIConfigurationRequest,
16
- updateAIConfigurationRequestSchema,
17
- } from '@insforge/shared-schemas';
18
-
19
- interface SystemPromptDialogProps {
20
- open: boolean;
21
- onOpenChange: (open: boolean) => void;
22
- initialSystemPrompt: string | null | undefined;
23
- onSuccess: (data: UpdateAIConfigurationRequest) => void;
24
- }
25
-
26
- export function SystemPromptDialog({
27
- open,
28
- onOpenChange,
29
- initialSystemPrompt,
30
- onSuccess,
31
- }: SystemPromptDialogProps) {
32
- const form = useForm<UpdateAIConfigurationRequest>({
33
- resolver: zodResolver(updateAIConfigurationRequestSchema),
34
- defaultValues: {
35
- systemPrompt: initialSystemPrompt ?? null,
36
- },
37
- });
38
-
39
- // Reset form when dialog opens with new data
40
- useEffect(() => {
41
- if (open) {
42
- form.reset({
43
- systemPrompt: initialSystemPrompt ?? null,
44
- });
45
- }
46
- }, [open, initialSystemPrompt, form]);
47
-
48
- const handleFormSubmit = (e: React.FormEvent) => {
49
- e.preventDefault();
50
- void form.handleSubmit((data: UpdateAIConfigurationRequest) => {
51
- onSuccess(data);
52
- onOpenChange(false);
53
- })();
54
- };
55
-
56
- const handleCancel = () => {
57
- onOpenChange(false);
58
- };
59
-
60
- return (
61
- <Dialog open={open} onOpenChange={onOpenChange}>
62
- <DialogContent
63
- aria-describedby={undefined}
64
- className="max-w-[600px] p-0 gap-0 border-zinc-200 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.1)]"
65
- >
66
- <DialogHeader className="px-6 py-3 flex flex-col gap-1 justify-start border-b border-zinc-200 dark:border-neutral-700">
67
- <DialogTitle className="text-lg font-semibold text-zinc-950 dark:text-white">
68
- Edit System Prompt
69
- </DialogTitle>
70
- </DialogHeader>
71
-
72
- <form id="system-prompt-form" onSubmit={handleFormSubmit} className="flex flex-col">
73
- <div className="flex flex-col gap-6 p-6">
74
- <div className="flex flex-row gap-10 items-start">
75
- <Label
76
- htmlFor="systemPrompt"
77
- className="text-sm font-normal text-zinc-950 dark:text-neutral-50 whitespace-nowrap"
78
- >
79
- System Prompt
80
- </Label>
81
- <div className="flex flex-col gap-1 w-full">
82
- <Textarea
83
- id="systemPrompt"
84
- {...form.register('systemPrompt')}
85
- placeholder="Enter system prompt..."
86
- className="w-full min-h-[160px] resize-none bg-transparent dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700 focus:outline-none"
87
- />
88
- {form.formState.errors.systemPrompt && (
89
- <p className="text-sm text-red-600 dark:text-red-500">
90
- {form.formState.errors.systemPrompt.message}
91
- </p>
92
- )}
93
- </div>
94
- </div>
95
- </div>
96
- </form>
97
-
98
- <DialogFooter className="p-6 gap-3 border-t border-zinc-200 dark:border-neutral-700">
99
- <Button
100
- type="button"
101
- variant="outline"
102
- onClick={handleCancel}
103
- className="h-9 px-3 py-2 rounded-sm text-sm font-medium dark:bg-neutral-600 dark:text-zinc-300 dark:border-neutral-600 dark:hover:bg-neutral-700"
104
- >
105
- Cancel
106
- </Button>
107
- <Button
108
- type="submit"
109
- form="system-prompt-form"
110
- className="h-9 px-3 py-2 rounded-sm text-sm font-medium bg-zinc-950 text-white hover:bg-zinc-800 disabled:opacity-40 dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
111
- >
112
- Save
113
- </Button>
114
- </DialogFooter>
115
- </DialogContent>
116
- </Dialog>
117
- );
118
- }
1
+ import { useEffect } from 'react';
2
+ import { useForm } from 'react-hook-form';
3
+ import { zodResolver } from '@hookform/resolvers/zod';
4
+ import {
5
+ Button,
6
+ Dialog,
7
+ DialogContent,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogFooter,
11
+ Label,
12
+ Textarea,
13
+ } from '@/components';
14
+ import {
15
+ UpdateAIConfigurationRequest,
16
+ updateAIConfigurationRequestSchema,
17
+ } from '@insforge/shared-schemas';
18
+
19
+ interface SystemPromptDialogProps {
20
+ open: boolean;
21
+ onOpenChange: (open: boolean) => void;
22
+ initialSystemPrompt: string | null | undefined;
23
+ onSuccess: (data: UpdateAIConfigurationRequest) => void;
24
+ }
25
+
26
+ export function SystemPromptDialog({
27
+ open,
28
+ onOpenChange,
29
+ initialSystemPrompt,
30
+ onSuccess,
31
+ }: SystemPromptDialogProps) {
32
+ const form = useForm<UpdateAIConfigurationRequest>({
33
+ resolver: zodResolver(updateAIConfigurationRequestSchema),
34
+ defaultValues: {
35
+ systemPrompt: initialSystemPrompt ?? null,
36
+ },
37
+ });
38
+
39
+ // Reset form when dialog opens with new data
40
+ useEffect(() => {
41
+ if (open) {
42
+ form.reset({
43
+ systemPrompt: initialSystemPrompt ?? null,
44
+ });
45
+ }
46
+ }, [open, initialSystemPrompt, form]);
47
+
48
+ const handleFormSubmit = (e: React.FormEvent) => {
49
+ e.preventDefault();
50
+ void form.handleSubmit((data: UpdateAIConfigurationRequest) => {
51
+ onSuccess(data);
52
+ onOpenChange(false);
53
+ })();
54
+ };
55
+
56
+ const handleCancel = () => {
57
+ onOpenChange(false);
58
+ };
59
+
60
+ return (
61
+ <Dialog open={open} onOpenChange={onOpenChange}>
62
+ <DialogContent
63
+ aria-describedby={undefined}
64
+ className="max-w-[600px] p-0 gap-0 border-zinc-200 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.1)]"
65
+ >
66
+ <DialogHeader className="px-6 py-3 flex flex-col gap-1 justify-start border-b border-zinc-200 dark:border-neutral-700">
67
+ <DialogTitle className="text-lg font-semibold text-zinc-950 dark:text-white">
68
+ Edit System Prompt
69
+ </DialogTitle>
70
+ </DialogHeader>
71
+
72
+ <form id="system-prompt-form" onSubmit={handleFormSubmit} className="flex flex-col">
73
+ <div className="flex flex-col gap-6 p-6">
74
+ <div className="flex flex-row gap-10 items-start">
75
+ <Label
76
+ htmlFor="systemPrompt"
77
+ className="text-sm font-normal text-zinc-950 dark:text-neutral-50 whitespace-nowrap"
78
+ >
79
+ System Prompt
80
+ </Label>
81
+ <div className="flex flex-col gap-1 w-full">
82
+ <Textarea
83
+ id="systemPrompt"
84
+ {...form.register('systemPrompt')}
85
+ placeholder="Enter system prompt..."
86
+ className="w-full min-h-[160px] resize-none bg-transparent dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700 focus:outline-none"
87
+ />
88
+ {form.formState.errors.systemPrompt && (
89
+ <p className="text-sm text-red-600 dark:text-red-500">
90
+ {form.formState.errors.systemPrompt.message}
91
+ </p>
92
+ )}
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </form>
97
+
98
+ <DialogFooter className="p-6 gap-3 border-t border-zinc-200 dark:border-neutral-700">
99
+ <Button
100
+ type="button"
101
+ variant="outline"
102
+ onClick={handleCancel}
103
+ className="h-9 px-3 py-2 rounded-sm text-sm font-medium dark:bg-neutral-600 dark:text-zinc-300 dark:border-neutral-600 dark:hover:bg-neutral-700"
104
+ >
105
+ Cancel
106
+ </Button>
107
+ <Button
108
+ type="submit"
109
+ form="system-prompt-form"
110
+ className="h-9 px-3 py-2 rounded-sm text-sm font-medium bg-zinc-950 text-white hover:bg-zinc-800 disabled:opacity-40 dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
111
+ >
112
+ Save
113
+ </Button>
114
+ </DialogFooter>
115
+ </DialogContent>
116
+ </Dialog>
117
+ );
118
+ }
@@ -1,6 +1,6 @@
1
- export { AIModelCard } from './AIConfigCard';
2
- export { default as AIEmptyState } from './AIEmptyState';
3
- export { ModalityFilterSidebar } from './ModalityFilterSidebar';
4
- export { ModelSelectionDialog } from './ModelSelectionDialog';
5
- export { ModelSelectionGrid } from './ModelSelectionGrid';
6
- export { SystemPromptDialog } from './SystemPromptDialog';
1
+ export { AIModelCard } from './AIConfigCard';
2
+ export { default as AIEmptyState } from './AIEmptyState';
3
+ export { ModalityFilterSidebar } from './ModalityFilterSidebar';
4
+ export { ModelSelectionDialog } from './ModelSelectionDialog';
5
+ export { ModelSelectionGrid } from './ModelSelectionGrid';
6
+ export { SystemPromptDialog } from './SystemPromptDialog';