insforge 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/.dockerignore +58 -0
  2. package/.env.example +49 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +79 -0
  6. package/.github/copilot-instructions.md +147 -0
  7. package/.github/workflows/build-image.yml +65 -0
  8. package/.github/workflows/ci-premerge-check.yml +24 -0
  9. package/.github/workflows/deploy-aws.yml +130 -0
  10. package/.github/workflows/lint-and-format.yml +33 -0
  11. package/.prettierignore +65 -0
  12. package/.prettierrc +9 -0
  13. package/CHANGELOG.md +3 -0
  14. package/CONTRIBUTING.md +126 -0
  15. package/Dockerfile +27 -0
  16. package/GITHUB_OAUTH_SETUP.md +49 -0
  17. package/GOOGLE_OAUTH_SETUP.md +148 -0
  18. package/LICENSE +201 -0
  19. package/README.md +134 -0
  20. package/assets/Dark.svg +23 -0
  21. package/assets/archDiagram.png +0 -0
  22. package/assets/banner.png +0 -0
  23. package/assets/mcpInstallv2.png +0 -0
  24. package/assets/sampleResponse.png +0 -0
  25. package/assets/signin.png +0 -0
  26. package/assets/userflow.png +0 -0
  27. package/backend/migrations/000_create-base-tables.sql +142 -0
  28. package/backend/migrations/001_create-helper-functions.sql +41 -0
  29. package/backend/migrations/002_rename-auth-tables.sql +30 -0
  30. package/backend/migrations/003_create-users-table.sql +56 -0
  31. package/backend/migrations/004_add-reload-postgrest-func.sql +24 -0
  32. package/backend/migrations/005_enable-project-admin-modify-users.sql +30 -0
  33. package/backend/migrations/006_modify-ai-usage-table.sql +25 -0
  34. package/backend/migrations/007_drop-metadata-table.sql +2 -0
  35. package/backend/migrations/008_add-system-tables.sql +77 -0
  36. package/backend/migrations/009_add-function-secrets.sql +24 -0
  37. package/backend/migrations/010_modify-ai-config-modalities.sql +93 -0
  38. package/backend/migrations/011_refactor-secrets-table.sql +15 -0
  39. package/backend/migrations/012_add-storage-uploaded-by.sql +8 -0
  40. package/backend/package.json +75 -0
  41. package/backend/src/api/middleware/auth.ts +240 -0
  42. package/backend/src/api/middleware/error.ts +231 -0
  43. package/backend/src/api/middleware/upload.ts +59 -0
  44. package/backend/src/api/routes/agent.ts +29 -0
  45. package/backend/src/api/routes/ai.ts +472 -0
  46. package/backend/src/api/routes/auth.oauth.ts +482 -0
  47. package/backend/src/api/routes/auth.ts +386 -0
  48. package/backend/src/api/routes/database.advance.ts +275 -0
  49. package/backend/src/api/routes/database.records.ts +246 -0
  50. package/backend/src/api/routes/database.tables.ts +161 -0
  51. package/backend/src/api/routes/docs.ts +66 -0
  52. package/backend/src/api/routes/functions.ts +183 -0
  53. package/backend/src/api/routes/logs.ts +150 -0
  54. package/backend/src/api/routes/metadata.ts +160 -0
  55. package/backend/src/api/routes/openapi.ts +82 -0
  56. package/backend/src/api/routes/secrets.ts +199 -0
  57. package/backend/src/api/routes/storage.ts +547 -0
  58. package/backend/src/api/routes/usage.ts +96 -0
  59. package/backend/src/core/ai/chat.ts +207 -0
  60. package/backend/src/core/ai/client.ts +242 -0
  61. package/backend/src/core/ai/config.ts +187 -0
  62. package/backend/src/core/ai/image.ts +156 -0
  63. package/backend/src/core/ai/model.ts +117 -0
  64. package/backend/src/core/ai/usage.ts +290 -0
  65. package/backend/src/core/auth/auth.ts +781 -0
  66. package/backend/src/core/auth/oauth.ts +398 -0
  67. package/backend/src/core/database/advance.ts +1074 -0
  68. package/backend/src/core/database/manager.ts +178 -0
  69. package/backend/src/core/database/table.ts +772 -0
  70. package/backend/src/core/documentation/agent.ts +689 -0
  71. package/backend/src/core/documentation/openapi.ts +856 -0
  72. package/backend/src/core/functions/functions.ts +310 -0
  73. package/backend/src/core/logs/analytics.ts +76 -0
  74. package/backend/src/core/logs/audit.ts +255 -0
  75. package/backend/src/core/logs/providers/base.provider.ts +83 -0
  76. package/backend/src/core/logs/providers/cloudwatch.provider.ts +510 -0
  77. package/backend/src/core/logs/providers/localdb.provider.ts +246 -0
  78. package/backend/src/core/secrets/encryption.ts +58 -0
  79. package/backend/src/core/secrets/secrets.ts +410 -0
  80. package/backend/src/core/socket/socket.ts +388 -0
  81. package/backend/src/core/socket/types.ts +79 -0
  82. package/backend/src/core/storage/storage.ts +923 -0
  83. package/backend/src/server.ts +288 -0
  84. package/backend/src/types/ai.ts +46 -0
  85. package/backend/src/types/auth.ts +90 -0
  86. package/backend/src/types/database.ts +136 -0
  87. package/backend/src/types/error-constants.ts +86 -0
  88. package/backend/src/types/logs.ts +47 -0
  89. package/backend/src/types/profile.ts +55 -0
  90. package/backend/src/types/storage.ts +23 -0
  91. package/backend/src/utils/cloud-token.ts +39 -0
  92. package/backend/src/utils/constants.ts +1 -0
  93. package/backend/src/utils/environment.ts +35 -0
  94. package/backend/src/utils/helpers.ts +49 -0
  95. package/backend/src/utils/logger.ts +13 -0
  96. package/backend/src/utils/response.ts +62 -0
  97. package/backend/src/utils/seed.ts +205 -0
  98. package/backend/src/utils/sql-parser.ts +63 -0
  99. package/backend/src/utils/uuid.ts +9 -0
  100. package/backend/src/utils/validations.ts +129 -0
  101. package/backend/tests/README.md +134 -0
  102. package/backend/tests/cleanup-all-test-data.sh +231 -0
  103. package/backend/tests/cloud/test-s3-multitenant.sh +132 -0
  104. package/backend/tests/local/comprehensive-curl-tests.sh +156 -0
  105. package/backend/tests/local/test-auth-router.sh +144 -0
  106. package/backend/tests/local/test-database-router.sh +222 -0
  107. package/backend/tests/local/test-e2e.sh +241 -0
  108. package/backend/tests/local/test-fk-errors.sh +97 -0
  109. package/backend/tests/local/test-id-field.sh +201 -0
  110. package/backend/tests/local/test-public-bucket.sh +265 -0
  111. package/backend/tests/local/test-secrets.sh +248 -0
  112. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -0
  113. package/backend/tests/local/test-traditional-rest.sh +209 -0
  114. package/backend/tests/manual/README.md +51 -0
  115. package/backend/tests/manual/create-large-table-simple.sql +11 -0
  116. package/backend/tests/manual/seed-large-table.sql +101 -0
  117. package/backend/tests/manual/setup-large-table-extras.sql +34 -0
  118. package/backend/tests/manual/test-better-auth.sh +303 -0
  119. package/backend/tests/manual/test-bulk-upsert.sh +410 -0
  120. package/backend/tests/manual/test-database-advance.sh +297 -0
  121. package/backend/tests/manual/test-postgrest-stability.sh +192 -0
  122. package/backend/tests/manual/test-rawsql-export-import.sh +412 -0
  123. package/backend/tests/manual/test-universal-storage.sh +264 -0
  124. package/backend/tests/manual/test-users.sql +18 -0
  125. package/backend/tests/run-all-tests.sh +140 -0
  126. package/backend/tests/setup.ts +22 -0
  127. package/backend/tests/test-config.sh +303 -0
  128. package/backend/tsconfig.json +23 -0
  129. package/backend/tsup.config.ts +18 -0
  130. package/backend/vitest.config.ts +22 -0
  131. package/docker-compose.prod.yml +145 -0
  132. package/docker-compose.yml +167 -0
  133. package/docker-init/db/db-init.sql +125 -0
  134. package/docker-init/db/jwt.sql +5 -0
  135. package/docker-init/db/logs.sql +9 -0
  136. package/docker-init/db/postgresql.conf +17 -0
  137. package/docs/deprecated/insforge-auth-api.md +215 -0
  138. package/docs/deprecated/insforge-auth-sdk.md +100 -0
  139. package/docs/deprecated/insforge-db-api.md +359 -0
  140. package/docs/deprecated/insforge-db-sdk.md +140 -0
  141. package/docs/deprecated/insforge-debug-sdk.md +157 -0
  142. package/docs/deprecated/insforge-debug.md +65 -0
  143. package/docs/deprecated/insforge-instructions.md +124 -0
  144. package/docs/deprecated/insforge-project.md +118 -0
  145. package/docs/deprecated/insforge-storage-api.md +279 -0
  146. package/docs/deprecated/insforge-storage-sdk.md +159 -0
  147. package/docs/insforge-instructions-sdk.md +407 -0
  148. package/eslint.config.js +317 -0
  149. package/examples/oauth/frontend-oauth-example.html +251 -0
  150. package/examples/response-examples.md +444 -0
  151. package/frontend/README.md +112 -0
  152. package/frontend/components.json +17 -0
  153. package/frontend/index.html +13 -0
  154. package/frontend/package.json +63 -0
  155. package/frontend/public/favicon.ico +0 -0
  156. package/frontend/src/App.tsx +106 -0
  157. package/frontend/src/assets/icons/checkbox_checked.svg +6 -0
  158. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -0
  159. package/frontend/src/assets/icons/checked.svg +3 -0
  160. package/frontend/src/assets/icons/error.svg +3 -0
  161. package/frontend/src/assets/icons/pencil.svg +4 -0
  162. package/frontend/src/assets/icons/refresh.svg +4 -0
  163. package/frontend/src/assets/icons/step_active.svg +3 -0
  164. package/frontend/src/assets/icons/step_inactive.svg +11 -0
  165. package/frontend/src/assets/icons/warning.svg +3 -0
  166. package/frontend/src/assets/logos/amazon.svg +1 -0
  167. package/frontend/src/assets/logos/claude_code.svg +3 -0
  168. package/frontend/src/assets/logos/cline.svg +6 -0
  169. package/frontend/src/assets/logos/cursor.svg +20 -0
  170. package/frontend/src/assets/logos/discord.svg +9 -0
  171. package/frontend/src/assets/logos/gemini.svg +19 -0
  172. package/frontend/src/assets/logos/github.svg +5 -0
  173. package/frontend/src/assets/logos/google.svg +13 -0
  174. package/frontend/src/assets/logos/grok.svg +10 -0
  175. package/frontend/src/assets/logos/insforge_dark.svg +15 -0
  176. package/frontend/src/assets/logos/insforge_light.svg +15 -0
  177. package/frontend/src/assets/logos/openai.svg +10 -0
  178. package/frontend/src/assets/logos/roo_code.svg +9 -0
  179. package/frontend/src/assets/logos/trae.svg +3 -0
  180. package/frontend/src/assets/logos/windsurf.svg +10 -0
  181. package/frontend/src/components/ButtonWithLoading.tsx +27 -0
  182. package/frontend/src/components/Checkbox.tsx +61 -0
  183. package/frontend/src/components/CodeBlock.tsx +32 -0
  184. package/frontend/src/components/ConfirmDialog.tsx +96 -0
  185. package/frontend/src/components/CopyButton.tsx +69 -0
  186. package/frontend/src/components/DeleteActionButton.tsx +42 -0
  187. package/frontend/src/components/EmptyState.tsx +41 -0
  188. package/frontend/src/components/ErrorState.tsx +35 -0
  189. package/frontend/src/components/FeatureSidebar.tsx +126 -0
  190. package/frontend/src/components/FeatureSidebarItem.tsx +101 -0
  191. package/frontend/src/components/JsonHighlight.tsx +61 -0
  192. package/frontend/src/components/LoadingState.tsx +16 -0
  193. package/frontend/src/components/PaginationControls.tsx +54 -0
  194. package/frontend/src/components/PromptDialog.tsx +68 -0
  195. package/frontend/src/components/SearchInput.tsx +90 -0
  196. package/frontend/src/components/SelectionClearButton.tsx +26 -0
  197. package/frontend/src/components/Stepper.tsx +139 -0
  198. package/frontend/src/components/ThemeToggle.tsx +58 -0
  199. package/frontend/src/components/TypeBadge.tsx +20 -0
  200. package/frontend/src/components/datagrid/DataGrid.tsx +264 -0
  201. package/frontend/src/components/datagrid/DefaultCellRenderer.tsx +114 -0
  202. package/frontend/src/components/datagrid/IdCell.tsx +44 -0
  203. package/frontend/src/components/datagrid/SortableHeader.tsx +74 -0
  204. package/frontend/src/components/datagrid/cell-editors/BooleanCellEditor.tsx +54 -0
  205. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +483 -0
  206. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +362 -0
  207. package/frontend/src/components/datagrid/cell-editors/TextCellEditor.tsx +38 -0
  208. package/frontend/src/components/datagrid/cell-editors/index.ts +14 -0
  209. package/frontend/src/components/datagrid/cell-editors/types.ts +43 -0
  210. package/frontend/src/components/datagrid/datagridTypes.tsx +72 -0
  211. package/frontend/src/components/datagrid/index.tsx +20 -0
  212. package/frontend/src/components/index.ts +39 -0
  213. package/frontend/src/components/layout/AppHeader.tsx +146 -0
  214. package/frontend/src/components/layout/AppSidebar.tsx +190 -0
  215. package/frontend/src/components/layout/CloudLayout.tsx +95 -0
  216. package/frontend/src/components/layout/Layout.tsx +43 -0
  217. package/frontend/src/components/radix/Alert.tsx +45 -0
  218. package/frontend/src/components/radix/AlertDialog.tsx +115 -0
  219. package/frontend/src/components/radix/Avatar.tsx +45 -0
  220. package/frontend/src/components/radix/Badge.tsx +33 -0
  221. package/frontend/src/components/radix/Button.tsx +50 -0
  222. package/frontend/src/components/radix/Card.tsx +58 -0
  223. package/frontend/src/components/radix/Dialog.tsx +98 -0
  224. package/frontend/src/components/radix/DropdownMenu.tsx +185 -0
  225. package/frontend/src/components/radix/Form.tsx +167 -0
  226. package/frontend/src/components/radix/Input.tsx +22 -0
  227. package/frontend/src/components/radix/Label.tsx +19 -0
  228. package/frontend/src/components/radix/Popover.tsx +29 -0
  229. package/frontend/src/components/radix/ScrollArea.tsx +44 -0
  230. package/frontend/src/components/radix/Select.tsx +151 -0
  231. package/frontend/src/components/radix/Separator.tsx +26 -0
  232. package/frontend/src/components/radix/Sheet.tsx +119 -0
  233. package/frontend/src/components/radix/Skeleton.tsx +7 -0
  234. package/frontend/src/components/radix/Switch.tsx +29 -0
  235. package/frontend/src/components/radix/Tabs.tsx +50 -0
  236. package/frontend/src/components/radix/Textarea.tsx +21 -0
  237. package/frontend/src/components/radix/Tooltip.tsx +28 -0
  238. package/frontend/src/features/ai/components/AIConfigCard.tsx +154 -0
  239. package/frontend/src/features/ai/components/AIConfigDialog.tsx +76 -0
  240. package/frontend/src/features/ai/components/AIConfigForm.tsx +222 -0
  241. package/frontend/src/features/ai/components/AIEmptyState.tsx +18 -0
  242. package/frontend/src/features/ai/components/fields/ModalityField.tsx +87 -0
  243. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +134 -0
  244. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +33 -0
  245. package/frontend/src/features/ai/helpers.ts +155 -0
  246. package/frontend/src/features/ai/hooks/useAIConfigs.ts +221 -0
  247. package/frontend/src/features/ai/hooks/useAIUsage.ts +77 -0
  248. package/frontend/src/features/ai/page/AIPage.tsx +178 -0
  249. package/frontend/src/features/ai/services/ai.service.ts +148 -0
  250. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +106 -0
  251. package/frontend/src/features/auth/components/AuthMethodTab.tsx +238 -0
  252. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +303 -0
  253. package/frontend/src/features/auth/components/OAuthEmptyState.tsx +15 -0
  254. package/frontend/src/features/auth/components/UserFormDialog.tsx +248 -0
  255. package/frontend/src/features/auth/components/UsersDataGrid.tsx +183 -0
  256. package/frontend/src/features/auth/components/UsersTab.tsx +114 -0
  257. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +129 -0
  258. package/frontend/src/features/auth/hooks/useUsers.ts +57 -0
  259. package/frontend/src/features/auth/index.ts +9 -0
  260. package/frontend/src/features/auth/page/AuthenticationPage.tsx +169 -0
  261. package/frontend/src/features/auth/services/auth.service.ts +112 -0
  262. package/frontend/src/features/auth/services/oauth.service.ts +49 -0
  263. package/frontend/src/features/dashboard/page/DashboardPage.tsx +194 -0
  264. package/frontend/src/features/database/components/ColumnTypeSelect.tsx +64 -0
  265. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +282 -0
  266. package/frontend/src/features/database/components/ForeignKeyCell.tsx +187 -0
  267. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +378 -0
  268. package/frontend/src/features/database/components/LinkRecordModal.tsx +288 -0
  269. package/frontend/src/features/database/components/RecordFormDialog.tsx +164 -0
  270. package/frontend/src/features/database/components/RecordFormField.tsx +568 -0
  271. package/frontend/src/features/database/components/TableEmptyState.tsx +21 -0
  272. package/frontend/src/features/database/components/TableForm.tsx +656 -0
  273. package/frontend/src/features/database/components/TableFormColumn.tsx +137 -0
  274. package/frontend/src/features/database/components/TableListSkeleton.tsx +9 -0
  275. package/frontend/src/features/database/components/TableSidebar.tsx +47 -0
  276. package/frontend/src/features/database/constants.ts +26 -0
  277. package/frontend/src/features/database/helpers.ts +125 -0
  278. package/frontend/src/features/database/hooks/UseLinkModal.tsx +78 -0
  279. package/frontend/src/features/database/index.ts +12 -0
  280. package/frontend/src/features/database/page/DatabasePage.tsx +626 -0
  281. package/frontend/src/features/database/schema.ts +25 -0
  282. package/frontend/src/features/database/services/database.service.ts +216 -0
  283. package/frontend/src/features/functions/components/FunctionEmptyState.tsx +15 -0
  284. package/frontend/src/features/functions/components/FunctionRow.tsx +71 -0
  285. package/frontend/src/features/functions/components/FunctionViewer.tsx +46 -0
  286. package/frontend/src/features/functions/components/FunctionsContent.tsx +88 -0
  287. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -0
  288. package/frontend/src/features/functions/components/SecretEmptyState.tsx +23 -0
  289. package/frontend/src/features/functions/components/SecretRow.tsx +68 -0
  290. package/frontend/src/features/functions/components/SecretsContent.tsx +120 -0
  291. package/frontend/src/features/functions/hooks/useFunctions.ts +106 -0
  292. package/frontend/src/features/functions/page/FunctionsPage.tsx +28 -0
  293. package/frontend/src/features/functions/services/functions.service.ts +48 -0
  294. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +87 -0
  295. package/frontend/src/features/login/components/PrivateRoute.tsx +24 -0
  296. package/frontend/src/features/login/page/CloudLoginPage.tsx +93 -0
  297. package/frontend/src/features/login/page/LoginPage.tsx +174 -0
  298. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +313 -0
  299. package/frontend/src/features/logs/components/LogsTable.tsx +199 -0
  300. package/frontend/src/features/logs/hooks/useAuditLogs.ts +39 -0
  301. package/frontend/src/features/logs/index.ts +5 -0
  302. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +530 -0
  303. package/frontend/src/features/logs/page/AuditsPage.tsx +192 -0
  304. package/frontend/src/features/logs/services/log.service.ts +171 -0
  305. package/frontend/src/features/metadata/hooks/useMetadata.ts +53 -0
  306. package/frontend/src/features/metadata/index.ts +0 -0
  307. package/frontend/src/features/metadata/page/MetadataPage.tsx +136 -0
  308. package/frontend/src/features/metadata/services/metadata.service.ts +17 -0
  309. package/frontend/src/features/onboard/components/CompletionCard.tsx +41 -0
  310. package/frontend/src/features/onboard/components/OnboardButton.tsx +84 -0
  311. package/frontend/src/features/onboard/components/StepContent.tsx +91 -0
  312. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +53 -0
  313. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +35 -0
  314. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +144 -0
  315. package/frontend/src/features/onboard/components/mcp/index.ts +4 -0
  316. package/frontend/src/features/onboard/components/mcp/mcp-helper.tsx +98 -0
  317. package/frontend/src/features/onboard/index.ts +3 -0
  318. package/frontend/src/features/onboard/page/OnBoardPage.tsx +104 -0
  319. package/frontend/src/features/onboard/types.ts +8 -0
  320. package/frontend/src/features/secrets/hooks/useSecrets.ts +139 -0
  321. package/frontend/src/features/secrets/services/secrets.service.ts +57 -0
  322. package/frontend/src/features/storage/components/BucketEmptyState.tsx +19 -0
  323. package/frontend/src/features/storage/components/BucketFormDialog.tsx +194 -0
  324. package/frontend/src/features/storage/components/BucketListSkeleton.tsx +17 -0
  325. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +287 -0
  326. package/frontend/src/features/storage/components/StorageDataGrid.tsx +239 -0
  327. package/frontend/src/features/storage/components/StorageManager.tsx +236 -0
  328. package/frontend/src/features/storage/components/StorageSidebar.tsx +44 -0
  329. package/frontend/src/features/storage/components/UploadToast.tsx +46 -0
  330. package/frontend/src/features/storage/index.ts +3 -0
  331. package/frontend/src/features/storage/page/StoragePage.tsx +553 -0
  332. package/frontend/src/features/storage/services/storage.service.ts +144 -0
  333. package/frontend/src/features/visualizer/components/AuthNode.tsx +107 -0
  334. package/frontend/src/features/visualizer/components/BucketNode.tsx +34 -0
  335. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +359 -0
  336. package/frontend/src/features/visualizer/components/TableNode.tsx +152 -0
  337. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +24 -0
  338. package/frontend/src/features/visualizer/components/index.ts +5 -0
  339. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +127 -0
  340. package/frontend/src/index.css +248 -0
  341. package/frontend/src/lib/api/client.ts +163 -0
  342. package/frontend/src/lib/contexts/AuthContext.tsx +157 -0
  343. package/frontend/src/lib/contexts/OnboardStepContext.tsx +68 -0
  344. package/frontend/src/lib/contexts/SocketContext.tsx +303 -0
  345. package/frontend/src/lib/contexts/ThemeContext.tsx +125 -0
  346. package/frontend/src/lib/hooks/useAuth.ts +4 -0
  347. package/frontend/src/lib/hooks/useConfirm.ts +55 -0
  348. package/frontend/src/lib/hooks/useInterval.ts +27 -0
  349. package/frontend/src/lib/hooks/useMediaQuery.ts +59 -0
  350. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +29 -0
  351. package/frontend/src/lib/hooks/usePagination.ts +27 -0
  352. package/frontend/src/lib/hooks/useTimeout.ts +27 -0
  353. package/frontend/src/lib/hooks/useToast.tsx +229 -0
  354. package/frontend/src/lib/utils/constants.ts +38 -0
  355. package/frontend/src/lib/utils/utils.ts +165 -0
  356. package/frontend/src/lib/utils/validation-schemas.ts +126 -0
  357. package/frontend/src/main.tsx +16 -0
  358. package/frontend/src/rdg.css +194 -0
  359. package/frontend/src/vite-env.d.ts +12 -0
  360. package/frontend/tailwind.config.js +97 -0
  361. package/frontend/tsconfig.json +26 -0
  362. package/frontend/tsconfig.node.json +10 -0
  363. package/frontend/vite.config.ts +37 -0
  364. package/frontend/vitest.config.ts +36 -0
  365. package/functions/deno.json +25 -0
  366. package/functions/server.ts +290 -0
  367. package/functions/worker-template.js +126 -0
  368. package/openapi/ai.yaml +689 -0
  369. package/openapi/auth.yaml +563 -0
  370. package/openapi/functions.yaml +476 -0
  371. package/openapi/health.yaml +30 -0
  372. package/openapi/logs.yaml +224 -0
  373. package/openapi/metadata.yaml +178 -0
  374. package/openapi/records.yaml +382 -0
  375. package/openapi/secrets.yaml +371 -0
  376. package/openapi/storage.yaml +876 -0
  377. package/openapi/tables.yaml +464 -0
  378. package/package.json +88 -0
  379. package/shared-schemas/package.json +31 -0
  380. package/shared-schemas/src/ai-api.schema.ts +167 -0
  381. package/shared-schemas/src/ai.schema.ts +54 -0
  382. package/shared-schemas/src/auth-api.schema.ts +193 -0
  383. package/shared-schemas/src/auth.schema.ts +94 -0
  384. package/shared-schemas/src/database-api.schema.ts +259 -0
  385. package/shared-schemas/src/database.schema.ts +69 -0
  386. package/shared-schemas/src/functions-api.schema.ts +25 -0
  387. package/shared-schemas/src/functions.schema.ts +16 -0
  388. package/shared-schemas/src/index.ts +13 -0
  389. package/shared-schemas/src/logs-api.schema.ts +49 -0
  390. package/shared-schemas/src/logs.schema.ts +14 -0
  391. package/shared-schemas/src/metadata.schema.ts +56 -0
  392. package/shared-schemas/src/storage-api.schema.ts +65 -0
  393. package/shared-schemas/src/storage.schema.ts +19 -0
  394. package/shared-schemas/tsconfig.json +21 -0
  395. package/tsconfig.json +8 -0
