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,626 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useQuery, useQueryClient } from '@tanstack/react-query';
3
+ import { Plus } from 'lucide-react';
4
+ import PencilIcon from '@/assets/icons/pencil.svg?react';
5
+ import RefreshIcon from '@/assets/icons/refresh.svg?react';
6
+ import { databaseService } from '@/features/database/services/database.service';
7
+ import { useMetadata } from '@/features/metadata/hooks/useMetadata';
8
+ import { Button } from '@/components/radix/Button';
9
+ import { Alert, AlertDescription } from '@/components/radix/Alert';
10
+ import { TableSidebar } from '@/features/database/components/TableSidebar';
11
+ import { RecordFormDialog } from '@/features/database/components/RecordFormDialog';
12
+ import { TableForm } from '@/features/database/components/TableForm';
13
+ import { ConfirmDialog } from '@/components/ConfirmDialog';
14
+ import { EmptyState } from '@/components/EmptyState';
15
+ import {
16
+ Tooltip,
17
+ TooltipContent,
18
+ TooltipProvider,
19
+ TooltipTrigger,
20
+ } from '@/components/radix/Tooltip';
21
+ import { useConfirm } from '@/lib/hooks/useConfirm';
22
+ import { useToast } from '@/lib/hooks/useToast';
23
+ import { DatabaseDataGrid } from '@/features/database/components/DatabaseDataGrid';
24
+ import { SearchInput, SelectionClearButton, DeleteActionButton } from '@/components';
25
+ import { SortColumn } from 'react-data-grid';
26
+ import { convertValueForColumn } from '@/lib/utils/utils';
27
+ import { LinkModalProvider, useLinkModal } from '@/features/database/hooks/UseLinkModal';
28
+ import { LinkRecordModal } from '@/features/database/components/LinkRecordModal';
29
+ import {
30
+ DataUpdatePayload,
31
+ DataUpdateResourceType,
32
+ ServerEvents,
33
+ SocketMessage,
34
+ useSocket,
35
+ } from '@/lib/contexts/SocketContext';
36
+
37
+ const PAGE_SIZE = 50;
38
+
39
+ function DatabasePageContent() {
40
+ // Load selected table from localStorage on mount
41
+ const [selectedTable, setSelectedTable] = useState<string | null>(() => {
42
+ return localStorage.getItem('selectedTable');
43
+ });
44
+ const [pendingTableSelection, setPendingTableSelection] = useState<string>();
45
+ const [showRecordForm, setShowRecordForm] = useState(false);
46
+ const [isTableFormDirty, setIsTableFormDirty] = useState(false);
47
+ const [showTableForm, setShowTableForm] = useState(false);
48
+ const [editingTable, setEditingTable] = useState<string | null>(null);
49
+ const [searchQuery, setSearchQuery] = useState('');
50
+ const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
51
+ const [sortColumns, setSortColumns] = useState<SortColumn[]>([]);
52
+ const [currentPage, setCurrentPage] = useState(1);
53
+ const [isSorting, setIsSorting] = useState(false);
54
+ const [isRefreshing, setIsRefreshing] = useState(false);
55
+
56
+ const { confirm, confirmDialogProps } = useConfirm();
57
+ const { showToast } = useToast();
58
+ const queryClient = useQueryClient();
59
+ const { modalState, closeModal } = useLinkModal();
60
+
61
+ const { socket, isConnected } = useSocket();
62
+
63
+ // Persist selected table to localStorage when it changes
64
+ useEffect(() => {
65
+ if (selectedTable) {
66
+ localStorage.setItem('selectedTable', selectedTable);
67
+ } else {
68
+ localStorage.removeItem('selectedTable');
69
+ }
70
+ }, [selectedTable]);
71
+
72
+ // Reset page when search query or selected table changes
73
+ useEffect(() => {
74
+ setCurrentPage(1);
75
+ }, [searchQuery, selectedTable]);
76
+
77
+ // Clear selected rows when table changes
78
+ useEffect(() => {
79
+ setSelectedRows(new Set());
80
+ }, [selectedTable]);
81
+
82
+ // Safe sort columns change handler
83
+ const handleSortColumnsChange = useCallback(
84
+ (newSortColumns: SortColumn[]) => {
85
+ try {
86
+ setIsSorting(true);
87
+ setSortColumns(newSortColumns);
88
+ // isSorting will be reset when the query completes
89
+ } catch {
90
+ // Clear sorting on error
91
+ setSortColumns([]);
92
+ setIsSorting(false);
93
+ showToast('Sorting failed. Please try a different sort option.', 'error');
94
+ }
95
+ },
96
+ [showToast]
97
+ );
98
+
99
+ // Fetch metadata
100
+ const { tables, isLoading, error: metadataError, refetch: refetchMetadata } = useMetadata();
101
+
102
+ // Fetch table data when selected
103
+ const {
104
+ data: tableData,
105
+ isLoading: isLoadingTable,
106
+ error: tableError,
107
+ refetch: refetchTableData,
108
+ } = useQuery({
109
+ queryKey: [
110
+ 'table',
111
+ selectedTable,
112
+ currentPage,
113
+ PAGE_SIZE,
114
+ searchQuery,
115
+ JSON.stringify(sortColumns),
116
+ ],
117
+ queryFn: async () => {
118
+ if (!selectedTable) {
119
+ return null;
120
+ }
121
+
122
+ const offset = (currentPage - 1) * PAGE_SIZE;
123
+
124
+ try {
125
+ const [schema, records] = await Promise.all([
126
+ databaseService.getTableSchema(selectedTable),
127
+ databaseService.getTableRecords(
128
+ selectedTable,
129
+ PAGE_SIZE,
130
+ offset,
131
+ searchQuery,
132
+ sortColumns
133
+ ),
134
+ ]);
135
+
136
+ return {
137
+ name: selectedTable,
138
+ schema,
139
+ records: records.records,
140
+ totalRecords: records.pagination.total ?? schema.recordCount,
141
+ };
142
+ } catch (error) {
143
+ // If sorting caused the error, retry without sorting
144
+ if (sortColumns && sortColumns.length > 0) {
145
+ setSortColumns([]);
146
+
147
+ const [schema, records] = await Promise.all([
148
+ databaseService.getTableSchema(selectedTable),
149
+ databaseService.getTableRecords(selectedTable, PAGE_SIZE, offset, searchQuery, []),
150
+ ]);
151
+
152
+ showToast('Sorting not supported for this table. Showing unsorted results.', 'info');
153
+
154
+ return {
155
+ name: selectedTable,
156
+ schema,
157
+ records: records.records,
158
+ totalRecords: records.pagination.total || schema.recordCount,
159
+ };
160
+ }
161
+ throw error;
162
+ }
163
+ },
164
+ enabled: !!selectedTable,
165
+ placeholderData: (previousData) => previousData, // Keep previous data while loading new sorted data
166
+ });
167
+
168
+ useEffect(() => {
169
+ if (!socket || !isConnected) {
170
+ return;
171
+ }
172
+
173
+ const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
174
+ if (
175
+ message.payload?.resource === DataUpdateResourceType.METADATA ||
176
+ message.payload?.resource === DataUpdateResourceType.DATABASE_SCHEMA
177
+ ) {
178
+ // Invalidate all tables queries
179
+ void queryClient.invalidateQueries({ queryKey: ['tables'] });
180
+ }
181
+ };
182
+
183
+ socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate);
184
+
185
+ return () => {
186
+ socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate);
187
+ };
188
+ }, [socket, isConnected, queryClient]);
189
+
190
+ // Reset sorting flag when loading completes
191
+ useEffect(() => {
192
+ if (!isLoadingTable && isSorting) {
193
+ setIsSorting(false);
194
+ }
195
+ }, [isLoadingTable, isSorting]);
196
+
197
+ // Auto-select first table (excluding system tables)
198
+ useEffect(() => {
199
+ if (!isLoading && tables) {
200
+ if (pendingTableSelection && tables.includes(pendingTableSelection)) {
201
+ setSelectedTable(pendingTableSelection);
202
+ setPendingTableSelection(undefined);
203
+ return;
204
+ }
205
+
206
+ if (selectedTable && !tables.includes(selectedTable)) {
207
+ setSelectedTable(null);
208
+ return;
209
+ }
210
+
211
+ if (!selectedTable && tables.length > 0 && !showTableForm && !pendingTableSelection) {
212
+ setSelectedTable(tables[0]);
213
+ }
214
+ }
215
+ }, [tables, pendingTableSelection, selectedTable, showTableForm, isLoading]);
216
+
217
+ const handleRefresh = async () => {
218
+ setIsRefreshing(true);
219
+ try {
220
+ // Reset all state
221
+ setSelectedRows(new Set());
222
+ setSortColumns([]);
223
+ setSearchQuery('');
224
+ setIsSorting(false);
225
+
226
+ // Refresh current table data (if table is selected)
227
+ if (selectedTable) {
228
+ await refetchTableData();
229
+ }
230
+ await refetchMetadata();
231
+ } finally {
232
+ setIsRefreshing(false);
233
+ }
234
+ };
235
+
236
+ const handleTableFormClose = async (): Promise<boolean> => {
237
+ if (isTableFormDirty) {
238
+ const confirmOptions = {
239
+ title: 'Unsaved Changes',
240
+ description: `You have unsaved changes. Do you want to discard the changes and exit the form?`,
241
+ confirmText: 'Discard',
242
+ destructive: true,
243
+ };
244
+
245
+ const shouldDiscard = await confirm(confirmOptions);
246
+ if (shouldDiscard) {
247
+ setShowTableForm(false);
248
+ setEditingTable(null);
249
+ return true;
250
+ } else {
251
+ return false;
252
+ }
253
+ } else {
254
+ setShowTableForm(false);
255
+ return true;
256
+ }
257
+ };
258
+
259
+ const handleSelectTable = (tableName: string) => {
260
+ if (showTableForm) {
261
+ void handleTableFormClose().then((discarded) => {
262
+ if (discarded) {
263
+ setSelectedTable(tableName);
264
+ }
265
+ });
266
+ } else {
267
+ setSelectedTable(tableName);
268
+ }
269
+ };
270
+
271
+ const handleCreateTable = () => {
272
+ setSelectedTable(null);
273
+ setEditingTable(null);
274
+ setShowTableForm(true);
275
+ };
276
+
277
+ const handleEditTable = (tableName: string) => {
278
+ setSelectedTable(tableName);
279
+ setEditingTable(tableName);
280
+ setShowTableForm(true);
281
+ };
282
+
283
+ const handleDeleteTable = async (tableName: string) => {
284
+ const confirmOptions = {
285
+ title: 'Delete Table',
286
+ description: `Are you sure you want to delete the table "${tableName}"? This will permanently delete all records in this table. This action cannot be undone.`,
287
+ confirmText: 'Delete',
288
+ destructive: true,
289
+ };
290
+
291
+ const shouldDelete = await confirm(confirmOptions);
292
+
293
+ if (shouldDelete) {
294
+ try {
295
+ // Update selectedTable BEFORE deleting to prevent queries on deleted table
296
+ if (selectedTable === tableName) {
297
+ setSelectedTable(null);
298
+ }
299
+
300
+ await databaseService.deleteTable(tableName);
301
+ showToast('Table deleted successfully', 'success');
302
+
303
+ // Invalidate all related queries for the deleted table
304
+ void queryClient.invalidateQueries({ queryKey: ['metadata'] });
305
+ void queryClient.invalidateQueries({ queryKey: ['tables'] });
306
+ void queryClient.invalidateQueries({ queryKey: ['table', tableName] });
307
+ void queryClient.invalidateQueries({ queryKey: ['table-schema', tableName] });
308
+ void queryClient.invalidateQueries({ queryKey: ['metadata'] });
309
+ } catch (error) {
310
+ const errorMessage = error instanceof Error ? error.message : 'Failed to delete table';
311
+ showToast(errorMessage, 'error');
312
+ }
313
+ }
314
+ };
315
+
316
+ // Handle record update
317
+ const handleRecordUpdate = async (rowId: string, columnKey: string, newValue: string) => {
318
+ if (!selectedTable) {
319
+ return;
320
+ }
321
+
322
+ try {
323
+ // Find column schema to determine the correct type conversion
324
+ const columnSchema = tableData?.schema?.columns?.find((col) => col.columnName === columnKey);
325
+ if (columnSchema) {
326
+ // Convert value based on column type using utility function
327
+ const conversionResult = convertValueForColumn(columnSchema.type, newValue);
328
+
329
+ if (!conversionResult.success) {
330
+ showToast(conversionResult.error || 'Invalid value', 'error');
331
+ return;
332
+ }
333
+ const updates = { [columnKey]: conversionResult.value };
334
+ await databaseService.updateRecord(selectedTable, rowId, updates);
335
+ await refetchTableData();
336
+ showToast('Record updated successfully', 'success');
337
+ }
338
+ } catch (error) {
339
+ showToast('Failed to update record', 'error');
340
+ throw error;
341
+ }
342
+ };
343
+
344
+ // Handle bulk delete
345
+ const handleBulkDelete = async (ids: string[]) => {
346
+ if (!selectedTable || ids.length === 0) {
347
+ return;
348
+ }
349
+
350
+ const shouldDelete = await confirm({
351
+ title: `Delete ${ids.length} ${ids.length === 1 ? 'Record' : 'Records'}`,
352
+ description: `Are you sure you want to delete ${ids.length} ${ids.length === 1 ? 'record' : 'records'}? This action cannot be undone.`,
353
+ confirmText: 'Delete',
354
+ destructive: true,
355
+ });
356
+
357
+ if (shouldDelete) {
358
+ try {
359
+ await Promise.all(ids.map((id) => databaseService.deleteRecord(selectedTable, id)));
360
+ await Promise.all([
361
+ refetchTableData(),
362
+ refetchMetadata(), // Also refresh metadata to update sidebar record counts
363
+ ]);
364
+ setSelectedRows(new Set());
365
+ showToast(`${ids.length} records deleted successfully`, 'success');
366
+ } catch {
367
+ showToast('Failed to delete some records', 'error');
368
+ }
369
+ }
370
+ };
371
+
372
+ const error = metadataError || tableError;
373
+
374
+ // Fetch schema for selected table
375
+ const { data: schemaData } = useQuery({
376
+ queryKey: ['table-schema', selectedTable],
377
+ queryFn: async () => {
378
+ if (!selectedTable) {
379
+ return undefined;
380
+ }
381
+ return await databaseService.getTableSchema(selectedTable);
382
+ },
383
+ enabled: !!selectedTable,
384
+ staleTime: 30 * 1000, // 30 seconds
385
+ });
386
+
387
+ // Fetch schema for editing table
388
+ const { data: editingTableSchema } = useQuery({
389
+ queryKey: ['table-schema', editingTable],
390
+ queryFn: async () => {
391
+ if (!editingTable) {
392
+ return undefined;
393
+ }
394
+ const editingTableSchema = await databaseService.getTableSchema(editingTable);
395
+ return editingTableSchema;
396
+ },
397
+ enabled: !!editingTable,
398
+ });
399
+
400
+ // Calculate pagination
401
+ const totalPages = Math.ceil((tableData?.totalRecords || 0) / PAGE_SIZE);
402
+
403
+ return (
404
+ <div className="flex h-full bg-bg-gray dark:bg-neutral-800">
405
+ {/* Secondary Sidebar - Table List */}
406
+ <TableSidebar
407
+ tables={tables}
408
+ selectedTable={selectedTable || undefined}
409
+ onTableSelect={handleSelectTable}
410
+ loading={isLoading}
411
+ onNewTable={handleCreateTable}
412
+ onEditTable={handleEditTable}
413
+ onDeleteTable={(tableName) => void handleDeleteTable(tableName)}
414
+ />
415
+
416
+ {/* Main Content Area */}
417
+ <div className="flex-1 min-w-0 flex flex-col overflow-hidden">
418
+ {showTableForm ? (
419
+ // Show TableForm replacing entire main content area
420
+ <TableForm
421
+ open={showTableForm}
422
+ onOpenChange={(open) => {
423
+ if (!open) {
424
+ void handleTableFormClose();
425
+ }
426
+ }}
427
+ mode={editingTable ? 'edit' : 'create'}
428
+ editTable={editingTable ? editingTableSchema : undefined}
429
+ setFormIsDirty={setIsTableFormDirty}
430
+ onSuccess={(newTableName?: string) => {
431
+ void refetchMetadata();
432
+ void refetchTableData();
433
+ setShowTableForm(false);
434
+ setPendingTableSelection(newTableName);
435
+ }}
436
+ />
437
+ ) : (
438
+ // Show normal content with header
439
+ <>
440
+ {/* Sticky Header Section */}
441
+ {selectedTable && (
442
+ <div className="sticky top-0 z-30 bg-bg-gray dark:bg-neutral-800">
443
+ <div className="pl-4 pr-1.5 py-1.5 h-12">
444
+ {/* Page Header with Breadcrumb */}
445
+ <div className="flex items-center justify-between">
446
+ <div className="flex items-center gap-3">
447
+ <nav className="flex items-center text-base font-semibold">
448
+ <span className="text-black dark:text-white">{selectedTable}</span>
449
+ </nav>
450
+
451
+ {/* Separator */}
452
+ <div className="h-6 w-px bg-gray-200 dark:bg-neutral-700" />
453
+
454
+ {/* Action buttons group */}
455
+ <div className="flex items-center gap-1">
456
+ <TooltipProvider>
457
+ <Tooltip>
458
+ <TooltipTrigger asChild>
459
+ <Button
460
+ variant="ghost"
461
+ size="icon"
462
+ className="p-1 h-9 w-9"
463
+ onClick={() => handleEditTable(selectedTable)}
464
+ >
465
+ <PencilIcon className="h-5 w-5 text-zinc-400 dark:text-neutral-400" />
466
+ </Button>
467
+ </TooltipTrigger>
468
+ <TooltipContent side="bottom" align="center">
469
+ <p>Edit Table</p>
470
+ </TooltipContent>
471
+ </Tooltip>
472
+
473
+ <Tooltip>
474
+ <TooltipTrigger asChild>
475
+ <Button
476
+ variant="ghost"
477
+ size="icon"
478
+ className="p-1 h-9 w-9"
479
+ onClick={() => void handleRefresh()}
480
+ disabled={isRefreshing}
481
+ >
482
+ <RefreshIcon className="h-5 w-5 text-zinc-400 dark:text-neutral-400" />
483
+ </Button>
484
+ </TooltipTrigger>
485
+ <TooltipContent side="bottom" align="center">
486
+ <p>{isRefreshing ? 'Refreshing...' : 'Refresh'}</p>
487
+ </TooltipContent>
488
+ </Tooltip>
489
+ </TooltipProvider>
490
+ </div>
491
+ </div>
492
+ </div>
493
+ </div>
494
+
495
+ <div className="pt-2 pb-4 px-3">
496
+ {/* Search Bar and Actions - only show when table is selected */}
497
+ {selectedTable && (
498
+ <div className="flex items-center justify-between">
499
+ {selectedRows.size > 0 ? (
500
+ <div className="flex items-center gap-3">
501
+ <SelectionClearButton
502
+ selectedCount={selectedRows.size}
503
+ itemType="record"
504
+ onClear={() => setSelectedRows(new Set())}
505
+ />
506
+ <DeleteActionButton
507
+ selectedCount={selectedRows.size}
508
+ itemType="record"
509
+ onDelete={() => void handleBulkDelete(Array.from(selectedRows))}
510
+ />
511
+ </div>
512
+ ) : (
513
+ <SearchInput
514
+ value={searchQuery}
515
+ onChange={setSearchQuery}
516
+ placeholder="Search Records by any String Field"
517
+ className="flex-1 max-w-80 dark:bg-neutral-800 dark:text-zinc-300 dark:border-neutral-700"
518
+ debounceTime={300}
519
+ />
520
+ )}
521
+ <div className="flex items-center gap-2 ml-4">
522
+ {selectedRows.size === 0 && selectedTable !== 'users' && (
523
+ <>
524
+ {/* Add Record Button */}
525
+ <Button
526
+ className="h-10 px-4 font-medium gap-1.5 dark:bg-emerald-300 dark:hover:bg-emerald-400"
527
+ onClick={() => setShowRecordForm(true)}
528
+ >
529
+ <Plus className="w-5 h-5" />
530
+ Add Record
531
+ </Button>
532
+ </>
533
+ )}
534
+ </div>
535
+ </div>
536
+ )}
537
+ </div>
538
+ </div>
539
+ )}
540
+
541
+ {/* Content - Full height without padding for table to fill */}
542
+ <div className="flex-1 flex flex-col overflow-hidden">
543
+ {error && (
544
+ <Alert variant="destructive" className="mb-4 mx-8 mt-4">
545
+ <AlertDescription>{String(error)}</AlertDescription>
546
+ </Alert>
547
+ )}
548
+
549
+ {!selectedTable ? (
550
+ <div className="flex-1 flex items-center justify-center">
551
+ <EmptyState
552
+ title="No Table Selected"
553
+ description="Select a table from the sidebar to view its data"
554
+ />
555
+ </div>
556
+ ) : (
557
+ <DatabaseDataGrid
558
+ data={tableData?.records || []}
559
+ schema={tableData?.schema}
560
+ loading={isLoadingTable && !tableData}
561
+ isSorting={isSorting}
562
+ isRefreshing={isRefreshing}
563
+ selectedRows={selectedRows}
564
+ onSelectedRowsChange={setSelectedRows}
565
+ sortColumns={sortColumns}
566
+ onSortColumnsChange={handleSortColumnsChange}
567
+ onCellEdit={handleRecordUpdate}
568
+ onJumpToTable={setSelectedTable}
569
+ searchQuery={searchQuery}
570
+ currentPage={currentPage}
571
+ totalPages={totalPages}
572
+ pageSize={PAGE_SIZE}
573
+ totalRecords={tableData?.totalRecords || 0}
574
+ onPageChange={setCurrentPage}
575
+ />
576
+ )}
577
+ </div>
578
+ </>
579
+ )}
580
+ </div>
581
+
582
+ {/* Add Record Form */}
583
+ {selectedTable && schemaData && (
584
+ // In the RecordForm onSuccess callback
585
+ <RecordFormDialog
586
+ open={showRecordForm}
587
+ onOpenChange={setShowRecordForm}
588
+ tableName={selectedTable}
589
+ schema={schemaData.columns}
590
+ onSuccess={() => {
591
+ void refetchTableData();
592
+ void refetchMetadata();
593
+ // Also invalidate the schema cache to ensure fresh data
594
+ void queryClient.invalidateQueries({ queryKey: ['table-schema', selectedTable] });
595
+ }}
596
+ />
597
+ )}
598
+
599
+ {/* Confirm Dialog */}
600
+ <ConfirmDialog {...confirmDialogProps} />
601
+
602
+ {/* Global Link Record Modal */}
603
+ {modalState.isOpen && modalState.referenceTable && modalState.referenceColumn && (
604
+ <LinkRecordModal
605
+ open={modalState.isOpen}
606
+ onOpenChange={closeModal}
607
+ referenceTable={modalState.referenceTable}
608
+ referenceColumn={modalState.referenceColumn}
609
+ currentValue={modalState.currentValue}
610
+ onSelectRecord={(record) => {
611
+ modalState.onSelectRecord?.(record);
612
+ closeModal();
613
+ }}
614
+ />
615
+ )}
616
+ </div>
617
+ );
618
+ }
619
+
620
+ export default function DatabasePage() {
621
+ return (
622
+ <LinkModalProvider>
623
+ <DatabasePageContent />
624
+ </LinkModalProvider>
625
+ );
626
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ import { columnSchema, foreignKeySchema } from '@insforge/shared-schemas';
3
+
4
+ // Foreign key schema
5
+ export const tableFormForeignKeySchema = foreignKeySchema.extend({
6
+ columnName: z.string(),
7
+ });
8
+
9
+ export const tableFormColumnSchema = columnSchema.extend({
10
+ // Internal tracking field (not sent to backend)
11
+ originalName: z.string().optional(),
12
+ isSystemColumn: z.boolean(),
13
+ isNewColumn: z.boolean(),
14
+ });
15
+
16
+ // Table form schema
17
+ export const tableFormSchema = z.object({
18
+ tableName: z.string(),
19
+ columns: z.array(tableFormColumnSchema),
20
+ });
21
+
22
+ // Type exports
23
+ export type TableFormForeignKeySchema = z.infer<typeof tableFormForeignKeySchema>;
24
+ export type TableFormColumnSchema = z.infer<typeof tableFormColumnSchema>;
25
+ export type TableFormSchema = z.infer<typeof tableFormSchema>;