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,378 @@
1
+ import { useState, useEffect, useMemo } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { Button } from '@/components/radix/Button';
4
+ import { Label } from '@/components/radix/Label';
5
+ import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/components/radix/Dialog';
6
+ import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/radix/Select';
7
+ import { useMetadata } from '@/features/metadata/hooks/useMetadata';
8
+ import { databaseService } from '@/features/database/services/database.service';
9
+ import { UseFormReturn } from 'react-hook-form';
10
+ import { TableFormSchema, TableFormForeignKeySchema } from '../schema';
11
+ import { ColumnSchema, OnDeleteActionSchema, OnUpdateActionSchema } from '@insforge/shared-schemas';
12
+ import { cn } from '@/lib/utils/utils';
13
+
14
+ interface ForeignKeyPopoverProps {
15
+ form: UseFormReturn<TableFormSchema>;
16
+ mode: 'create' | 'edit';
17
+ editTableName?: string;
18
+ open: boolean;
19
+ onOpenChange: (open: boolean) => void;
20
+ onAddForeignKey: (fk: TableFormForeignKeySchema) => void;
21
+ initialValue?: TableFormForeignKeySchema;
22
+ }
23
+
24
+ export function ForeignKeyPopover({
25
+ form,
26
+ mode,
27
+ editTableName,
28
+ open,
29
+ onOpenChange,
30
+ onAddForeignKey,
31
+ initialValue,
32
+ }: ForeignKeyPopoverProps) {
33
+ const [newForeignKey, setNewForeignKey] = useState<TableFormForeignKeySchema>({
34
+ columnName: '',
35
+ referenceTable: '',
36
+ referenceColumn: '',
37
+ onDelete: 'NO ACTION',
38
+ onUpdate: 'NO ACTION',
39
+ });
40
+
41
+ const columns = form.watch('columns');
42
+
43
+ // Set initial values when editing
44
+ useEffect(() => {
45
+ if (open && initialValue) {
46
+ setNewForeignKey({
47
+ columnName: initialValue.columnName,
48
+ referenceTable: initialValue.referenceTable,
49
+ referenceColumn: initialValue.referenceColumn,
50
+ onDelete: initialValue.onDelete,
51
+ onUpdate: initialValue.onUpdate,
52
+ });
53
+ } else if (!open) {
54
+ // Reset when closing
55
+ setNewForeignKey({
56
+ columnName: '',
57
+ referenceTable: '',
58
+ referenceColumn: '',
59
+ onDelete: 'NO ACTION',
60
+ onUpdate: 'NO ACTION',
61
+ });
62
+ }
63
+ }, [open, initialValue]);
64
+
65
+ // Get available tables
66
+ const { tables } = useMetadata({ enabled: open });
67
+
68
+ const availableTables = tables.filter(
69
+ (tableName) => mode === 'create' || tableName !== editTableName
70
+ );
71
+
72
+ // Get columns for selected reference table
73
+ const { data: referenceTableSchema } = useQuery({
74
+ queryKey: ['table-schema', newForeignKey.referenceTable],
75
+ queryFn: async () => {
76
+ if (!newForeignKey.referenceTable) {
77
+ return null;
78
+ }
79
+ return await databaseService.getTableSchema(newForeignKey.referenceTable);
80
+ },
81
+ enabled: !!newForeignKey.referenceTable && open,
82
+ });
83
+
84
+ // Get the type of the selected source column
85
+ const getSourceFieldType = useMemo(() => {
86
+ if (!newForeignKey.columnName) {
87
+ return null;
88
+ }
89
+ const sourceColumn = columns.find((col) => col.columnName === newForeignKey.columnName);
90
+ return sourceColumn?.type || null;
91
+ }, [newForeignKey.columnName, columns]);
92
+
93
+ // Calculate if the button should be enabled
94
+ const isAddButtonEnabled = Boolean(
95
+ newForeignKey.columnName && newForeignKey.referenceTable && newForeignKey.referenceColumn
96
+ );
97
+
98
+ const handleAddForeignKey = () => {
99
+ if (newForeignKey.columnName && newForeignKey.referenceTable && newForeignKey.referenceColumn) {
100
+ onAddForeignKey(newForeignKey);
101
+ setNewForeignKey({
102
+ columnName: '',
103
+ referenceTable: '',
104
+ referenceColumn: '',
105
+ onDelete: 'NO ACTION',
106
+ onUpdate: 'NO ACTION',
107
+ });
108
+ onOpenChange(false);
109
+ }
110
+ };
111
+
112
+ const handleCancelAddForeignKey = () => {
113
+ setNewForeignKey({
114
+ columnName: '',
115
+ referenceTable: '',
116
+ referenceColumn: '',
117
+ onDelete: 'NO ACTION',
118
+ onUpdate: 'NO ACTION',
119
+ });
120
+ onOpenChange(false);
121
+ };
122
+
123
+ return (
124
+ <Dialog open={open} onOpenChange={onOpenChange}>
125
+ <DialogContent className="max-w-[520px] p-0 gap-0">
126
+ <div className="flex flex-col">
127
+ {/* Header */}
128
+ <div className="flex flex-col gap-1 px-6 py-3 border-b border-zinc-200 dark:border-neutral-700">
129
+ <DialogTitle className="text-lg font-semibold dark:text-white">
130
+ {initialValue ? 'Edit Foreign Key' : 'Add Foreign Key'}
131
+ </DialogTitle>
132
+ <DialogDescription className="text-sm text-zinc-500 dark:text-neutral-400">
133
+ {initialValue
134
+ ? 'Modify the relationship between tables'
135
+ : 'Create a relationship between this table and another table'}
136
+ </DialogDescription>
137
+ </div>
138
+
139
+ {/* Form Content */}
140
+ <div className="flex flex-col gap-6 p-6">
141
+ {/* Column selector */}
142
+ <div className="flex flex-row gap-10 items-center">
143
+ <Label className="text-sm text-black dark:text-white flex-1">Column</Label>
144
+ <Select
145
+ value={newForeignKey.columnName}
146
+ onValueChange={(value) =>
147
+ setNewForeignKey((prev) => ({ ...prev, columnName: value }))
148
+ }
149
+ >
150
+ <SelectTrigger className="w-70 h-10 border-zinc-200 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
151
+ <span
152
+ className={cn(
153
+ 'text-sm text-muted-foreground dark:text-neutral-400',
154
+ newForeignKey.columnName && 'text-black dark:text-white'
155
+ )}
156
+ >
157
+ {newForeignKey.columnName || 'Select column'}
158
+ </span>
159
+ </SelectTrigger>
160
+ <SelectContent>
161
+ {columns
162
+ .filter((col) => col.columnName)
163
+ .map((col, index) => (
164
+ <SelectItem
165
+ key={col.columnName || index}
166
+ value={col.columnName}
167
+ disabled={col.isSystemColumn}
168
+ >
169
+ <div className="flex flex-row items-center justify-between gap-2">
170
+ <span className="truncate max-w-[160px] block">{col.columnName}</span>
171
+ <span className="text-xs text-muted-foreground dark:text-neutral-400 flex-shrink-0">
172
+ ({col.type})
173
+ </span>
174
+ </div>
175
+ </SelectItem>
176
+ ))}
177
+ </SelectContent>
178
+ </Select>
179
+ </div>
180
+
181
+ {/* Reference Table selector */}
182
+ <div className="flex flex-row gap-10 items-center">
183
+ <Label className="text-sm text-black dark:text-white flex-1">Reference Table</Label>
184
+ <Select
185
+ value={newForeignKey.referenceTable}
186
+ onValueChange={(value) => {
187
+ setNewForeignKey((prev) => ({
188
+ ...prev,
189
+ referenceTable: value,
190
+ referenceColumn: '', // Reset column when table changes
191
+ }));
192
+ }}
193
+ >
194
+ <SelectTrigger className="w-70 h-10 border-zinc-200 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
195
+ <span
196
+ className={cn(
197
+ 'text-sm text-muted-foreground dark:text-neutral-400',
198
+ newForeignKey.referenceTable && 'text-black dark:text-white'
199
+ )}
200
+ >
201
+ {newForeignKey.referenceTable || 'Select table'}
202
+ </span>
203
+ </SelectTrigger>
204
+ <SelectContent>
205
+ {availableTables.map((tableName) => (
206
+ <SelectItem key={tableName} value={tableName}>
207
+ {tableName}
208
+ </SelectItem>
209
+ ))}
210
+ </SelectContent>
211
+ </Select>
212
+ </div>
213
+
214
+ {/* Reference Column selector - only shown after table and source column are selected */}
215
+ {newForeignKey.referenceTable && newForeignKey.columnName && (
216
+ <div className="flex flex-row gap-10 items-center">
217
+ <Label className="text-sm text-black dark:text-white flex-1">
218
+ Reference Column
219
+ </Label>
220
+ <Select
221
+ key={`column-select-${newForeignKey.referenceTable}`}
222
+ value={newForeignKey.referenceColumn}
223
+ onValueChange={(value) =>
224
+ setNewForeignKey((prev) => ({ ...prev, referenceColumn: value }))
225
+ }
226
+ >
227
+ <SelectTrigger className="w-70 h-10 border-zinc-200 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
228
+ <span
229
+ className={cn(
230
+ 'text-sm text-muted-foreground dark:text-neutral-400',
231
+ newForeignKey.referenceColumn && 'text-black dark:text-white'
232
+ )}
233
+ >
234
+ {newForeignKey.referenceColumn || 'Select column'}
235
+ </span>
236
+ </SelectTrigger>
237
+ <SelectContent className="max-w-[360px]">
238
+ {(() => {
239
+ const allColumns = referenceTableSchema?.columns || [];
240
+ if (allColumns.length > 0) {
241
+ const sourceType = getSourceFieldType;
242
+
243
+ return allColumns.map((col: ColumnSchema) => {
244
+ // Check if types match exactly (sourceType should always exist at this point since we require columnName)
245
+ const typesMatch =
246
+ sourceType && col.type.toLowerCase() === sourceType.toLowerCase();
247
+
248
+ // Disable if not a valid reference or types don't match
249
+ const isDisabled = !col.isUnique || !typesMatch;
250
+
251
+ // Determine what to show on the right side
252
+ let rightText = '';
253
+ if (!col.isUnique) {
254
+ rightText = 'Not unique';
255
+ } else if (!typesMatch) {
256
+ rightText = 'Columntype not match';
257
+ }
258
+
259
+ return (
260
+ <SelectItem
261
+ key={col.columnName}
262
+ value={col.columnName}
263
+ disabled={isDisabled}
264
+ className="relative flex items-center justify-between pr-16"
265
+ >
266
+ <div className="flex flex-row items-center justify-between gap-2">
267
+ <span
268
+ className="flex-1 truncate max-w-[180px] block"
269
+ title={col.columnName}
270
+ >
271
+ {col.columnName}
272
+ </span>
273
+ <span className="text-xs text-muted-foreground dark:text-neutral-400">
274
+ ({col.type})
275
+ </span>
276
+ {rightText && (
277
+ <span className="text-right text-xs text-muted-foreground dark:text-neutral-400">
278
+ {rightText}
279
+ </span>
280
+ )}
281
+ </div>
282
+ </SelectItem>
283
+ );
284
+ });
285
+ }
286
+
287
+ return (
288
+ <div className="px-2 py-1.5 text-sm text-muted-foreground">
289
+ No columns available
290
+ </div>
291
+ );
292
+ })()}
293
+ </SelectContent>
294
+ </Select>
295
+ </div>
296
+ )}
297
+
298
+ {/* On Update action */}
299
+ <div className="flex flex-row gap-10 items-center">
300
+ <Label className="text-sm text-black dark:text-white flex-1">On Update</Label>
301
+ <Select
302
+ value={newForeignKey.onUpdate}
303
+ onValueChange={(value) =>
304
+ setNewForeignKey((prev) => ({
305
+ ...prev,
306
+ onUpdate: value as OnUpdateActionSchema,
307
+ }))
308
+ }
309
+ >
310
+ <SelectTrigger className="w-70 h-10 border-zinc-200 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
311
+ <span className="text-sm text-black dark:text-white">
312
+ {newForeignKey.onUpdate}
313
+ </span>
314
+ </SelectTrigger>
315
+ <SelectContent>
316
+ <SelectItem value="NO ACTION">No Action</SelectItem>
317
+ <SelectItem value="CASCADE">Cascade</SelectItem>
318
+ <SelectItem value="RESTRICT">Restrict</SelectItem>
319
+ </SelectContent>
320
+ </Select>
321
+ </div>
322
+
323
+ {/* On Delete action */}
324
+ <div className="flex flex-row gap-10 items-center">
325
+ <Label className="text-sm text-black dark:text-white flex-1">On Delete</Label>
326
+ <Select
327
+ value={newForeignKey.onDelete}
328
+ onValueChange={(value) =>
329
+ setNewForeignKey((prev) => ({
330
+ ...prev,
331
+ onDelete: value as OnDeleteActionSchema,
332
+ }))
333
+ }
334
+ >
335
+ <SelectTrigger className="w-70 h-10 border-zinc-200 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
336
+ <span className="text-sm text-black dark:text-white">
337
+ {newForeignKey.onDelete}
338
+ </span>
339
+ </SelectTrigger>
340
+ <SelectContent>
341
+ <SelectItem value="NO ACTION">No Action</SelectItem>
342
+ <SelectItem value="CASCADE">Cascade</SelectItem>
343
+ <SelectItem value="SET NULL">Set Null</SelectItem>
344
+ <SelectItem value="SET DEFAULT">Set Default</SelectItem>
345
+ <SelectItem value="RESTRICT">Restrict</SelectItem>
346
+ </SelectContent>
347
+ </Select>
348
+ </div>
349
+ </div>
350
+
351
+ {/* Footer */}
352
+ <div className="flex justify-end gap-3 p-6 border-t border-zinc-200 dark:border-neutral-700">
353
+ <Button
354
+ type="button"
355
+ variant="outline"
356
+ onClick={handleCancelAddForeignKey}
357
+ className="h-10 px-4 dark:bg-neutral-600 dark:text-white dark:border-transparent dark:hover:bg-neutral-700"
358
+ >
359
+ Cancel
360
+ </Button>
361
+ <Button
362
+ type="button"
363
+ onClick={handleAddForeignKey}
364
+ disabled={!isAddButtonEnabled}
365
+ className={`h-10 px-4 ${
366
+ !isAddButtonEnabled
367
+ ? 'bg-zinc-950/40 dark:bg-emerald-300/40'
368
+ : 'bg-zinc-950 dark:text-zinc-950 dark:bg-emerald-300 dark:hover:bg-emerald-400'
369
+ } text-white dark:text-zinc-950 shadow-sm`}
370
+ >
371
+ {initialValue ? 'Update Foreign Key' : 'Add Foreign Key'}
372
+ </Button>
373
+ </div>
374
+ </div>
375
+ </DialogContent>
376
+ </Dialog>
377
+ );
378
+ }
@@ -0,0 +1,288 @@
1
+ import { useState, useEffect, useMemo, useCallback } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/radix/Dialog';
4
+ import { Button } from '@/components/radix/Button';
5
+ import { databaseService } from '@/features/database/services/database.service';
6
+ import { convertSchemaToColumns } from '@/features/database/components/DatabaseDataGrid';
7
+ import { SearchInput, DataGrid, TypeBadge } from '@/components';
8
+ import {
9
+ type CellMouseEvent,
10
+ type CellClickArgs,
11
+ type RenderCellProps,
12
+ type RenderHeaderCellProps,
13
+ type SortColumn,
14
+ SortableHeaderRenderer,
15
+ type DatabaseRecord,
16
+ type ConvertedValue,
17
+ type DataGridRowType,
18
+ } from '@/components/datagrid';
19
+ import { formatValueForDisplay } from '@/lib/utils/utils';
20
+ import { ColumnType } from '@insforge/shared-schemas';
21
+
22
+ const PAGE_SIZE = 50;
23
+
24
+ interface LinkRecordModalProps {
25
+ open: boolean;
26
+ onOpenChange: (open: boolean) => void;
27
+ referenceTable: string;
28
+ referenceColumn: string;
29
+ onSelectRecord: (record: DatabaseRecord) => void;
30
+ currentValue?: string | null;
31
+ }
32
+
33
+ export function LinkRecordModal({
34
+ open,
35
+ onOpenChange,
36
+ referenceTable,
37
+ referenceColumn,
38
+ onSelectRecord,
39
+ }: LinkRecordModalProps) {
40
+ const [searchQuery, setSearchQuery] = useState('');
41
+ const [selectedRecord, setSelectedRecord] = useState<DatabaseRecord | null>(null);
42
+ const [sortColumns, setSortColumns] = useState<SortColumn[]>([]);
43
+ const [currentPage, setCurrentPage] = useState(1);
44
+
45
+ // Fetch table schema
46
+ const { data: schema } = useQuery({
47
+ queryKey: ['table-schema', referenceTable],
48
+ queryFn: () => databaseService.getTableSchema(referenceTable),
49
+ enabled: open,
50
+ });
51
+
52
+ // Fetch records from the reference table
53
+ const { data: recordsData, isLoading } = useQuery({
54
+ queryKey: [
55
+ 'table',
56
+ referenceTable,
57
+ currentPage,
58
+ PAGE_SIZE,
59
+ searchQuery,
60
+ JSON.stringify(sortColumns),
61
+ ],
62
+ queryFn: async () => {
63
+ const offset = (currentPage - 1) * PAGE_SIZE;
64
+ const [schema, records] = await Promise.all([
65
+ databaseService.getTableSchema(referenceTable),
66
+ databaseService.getTableRecords(
67
+ referenceTable,
68
+ PAGE_SIZE,
69
+ offset,
70
+ searchQuery || undefined,
71
+ sortColumns
72
+ ),
73
+ ]);
74
+
75
+ return {
76
+ schema,
77
+ records: records.records,
78
+ totalRecords: records.pagination.total || schema.recordCount,
79
+ };
80
+ },
81
+ enabled: open,
82
+ });
83
+
84
+ // Reset page when search query changes
85
+ useEffect(() => {
86
+ setCurrentPage(1);
87
+ }, [searchQuery]);
88
+
89
+ const records = useMemo(
90
+ (): DataGridRowType[] => recordsData?.records || [],
91
+ [recordsData?.records]
92
+ );
93
+ const totalRecords = recordsData?.totalRecords || 0;
94
+ const totalPages = Math.ceil(totalRecords / PAGE_SIZE);
95
+
96
+ // Create selected rows set for highlighting
97
+ const selectedRows = useMemo(() => {
98
+ if (!selectedRecord) {
99
+ return new Set<string>();
100
+ }
101
+ return new Set([String(selectedRecord.id || '')]);
102
+ }, [selectedRecord]);
103
+
104
+ // Handle cell click to select record - only for reference column
105
+ const handleCellClick = useCallback(
106
+ (args: CellClickArgs<DataGridRowType>, event: CellMouseEvent) => {
107
+ // Only allow selection when clicking on the reference column
108
+ if (args.column.key !== referenceColumn) {
109
+ // Prevent the default selection behavior for non-reference columns
110
+ event?.preventDefault();
111
+ event?.stopPropagation();
112
+ return;
113
+ }
114
+
115
+ const record = records.find((r: DatabaseRecord) => String(r.id) === String(args.row.id));
116
+ if (record) {
117
+ setSelectedRecord(record);
118
+ }
119
+ },
120
+ [records, referenceColumn]
121
+ );
122
+
123
+ // Convert schema to columns for the DataGrid with visual distinction
124
+ const columns = useMemo(() => {
125
+ const cols = convertSchemaToColumns(schema);
126
+ // Add visual indication for the reference column (clickable column)
127
+ return cols.map((col) => {
128
+ const baseCol = {
129
+ ...col,
130
+ width: 210,
131
+ minWidth: 210,
132
+ resizable: true,
133
+ editable: false,
134
+ };
135
+
136
+ // Helper function to render cell value properly based on type
137
+ const renderCellValue = (value: ConvertedValue, type: ColumnType | undefined) => {
138
+ return formatValueForDisplay(value, type);
139
+ };
140
+
141
+ if (col.key === referenceColumn) {
142
+ return {
143
+ ...baseCol,
144
+ renderCell: (props: RenderCellProps<DataGridRowType>) => {
145
+ const displayValue = renderCellValue(String(props.row[col.key]), col.type);
146
+ return (
147
+ <div className="w-full h-full flex items-center cursor-pointer">
148
+ <span className="truncate font-medium" title={displayValue}>
149
+ {displayValue}
150
+ </span>
151
+ </div>
152
+ );
153
+ },
154
+ renderHeaderCell: (props: RenderHeaderCellProps<DataGridRowType>) => (
155
+ <SortableHeaderRenderer
156
+ column={col}
157
+ sortDirection={props.sortDirection}
158
+ columnType={col.type}
159
+ showTypeBadge={true}
160
+ mutedHeader={false}
161
+ />
162
+ ),
163
+ };
164
+ }
165
+
166
+ return {
167
+ ...baseCol,
168
+ cellClass: 'link-modal-disabled-cell',
169
+ renderCell: (props: RenderCellProps<DataGridRowType>) => {
170
+ const displayValue = renderCellValue(String(props.row[col.key]), col.type);
171
+ return (
172
+ <div className="w-full h-full flex items-center cursor-not-allowed relative">
173
+ <div className="absolute inset-0 pointer-events-none opacity-0 hover:opacity-10 bg-gray-200 dark:bg-gray-600 transition-opacity z-5" />
174
+ <span className="truncate dark:text-zinc-300 opacity-70" title={displayValue}>
175
+ {displayValue}
176
+ </span>
177
+ </div>
178
+ );
179
+ },
180
+ renderHeaderCell: (props: RenderHeaderCellProps<DataGridRowType>) => (
181
+ <SortableHeaderRenderer
182
+ column={col}
183
+ sortDirection={props.sortDirection}
184
+ columnType={col.type}
185
+ showTypeBadge={true}
186
+ mutedHeader={true}
187
+ />
188
+ ),
189
+ };
190
+ });
191
+ }, [schema, referenceColumn]);
192
+
193
+ const handleConfirmSelection = () => {
194
+ if (selectedRecord) {
195
+ onSelectRecord(selectedRecord);
196
+ onOpenChange(false);
197
+ }
198
+ };
199
+
200
+ const handleCancel = () => {
201
+ onOpenChange(false);
202
+ };
203
+
204
+ return (
205
+ <Dialog open={open} onOpenChange={onOpenChange}>
206
+ <DialogContent className="max-w-4xl h-[calc(100vh-48px)] p-0 gap-0 flex flex-col">
207
+ <DialogHeader className="px-6 py-3 border-b border-zinc-200 dark:border-neutral-700 flex-shrink-0 flex flex-col gap-1">
208
+ <DialogTitle className="text-lg font-semibold text-zinc-950 dark:text-white">
209
+ Link Record
210
+ </DialogTitle>
211
+ <div className="flex items-center gap-1.5">
212
+ <span className="text-sm text-zinc-500 dark:text-neutral-400">
213
+ Select a record to reference from
214
+ </span>
215
+ <TypeBadge
216
+ type={`${referenceTable}.${referenceColumn}`}
217
+ className="dark:bg-neutral-700"
218
+ />
219
+ </div>
220
+ </DialogHeader>
221
+
222
+ {/* Search Bar */}
223
+ <div className="p-3">
224
+ <SearchInput
225
+ value={searchQuery}
226
+ onChange={setSearchQuery}
227
+ placeholder="Search records..."
228
+ className="w-60 dark:text-white dark:bg-neutral-900 dark:border-neutral-700"
229
+ debounceTime={300}
230
+ />
231
+ </div>
232
+
233
+ {/* Records DataGrid */}
234
+ <div className="flex-1 overflow-hidden">
235
+ <DataGrid
236
+ data={records}
237
+ columns={columns}
238
+ loading={isLoading && !records.length}
239
+ selectedRows={selectedRows}
240
+ onSelectedRowsChange={(newSelectedRows) => {
241
+ // Handle selection changes from cell clicks
242
+ const selectedId = Array.from(newSelectedRows)[0];
243
+ if (selectedId) {
244
+ const record = records.find((r: DatabaseRecord) => String(r.id) === selectedId);
245
+ if (record) {
246
+ setSelectedRecord(record);
247
+ }
248
+ } else {
249
+ setSelectedRecord(null);
250
+ }
251
+ }}
252
+ sortColumns={sortColumns}
253
+ onSortColumnsChange={setSortColumns}
254
+ onCellClick={handleCellClick}
255
+ currentPage={currentPage}
256
+ totalPages={totalPages}
257
+ pageSize={PAGE_SIZE}
258
+ totalRecords={totalRecords}
259
+ onPageChange={setCurrentPage}
260
+ showSelection={false}
261
+ showPagination={true}
262
+ emptyStateTitle={
263
+ searchQuery ? 'No records match your search criteria' : 'No records found'
264
+ }
265
+ />
266
+ </div>
267
+
268
+ {/* Footer */}
269
+ <div className="px-6 py-4 border-t border-zinc-200 dark:border-neutral-700 flex justify-end gap-3 flex-shrink-0">
270
+ <Button
271
+ variant="outline"
272
+ onClick={handleCancel}
273
+ className="dark:bg-neutral-600 dark:text-white dark:border-transparent dark:hover:bg-neutral-700"
274
+ >
275
+ Cancel
276
+ </Button>
277
+ <Button
278
+ onClick={handleConfirmSelection}
279
+ disabled={!selectedRecord}
280
+ className="bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-emerald-300 dark:text-zinc-950 dark:hover:bg-emerald-400"
281
+ >
282
+ Add Record
283
+ </Button>
284
+ </div>
285
+ </DialogContent>
286
+ </Dialog>
287
+ );
288
+ }