@@ -0,0 +1,656 @@
1
+ import { useState, useEffect, useMemo } from 'react';
2
+ import { useForm, useFieldArray } from 'react-hook-form';
3
+ import { zodResolver } from '@hookform/resolvers/zod';
4
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
5
+ import { AlertCircle, Plus, X, Link, MoveRight } from 'lucide-react';
6
+ import { Button } from '@/components/radix/Button';
7
+ import { Input } from '@/components/radix/Input';
8
+ import { Alert, AlertDescription } from '@/components/radix/Alert';
9
+ import { databaseService } from '@/features/database/services/database.service';
10
+ import {
11
+ TableFormColumnSchema,
12
+ TableFormForeignKeySchema,
13
+ tableFormSchema,
14
+ TableFormSchema,
15
+ } from '@/features/database/schema';
16
+ import { useToast } from '@/lib/hooks/useToast';
17
+ import { TableFormColumn } from './TableFormColumn';
18
+ import { ForeignKeyPopover } from './ForeignKeyPopover';
19
+ import { ColumnType, TableSchema, UpdateTableSchemaRequest } from '@insforge/shared-schemas';
20
+ import { SYSTEM_FIELDS } from '../helpers';
21
+
22
+ const newColumn: TableFormColumnSchema = {
23
+ columnName: '',
24
+ type: ColumnType.STRING,
25
+ isNullable: true,
26
+ isUnique: false,
27
+ defaultValue: '',
28
+ isSystemColumn: false,
29
+ isNewColumn: true,
30
+ };
31
+
32
+ interface TableFormProps {
33
+ open: boolean;
34
+ onOpenChange: (open: boolean) => void;
35
+ onSuccess?: (newTable?: string) => void;
36
+ mode?: 'create' | 'edit';
37
+ editTable?: TableSchema;
38
+ setFormIsDirty: (dirty: boolean) => void;
39
+ }
40
+
41
+ export function TableForm({
42
+ open,
43
+ onOpenChange,
44
+ onSuccess,
45
+ mode = 'create',
46
+ editTable,
47
+ setFormIsDirty,
48
+ }: TableFormProps) {
49
+ const [error, setError] = useState<string | null>(null);
50
+ const [showForeignKeyDialog, setShowForeignKeyDialog] = useState(false);
51
+ const [editingForeignKey, setEditingForeignKey] = useState<string>();
52
+ const [foreignKeys, setForeignKeys] = useState<TableFormForeignKeySchema[]>([]);
53
+ const queryClient = useQueryClient();
54
+ const { showToast } = useToast();
55
+
56
+ const form = useForm({
57
+ resolver: zodResolver(tableFormSchema),
58
+ defaultValues: {
59
+ tableName: '',
60
+ columns:
61
+ mode === 'create'
62
+ ? [
63
+ {
64
+ columnName: 'id',
65
+ type: ColumnType.UUID,
66
+ defaultValue: 'gen_random_uuid()',
67
+ isPrimaryKey: true,
68
+ isNullable: false,
69
+ isUnique: true,
70
+ isSystemColumn: true,
71
+ isNewColumn: false,
72
+ },
73
+ {
74
+ columnName: 'created_at',
75
+ type: ColumnType.DATETIME,
76
+ defaultValue: 'CURRENT_TIMESTAMP',
77
+ isNullable: true,
78
+ isUnique: false,
79
+ isSystemColumn: true,
80
+ isNewColumn: false,
81
+ },
82
+ {
83
+ columnName: 'updated_at',
84
+ type: ColumnType.DATETIME,
85
+ defaultValue: 'CURRENT_TIMESTAMP',
86
+ isNullable: true,
87
+ isUnique: false,
88
+ isSystemColumn: true,
89
+ isNewColumn: false,
90
+ },
91
+ {
92
+ ...newColumn,
93
+ },
94
+ ]
95
+ : [{ ...newColumn }],
96
+ },
97
+ });
98
+
99
+ // Reset form when switching between modes or when editTable changes
100
+ useEffect(() => {
101
+ // Clear error when effect runs
102
+ setError(null);
103
+
104
+ if (open && mode === 'edit' && editTable) {
105
+ form.reset({
106
+ tableName: editTable.tableName,
107
+ columns: editTable.columns.map((col) => ({
108
+ columnName: col.columnName,
109
+ type: col.type,
110
+ isPrimaryKey: col.isPrimaryKey,
111
+ isNullable: col.isNullable,
112
+ isUnique: col.isUnique || false,
113
+ defaultValue: col.defaultValue || '',
114
+ originalName: col.columnName, // Track original name for rename detection
115
+ isSystemColumn: SYSTEM_FIELDS.includes(col.columnName),
116
+ isNewColumn: false,
117
+ })),
118
+ });
119
+
120
+ // Set foreign keys from editTable
121
+ const existingForeignKeys = editTable.columns
122
+ .filter((col) => !SYSTEM_FIELDS.includes(col.columnName) && col.foreignKey)
123
+ .map((col) => ({
124
+ columnName: col.columnName,
125
+ referenceTable: col.foreignKey?.referenceTable ?? '',
126
+ referenceColumn: col.foreignKey?.referenceColumn ?? '',
127
+ onDelete: col.foreignKey?.onDelete || 'NO ACTION',
128
+ onUpdate: col.foreignKey?.onUpdate || 'NO ACTION',
129
+ }));
130
+ setForeignKeys(existingForeignKeys);
131
+ } else {
132
+ form.reset({
133
+ tableName: '',
134
+ columns: [
135
+ {
136
+ columnName: 'id',
137
+ type: ColumnType.UUID,
138
+ defaultValue: 'gen_random_uuid()',
139
+ isPrimaryKey: true,
140
+ isNullable: false,
141
+ isUnique: true,
142
+ isSystemColumn: true,
143
+ isNewColumn: false,
144
+ },
145
+ {
146
+ columnName: 'created_at',
147
+ type: ColumnType.DATETIME,
148
+ defaultValue: 'CURRENT_TIMESTAMP',
149
+ isNullable: true,
150
+ isUnique: false,
151
+ isSystemColumn: true,
152
+ isNewColumn: false,
153
+ },
154
+ {
155
+ columnName: 'updated_at',
156
+ type: ColumnType.DATETIME,
157
+ defaultValue: 'CURRENT_TIMESTAMP',
158
+ isNullable: true,
159
+ isUnique: false,
160
+ isSystemColumn: true,
161
+ isNewColumn: false,
162
+ },
163
+ { ...newColumn },
164
+ ],
165
+ });
166
+ setForeignKeys([]);
167
+ }
168
+ }, [mode, editTable, form, open]);
169
+
170
+ useEffect(() => {
171
+ setFormIsDirty(form.formState.isDirty);
172
+ }, [form.formState.isDirty, setFormIsDirty]);
173
+
174
+ const { fields, append, remove } = useFieldArray({
175
+ control: form.control,
176
+ name: 'columns',
177
+ });
178
+
179
+ const sortedFields = useMemo(() => {
180
+ return [...fields].sort((a, b) => {
181
+ // System fields come first
182
+ if (a.isSystemColumn && !b.isSystemColumn) {
183
+ return -1;
184
+ }
185
+ if (!a.isSystemColumn && b.isSystemColumn) {
186
+ return 1;
187
+ }
188
+
189
+ // Within system fields, maintain the order: id, createdAt, updated_at
190
+ if (a.isSystemColumn && b.isSystemColumn) {
191
+ return SYSTEM_FIELDS.indexOf(a.columnName) - SYSTEM_FIELDS.indexOf(b.columnName);
192
+ }
193
+
194
+ // Keep original order for non-system fields
195
+ return 0;
196
+ });
197
+ }, [fields]);
198
+
199
+ const createTableMutation = useMutation({
200
+ mutationFn: (data: TableFormSchema) => {
201
+ const columns = data.columns.map((col) => {
202
+ // Find foreign key for this field if it exists
203
+ const foreignKey = foreignKeys.find((fk) => fk.columnName === col.columnName);
204
+
205
+ return {
206
+ columnName: col.columnName,
207
+ type: col.type,
208
+ isNullable: col.isNullable,
209
+ isUnique: col.isUnique,
210
+ defaultValue: col.defaultValue,
211
+ // Embed foreign key information directly in the column
212
+ ...(foreignKey && {
213
+ foreignKey: {
214
+ referenceTable: foreignKey.referenceTable,
215
+ referenceColumn: foreignKey.referenceColumn,
216
+ onDelete: foreignKey.onDelete,
217
+ onUpdate: foreignKey.onUpdate,
218
+ },
219
+ }),
220
+ };
221
+ });
222
+
223
+ return databaseService.createTable(data.tableName, columns);
224
+ },
225
+ onSuccess: (data) => {
226
+ void queryClient.invalidateQueries({ queryKey: ['database-metadata'] });
227
+ void queryClient.invalidateQueries({ queryKey: ['tables'] });
228
+ void queryClient.invalidateQueries({ queryKey: ['metadata'] });
229
+
230
+ showToast('Table created successfully!', 'success');
231
+
232
+ form.reset();
233
+ setError(null);
234
+ setForeignKeys([]);
235
+ onSuccess?.(data.tableName);
236
+ },
237
+ onError: (err) => {
238
+ const errorMessage = err.message || 'Failed to create table';
239
+ setError(errorMessage);
240
+ showToast('Failed to create table', 'error');
241
+ },
242
+ });
243
+
244
+ const updateTableMutation = useMutation({
245
+ mutationFn: (data: TableFormSchema) => {
246
+ if (!editTable) {
247
+ return Promise.resolve();
248
+ }
249
+
250
+ // Compare fields to determine what operations to perform
251
+ const addColumns: UpdateTableSchemaRequest['addColumns'] = [];
252
+ const dropColumns: UpdateTableSchemaRequest['dropColumns'] = [];
253
+ const updateColumns: UpdateTableSchemaRequest['updateColumns'] = [];
254
+ const addForeignKeys: UpdateTableSchemaRequest['addForeignKeys'] = [];
255
+ const dropForeignKeys: UpdateTableSchemaRequest['dropForeignKeys'] = [];
256
+
257
+ // Filter out system columns from existing fields for comparison
258
+ const existingUserColumns = editTable.columns.filter(
259
+ (col) => !SYSTEM_FIELDS.includes(col.columnName)
260
+ );
261
+
262
+ // Track which original columns we've seen
263
+ const processedOriginalColumns = new Set<string>();
264
+
265
+ // Process each field
266
+ data.columns.forEach((col) => {
267
+ if (col.originalName) {
268
+ // This field existed before
269
+ processedOriginalColumns.add(col.originalName);
270
+ const newDefaultValue = col.defaultValue || undefined;
271
+ const originalDefaultValue = editTable.columns.find(
272
+ (_col) => _col.columnName === col.originalName
273
+ )?.defaultValue;
274
+
275
+ // Check if it was renamed
276
+ if (col.originalName !== col.columnName) {
277
+ updateColumns.push({
278
+ columnName: col.originalName,
279
+ defaultValue:
280
+ newDefaultValue !== originalDefaultValue ? (newDefaultValue ?? '') : undefined,
281
+ newColumnName: col.columnName,
282
+ });
283
+ } else if (newDefaultValue !== originalDefaultValue) {
284
+ updateColumns.push({
285
+ columnName: col.columnName,
286
+ defaultValue: newDefaultValue ?? '',
287
+ });
288
+ }
289
+ } else {
290
+ // This is a new field (added via Add Field button)
291
+ const { ...fieldData } = col;
292
+ addColumns.push({
293
+ ...fieldData,
294
+ defaultValue: fieldData.defaultValue || undefined,
295
+ });
296
+ }
297
+ });
298
+
299
+ // Find dropped columns
300
+ existingUserColumns.forEach((col) => {
301
+ if (!processedOriginalColumns.has(col.columnName)) {
302
+ dropColumns.push(col.columnName);
303
+ }
304
+ });
305
+
306
+ // Handle foreign keys
307
+ // Get existing foreign keys from editTable
308
+ const existingForeignKeys = existingUserColumns
309
+ .filter((col) => col.foreignKey)
310
+ .map((col) => ({
311
+ columnName: col.columnName,
312
+ ...col.foreignKey,
313
+ }));
314
+
315
+ // Compare with new foreign keys
316
+ foreignKeys.forEach((fk) => {
317
+ const existingFK = existingForeignKeys.find((efk) => efk.columnName === fk.columnName);
318
+
319
+ if (!existingFK) {
320
+ addForeignKeys.push({
321
+ columnName: fk.columnName,
322
+ foreignKey: {
323
+ referenceTable: fk.referenceTable,
324
+ referenceColumn: fk.referenceColumn,
325
+ onDelete: fk.onDelete,
326
+ onUpdate: fk.onUpdate,
327
+ },
328
+ });
329
+ }
330
+ });
331
+
332
+ // Check for dropped foreign keys
333
+ existingForeignKeys.forEach((efk) => {
334
+ const stillExists = foreignKeys.find((fk) => fk.columnName === efk.columnName);
335
+ if (!stillExists) {
336
+ // This foreign key was removed
337
+ dropForeignKeys.push(efk.columnName);
338
+ }
339
+ });
340
+
341
+ const operations: UpdateTableSchemaRequest = {
342
+ addColumns,
343
+ dropColumns,
344
+ updateColumns,
345
+ addForeignKeys,
346
+ dropForeignKeys,
347
+ };
348
+
349
+ if (data.tableName !== editTable.tableName) {
350
+ operations.renameTable = { newTableName: data.tableName };
351
+ }
352
+
353
+ return databaseService.updateTableSchema(editTable.tableName, operations);
354
+ },
355
+ onSuccess: (_, data) => {
356
+ void queryClient.invalidateQueries({ queryKey: ['database-metadata'] });
357
+ void queryClient.invalidateQueries({ queryKey: ['tables'] });
358
+ void queryClient.invalidateQueries({ queryKey: ['metadata'] });
359
+
360
+ // Invalidate all table data queries for this table (with all parameter combinations)
361
+ void queryClient.invalidateQueries({ queryKey: ['table', editTable?.tableName] });
362
+
363
+ // Invalidate the separate table schema query used by AddRecordSheet
364
+ void queryClient.invalidateQueries({ queryKey: ['table-schema', editTable?.tableName] });
365
+
366
+ showToast(`Table "${data.tableName}" updated successfully!`, 'success');
367
+
368
+ form.reset();
369
+ setError(null);
370
+ setForeignKeys([]);
371
+ onSuccess?.();
372
+ },
373
+ onError: (err) => {
374
+ // Invalidate queries to ensure we have fresh data after failed request
375
+ void queryClient.invalidateQueries({ queryKey: ['table', editTable?.tableName] });
376
+ void queryClient.invalidateQueries({ queryKey: ['table-schema', editTable?.tableName] });
377
+
378
+ const errorMessage = err.message || 'Failed to update table';
379
+ setError(errorMessage);
380
+ showToast('Failed to update table', 'error');
381
+ },
382
+ });
383
+
384
+ const handleSubmit = form.handleSubmit((data) => {
385
+ if (mode === 'edit') {
386
+ updateTableMutation.mutate(data);
387
+ } else {
388
+ createTableMutation.mutate(data);
389
+ }
390
+ });
391
+
392
+ const addField = () => {
393
+ append({ ...newColumn });
394
+ };
395
+
396
+ const handleAddForeignKey = (fk: TableFormForeignKeySchema) => {
397
+ if (editingForeignKey) {
398
+ // Update existing foreign key
399
+ setForeignKeys(
400
+ foreignKeys.map((existingFk) =>
401
+ existingFk.columnName === editingForeignKey ? { ...fk } : existingFk
402
+ )
403
+ );
404
+ setEditingForeignKey(undefined);
405
+ } else {
406
+ // Add new foreign key
407
+ setForeignKeys([
408
+ ...foreignKeys,
409
+ {
410
+ ...fk,
411
+ },
412
+ ]);
413
+ }
414
+ };
415
+
416
+ const handleRemoveForeignKey = (columnName?: string) => {
417
+ setForeignKeys(foreignKeys.filter((fk) => fk.columnName !== columnName));
418
+ };
419
+
420
+ if (!open) {
421
+ return null;
422
+ }
423
+
424
+ return (
425
+ <div className="flex flex-col h-full">
426
+ {/* Content area with slate background */}
427
+ <div className="flex-1 bg-slate-100 dark:bg-neutral-900 flex flex-col items-center overflow-auto">
428
+ <div className="flex flex-col gap-6 w-full max-w-[1080px] px-6 py-6">
429
+ {/* Title Bar */}
430
+ <div className="flex items-center justify-between">
431
+ <h1 className="text-xl font-semibold text-black dark:text-neutral-50">
432
+ {mode === 'edit' ? 'Edit Table' : 'Create New Table'}
433
+ </h1>
434
+ <button
435
+ type="button"
436
+ onClick={() => onOpenChange(false)}
437
+ className="flex items-center justify-center w-12 h-12 bg-white dark:bg-neutral-600 rounded-full border border-zinc-200 shadow-sm hover:bg-gray-50 transition-colors dark:border-transparent dark:hover:bg-neutral-700"
438
+ >
439
+ <X className="w-5 h-5 text-zinc-500 dark:text-zinc-300" />
440
+ </button>
441
+ </div>
442
+
443
+ <form onSubmit={() => void handleSubmit()} className="flex flex-col gap-6">
444
+ {/* Table Name */}
445
+ <div className="bg-white rounded-xl border border-zinc-200 p-6 dark:bg-neutral-800 dark:border-transparent">
446
+ <div className="flex flex-row gap-10 items-center">
447
+ <label className="whitespace-nowrap text-sm font-normal text-zinc-950 dark:text-neutral-50">
448
+ Table Name
449
+ </label>
450
+ <Input
451
+ {...form.register('tableName')}
452
+ placeholder="e.g., products, orders, customers"
453
+ className="h-10 rounded-md border-zinc-200 shadow-sm placeholder:text-zinc-500 dark:text-white dark:bg-neutral-900 dark:border-neutral-700 dark:placeholder:text-neutral-400"
454
+ />
455
+ {form.formState.errors.tableName && (
456
+ <p className="text-sm text-destructive dark:text-red-400">
457
+ {form.formState.errors.tableName.message}
458
+ </p>
459
+ )}
460
+ </div>
461
+ </div>
462
+
463
+ {/* Columns Section */}
464
+ <div className="bg-white rounded-xl border border-zinc-200 overflow-hidden dark:bg-neutral-800 dark:border-transparent pb-3">
465
+ {/* Columns Header */}
466
+ <div className="p-6 bg-white dark:bg-neutral-800">
467
+ <h2 className="text-base font-medium text-black dark:text-neutral-50">Columns</h2>
468
+ </div>
469
+
470
+ {/* Columns Table */}
471
+ <div className="px-3 overflow-x-auto">
472
+ {/* Table Headers */}
473
+ <div className="flex items-center gap-6 px-4 py-2 bg-slate-50 rounded-t text-sm font-medium text-zinc-950 dark:bg-neutral-700 dark:text-white">
474
+ <div className="flex-1 min-w-[175px]">Name</div>
475
+ <div className="flex-1 min-w-[175px]">Type</div>
476
+ <div className="flex-1 min-w-[175px]">Default Value</div>
477
+ <div className="w-18 2xl:w-25 text-center flex-shrink-0">Nullable</div>
478
+ <div className="w-18 2xl:w-25 text-center flex-shrink-0">Unique</div>
479
+ <div className="w-5 flex-shrink-0" />
480
+ </div>
481
+
482
+ {/* Columns */}
483
+ {sortedFields.map((field) => {
484
+ const originalIndex = fields.findIndex((f) => f.id === field.id);
485
+ return (
486
+ <TableFormColumn
487
+ key={field.id}
488
+ column={field}
489
+ index={originalIndex}
490
+ control={form.control}
491
+ onRemove={() => remove(originalIndex)}
492
+ isSystemColumn={field.isSystemColumn}
493
+ isNewColumn={field.isNewColumn}
494
+ />
495
+ );
496
+ })}
497
+ </div>
498
+
499
+ {/* Add Column Button */}
500
+ <div className="border-t border-zinc-200 dark:border-neutral-700 flex justify-center py-3">
501
+ <Button
502
+ type="button"
503
+ onClick={addField}
504
+ variant="outline"
505
+ className="w-50 h-9 px-2 gap-2 text-sm font-medium text-zinc-700 hover:text-zinc-950 dark:bg-neutral-600 dark:hover:bg-neutral-700 dark:text-white dark:hover:text-white dark:border-transparent"
506
+ >
507
+ <Plus className="w-5 h-5" />
508
+ Add Column
509
+ </Button>
510
+ </div>
511
+ </div>
512
+
513
+ {/* Foreign Keys Section */}
514
+ <div className="bg-white pb-3 rounded-xl border border-zinc-200 dark:bg-neutral-800 dark:border-transparent">
515
+ <div className="p-6">
516
+ <h2 className="text-base font-semibold text-black dark:text-white">Foreign Keys</h2>
517
+ <p className="text-sm text-zinc-500 dark:text-neutral-400">
518
+ Create a relationship between this table and another table
519
+ </p>
520
+ </div>
521
+
522
+ {/* Existing foreign keys */}
523
+ {foreignKeys.length > 0 && (
524
+ <div className="px-6 pb-6 space-y-3">
525
+ {foreignKeys.map((fk) => (
526
+ <div
527
+ key={fk.columnName}
528
+ className="group flex items-center gap-6 2xl:gap-8 pl-4 pr-2 py-2 rounded-lg border border-zinc-200 bg-white hover:bg-zinc-100 transition-colors duration-150 dark:bg-neutral-700 dark:border-transparent dark:hover:bg-neutral-600"
529
+ >
530
+ <div className="flex items-center gap-2 flex-1 min-w-[188px] overflow-hidden">
531
+ <Link className="flex-shrink-0 w-5 h-5 text-zinc-500 dark:text-neutral-400" />
532
+ <span className="font-medium text-sm text-zinc-950 dark:text-white truncate">
533
+ {fk.columnName}
534
+ </span>
535
+ <MoveRight className="flex-shrink-0 w-5 h-5 text-zinc-950 dark:text-neutral-400" />
536
+ <span className="font-medium text-sm text-zinc-950 dark:text-white flex-1 truncate">
537
+ {fk.referenceTable}.{fk.referenceColumn}
538
+ </span>
539
+ </div>
540
+ <div className="flex items-center gap-2 w-45">
541
+ <span className="font-medium text-sm text-zinc-950 dark:text-white whitespace-nowrap">
542
+ On Update:
543
+ </span>
544
+ <span className="text-sm font-medium text-zinc-500 dark:text-neutral-400">
545
+ {fk.onUpdate}
546
+ </span>
547
+ </div>
548
+ <div className="flex items-center gap-2 w-45">
549
+ <span className="font-medium text-sm text-zinc-950 dark:text-white whitespace-nowrap">
550
+ On Delete:
551
+ </span>
552
+ <span className="text-sm font-medium text-zinc-500 dark:text-neutral-400">
553
+ {fk.onDelete}
554
+ </span>
555
+ </div>
556
+ <div className="flex items-center gap-3">
557
+ {/* <Button
558
+ type="button"
559
+ variant="ghost"
560
+ size="sm"
561
+ className="h-10 px-3 gap-1.5 text-zinc-950 hover:bg-zinc-200 transition-colors rounded-md"
562
+ onClick={() => {
563
+ setEditingForeignKey(fk.columnName);
564
+ setShowForeignKeyDialog(true);
565
+ }}
566
+ >
567
+ <Pencil className="w-4 h-4" />
568
+ <span className="font-medium text-sm">Edit</span>
569
+ </Button> */}
570
+ <Button
571
+ type="button"
572
+ variant="ghost"
573
+ size="sm"
574
+ onClick={() => handleRemoveForeignKey(fk.columnName)}
575
+ className="h-10 px-3 gap-1.5 text-zinc-950 hover:bg-zinc-200 transition-colors rounded-md dark:bg-neutral-700 dark:text-white dark:group-hover:bg-neutral-600 dark:hover:bg-neutral-500"
576
+ >
577
+ <X className="w-4 h-4" />
578
+ <span className="font-medium text-sm">Remove</span>
579
+ </Button>
580
+ </div>
581
+ </div>
582
+ ))}
583
+ </div>
584
+ )}
585
+
586
+ {/* Add Foreign Key Button */}
587
+ <div className="flex justify-center py-3 border-t border-zinc-200 dark:border-neutral-700">
588
+ <Button
589
+ type="button"
590
+ variant="outline"
591
+ className="w-50 h-9 p-2 gap-2 text-sm font-medium text-zinc-950 bg-white border-zinc-200 shadow-sm hover:bg-zinc-50 dark:bg-neutral-600 dark:text-white dark:border-transparent dark:hover:bg-neutral-700"
592
+ onClick={() => setShowForeignKeyDialog(true)}
593
+ >
594
+ <Link className="w-5 h-5" />
595
+ Add Foreign Keys
596
+ </Button>
597
+ </div>
598
+
599
+ {/* Foreign Key Popover */}
600
+ <ForeignKeyPopover
601
+ form={form}
602
+ mode={mode}
603
+ editTableName={editTable?.tableName}
604
+ open={showForeignKeyDialog}
605
+ onOpenChange={(open) => {
606
+ setShowForeignKeyDialog(open);
607
+ if (!open) {
608
+ setEditingForeignKey(undefined);
609
+ }
610
+ }}
611
+ onAddForeignKey={handleAddForeignKey}
612
+ initialValue={
613
+ editingForeignKey
614
+ ? foreignKeys.find((fk) => fk.columnName === editingForeignKey)
615
+ : undefined
616
+ }
617
+ />
618
+ </div>
619
+
620
+ {error && (
621
+ <Alert variant="destructive" className="dark:bg-neutral-800 dark:text-zinc-300">
622
+ <AlertCircle className="h-4 w-4" />
623
+ <AlertDescription>{error}</AlertDescription>
624
+ </Alert>
625
+ )}
626
+ </form>
627
+ </div>
628
+ </div>
629
+
630
+ {/* Footer */}
631
+ <div className="bg-slate-50 border-t border-zinc-200 px-6 py-3 pb-6 dark:bg-neutral-800 dark:border-neutral-700">
632
+ <div className="flex justify-end gap-3 max-w-[1080px] mx-auto px-6">
633
+ <Button
634
+ type="button"
635
+ variant="outline"
636
+ onClick={() => onOpenChange(false)}
637
+ className="h-10 px-4 text-sm font-medium border-zinc-200 shadow-sm dark:bg-neutral-800 dark:text-zinc-300 dark:border-neutral-700 dark:hover:bg-neutral-700"
638
+ >
639
+ Cancel
640
+ </Button>
641
+ <Button
642
+ onClick={() => void handleSubmit()}
643
+ disabled={
644
+ !form.formState.isValid ||
645
+ createTableMutation.isPending ||
646
+ updateTableMutation.isPending
647
+ }
648
+ className="h-10 px-4 text-sm font-medium bg-zinc-950 text-neutral-50 shadow-sm disabled:opacity-40 dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
649
+ >
650
+ {mode === 'edit' ? 'Update Table' : 'Save Table'}
651
+ </Button>
652
+ </div>
653
+ </div>
654
+ </div>
655
+ );
656
+ }