sea-dev 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/tasks/README.md +89 -0
- package/.cursor/rules/commits.mdc +31 -0
- package/.cursor/rules/general.mdc +84 -0
- package/.github/workflows/ci-cd.yml +141 -0
- package/CLAUDE.md +337 -0
- package/README.md +129 -0
- package/apps/api/.prettierignore +6 -0
- package/apps/api/.prettierrc.js +3 -0
- package/apps/api/dotenvx-safe.sh +11 -0
- package/apps/api/eslint.config.mjs +3 -0
- package/apps/api/package.json +58 -0
- package/apps/api/src/clients/posthog.ts +25 -0
- package/apps/api/src/dal/submission.ts +59 -0
- package/apps/api/src/errors.ts +55 -0
- package/apps/api/src/index.ts +21 -0
- package/apps/api/src/lib/channel.ts +28 -0
- package/apps/api/src/lib/config.ts +9 -0
- package/apps/api/src/lib/fmt.test.ts +9 -0
- package/apps/api/src/lib/fmt.ts +62 -0
- package/apps/api/src/lib/invariant.ts +23 -0
- package/apps/api/src/middleware/auth.ts +66 -0
- package/apps/api/src/routes/index.ts +20 -0
- package/apps/api/src/routes/v2/chat/handlers.ts +693 -0
- package/apps/api/src/routes/v2/chat/index.ts +257 -0
- package/apps/api/src/routes/v2/chat/schemas.ts +43 -0
- package/apps/api/src/routes/v2/deals/handlers.ts +64 -0
- package/apps/api/src/routes/v2/deals/index.ts +88 -0
- package/apps/api/src/routes/v2/deals/schemas.ts +38 -0
- package/apps/api/src/routes/v2/forms/handlers.ts +415 -0
- package/apps/api/src/routes/v2/forms/index.ts +382 -0
- package/apps/api/src/routes/v2/forms/schemas.ts +243 -0
- package/apps/api/src/routes/v2/index.ts +19 -0
- package/apps/api/src/routes/v2/pipelines/handlers.ts +261 -0
- package/apps/api/src/routes/v2/pipelines/index.ts +224 -0
- package/apps/api/src/routes/v2/pipelines/schemas.ts +173 -0
- package/apps/api/src/routes/v2/submissions/handlers.ts +555 -0
- package/apps/api/src/routes/v2/submissions/index.ts +366 -0
- package/apps/api/src/routes/v2/submissions/schemas.ts +233 -0
- package/apps/api/src/routes/v2/workflows/handlers.ts +81 -0
- package/apps/api/src/routes/v2/workflows/index.ts +88 -0
- package/apps/api/src/routes/v2/workflows/schemas.ts +40 -0
- package/apps/api/src/server.ts +146 -0
- package/apps/api/src/static/favicon.ico +0 -0
- package/apps/api/src/types/api.ts +14 -0
- package/apps/api/src/types/result.ts +3 -0
- package/apps/api/tsconfig.json +22 -0
- package/apps/api/vite.config.ts +28 -0
- package/apps/api/vitest.config.ts +14 -0
- package/apps/conversion-worker/Dockerfile +59 -0
- package/apps/conversion-worker/package.json +31 -0
- package/apps/conversion-worker/src/lib/config.ts +7 -0
- package/apps/conversion-worker/src/main.ts +22 -0
- package/apps/conversion-worker/src/workflows/convert-pptx.ts +116 -0
- package/apps/conversion-worker/tsconfig.json +27 -0
- package/apps/conversion-worker/vite.config.ts +33 -0
- package/apps/main/.prettierignore +6 -0
- package/apps/main/.prettierrc.js +3 -0
- package/apps/main/CLAUDE.md +245 -0
- package/apps/main/Procfile +1 -0
- package/apps/main/README.md +193 -0
- package/apps/main/db-tests.jsonl +116 -0
- package/apps/main/dotenvx-safe.sh +11 -0
- package/apps/main/drizzle/meta/_journal.json +1 -0
- package/apps/main/drizzle.config.ts +25 -0
- package/apps/main/eslint.config.mjs +3 -0
- package/apps/main/generate-routes.mjs +5 -0
- package/apps/main/package.json +131 -0
- package/apps/main/playwright.config.ts +23 -0
- package/apps/main/postcss.config.ts +5 -0
- package/apps/main/public/bg-dark.svg +10 -0
- package/apps/main/public/bg.svg +10 -0
- package/apps/main/public/favicon.ico +0 -0
- package/apps/main/run.sh +146 -0
- package/apps/main/scripts/browser.ts +14 -0
- package/apps/main/scripts/db-test-cov.ts +277 -0
- package/apps/main/scripts/login.ts +78 -0
- package/apps/main/scripts/repl.ts +61 -0
- package/apps/main/src/_foo.ts +31 -0
- package/apps/main/src/_tests/db.test.ts +19 -0
- package/apps/main/src/_tests/mock-db.ts +60 -0
- package/apps/main/src/client.tsx +13 -0
- package/apps/main/src/clients/loops.ts +13 -0
- package/apps/main/src/clients/polar.ts +12 -0
- package/apps/main/src/clients/posthog.ts +12 -0
- package/apps/main/src/components/chat/chat-context.tsx +99 -0
- package/apps/main/src/components/chat/chat-messages.tsx +184 -0
- package/apps/main/src/components/chat/chat-status.tsx +140 -0
- package/apps/main/src/components/chat/chat.tsx +458 -0
- package/apps/main/src/components/chat/citation-modal.tsx +54 -0
- package/apps/main/src/components/cta.tsx +21 -0
- package/apps/main/src/components/data-display/derived.tsx +40 -0
- package/apps/main/src/components/data-display/group-single.tsx +57 -0
- package/apps/main/src/components/data-display/group-table.tsx +165 -0
- package/apps/main/src/components/data-display/group-wrapper.tsx +54 -0
- package/apps/main/src/components/data-display/item.tsx +678 -0
- package/apps/main/src/components/error.tsx +45 -0
- package/apps/main/src/components/forms/error.tsx +22 -0
- package/apps/main/src/components/grid.tsx +7 -0
- package/apps/main/src/components/header/container.tsx +73 -0
- package/apps/main/src/components/header/header-bar.tsx +102 -0
- package/apps/main/src/components/modals/copy-display.tsx +37 -0
- package/apps/main/src/components/modals/copy-form.tsx +152 -0
- package/apps/main/src/components/modals/duplicate-workflow.tsx +89 -0
- package/apps/main/src/components/modals/field-correction.tsx +323 -0
- package/apps/main/src/components/modals/form-viewer.tsx +126 -0
- package/apps/main/src/components/modals/modals.tsx +44 -0
- package/apps/main/src/components/modals/new-deal.tsx +78 -0
- package/apps/main/src/components/modals/new-form.tsx +133 -0
- package/apps/main/src/components/modals/new-pipeline.tsx +70 -0
- package/apps/main/src/components/modals/new-submission.tsx +321 -0
- package/apps/main/src/components/modals/new-workflow.tsx +342 -0
- package/apps/main/src/components/modals/transformation-sources-modal.tsx +157 -0
- package/apps/main/src/components/modals/view-report.tsx +193 -0
- package/apps/main/src/components/not-found.tsx +14 -0
- package/apps/main/src/components/search/search-bar.tsx +178 -0
- package/apps/main/src/components/sheet-selector.tsx +135 -0
- package/apps/main/src/components/side-panel/doc-list.tsx +480 -0
- package/apps/main/src/components/sidebar/admin-sidebar.tsx +75 -0
- package/apps/main/src/components/sidebar/app-sidebar.tsx +417 -0
- package/apps/main/src/components/sidebar/model-select.tsx +134 -0
- package/apps/main/src/components/sidebar/settings-sidebar.tsx +132 -0
- package/apps/main/src/components/sidebar/sidebar-right.tsx +22 -0
- package/apps/main/src/components/sidebar/stop-impersonate.tsx +21 -0
- package/apps/main/src/components/svg/loading.tsx +33 -0
- package/apps/main/src/components/theme-selector.tsx +43 -0
- package/apps/main/src/components/unsaved-badge.tsx +19 -0
- package/apps/main/src/components/upload/file-upload.tsx +354 -0
- package/apps/main/src/fns/submission-groups.ts +28 -0
- package/apps/main/src/fns/submission-items.ts +11 -0
- package/apps/main/src/global-middleware.ts +16 -0
- package/apps/main/src/hooks/use-update-state.ts +18 -0
- package/apps/main/src/lib/auth-client.ts +16 -0
- package/apps/main/src/lib/auth.test.ts +359 -0
- package/apps/main/src/lib/auth.ts +144 -0
- package/apps/main/src/lib/billing.ts +23 -0
- package/apps/main/src/lib/config-iso.ts +76 -0
- package/apps/main/src/lib/config.ts +61 -0
- package/apps/main/src/lib/excel.ts +16 -0
- package/apps/main/src/lib/feedback-cache.ts +70 -0
- package/apps/main/src/lib/logger.ts +44 -0
- package/apps/main/src/lib/models.ts +22 -0
- package/apps/main/src/lib/not-found.ts +17 -0
- package/apps/main/src/lib/pdf.ts +16 -0
- package/apps/main/src/lib/tabularize.ts +54 -0
- package/apps/main/src/lib/utils.ts +10 -0
- package/apps/main/src/lib/zfd.ts +217 -0
- package/apps/main/src/middleware.ts +55 -0
- package/apps/main/src/routeTree.gen.ts +1255 -0
- package/apps/main/src/router.tsx +24 -0
- package/apps/main/src/routes/__root.tsx +92 -0
- package/apps/main/src/routes/_authed/_app/(dashboard)/index.tsx +227 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/config.tsx +224 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/index.tsx +206 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-actions-menu.tsx +94 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-artifacts.tsx +153 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-chat.tsx +220 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-history-menu.tsx +81 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-model-select.tsx +84 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-relevant-items.tsx +226 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-upload-button.tsx +298 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/context-modal.tsx +187 -0
- package/apps/main/src/routes/_authed/_app/agents/-fns.ts +560 -0
- package/apps/main/src/routes/_authed/_app/agents/index.tsx +65 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId/-components/citation-tree.tsx +268 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId.tsx +655 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/doc-loading.tsx +37 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/share-link.tsx +42 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-card.tsx +89 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-filter.tsx +193 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submissions.tsx +36 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/summary.tsx +82 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-fns.ts +653 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/index.tsx +259 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/route.tsx +29 -0
- package/apps/main/src/routes/_authed/_app/deals/index.tsx +104 -0
- package/apps/main/src/routes/_authed/_app/feedback/index.tsx +639 -0
- package/apps/main/src/routes/_authed/_app/feedback/insights.tsx +250 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/blockers-panel.tsx +260 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/manual-input-panel.tsx +301 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/submission-selector-modal.tsx +143 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/index.tsx +1485 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/dag-view.tsx +296 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/step-config-modal.tsx +634 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/index.tsx +911 -0
- package/apps/main/src/routes/_authed/_app/pipelines/-fns.ts +510 -0
- package/apps/main/src/routes/_authed/_app/pipelines/index.tsx +103 -0
- package/apps/main/src/routes/_authed/_app/reports/$reportId.tsx +397 -0
- package/apps/main/src/routes/_authed/_app/reports/-fns.ts +11 -0
- package/apps/main/src/routes/_authed/_app/reports/index.tsx +22 -0
- package/apps/main/src/routes/_authed/_app/route.tsx +48 -0
- package/apps/main/src/routes/_authed/_app/submissions/-columns.tsx +161 -0
- package/apps/main/src/routes/_authed/_app/submissions/-fns.ts +128 -0
- package/apps/main/src/routes/_authed/_app/submissions/index.tsx +190 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/$formId.tsx +542 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/derived.tsx +154 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/field.tsx +369 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/group.tsx +475 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/index.tsx +263 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/route.tsx +33 -0
- package/apps/main/src/routes/_authed/_app/workflows/-components/form-card.tsx +315 -0
- package/apps/main/src/routes/_authed/_app/workflows/index.tsx +86 -0
- package/apps/main/src/routes/_authed/admin/index.tsx +12 -0
- package/apps/main/src/routes/_authed/admin/route.tsx +42 -0
- package/apps/main/src/routes/_authed/admin/users/-columns.tsx +124 -0
- package/apps/main/src/routes/_authed/admin/users/-fns.ts +30 -0
- package/apps/main/src/routes/_authed/admin/users/index.tsx +29 -0
- package/apps/main/src/routes/_authed/catchNotFound.tsx +114 -0
- package/apps/main/src/routes/_authed/redirects/forms.$id.tsx +29 -0
- package/apps/main/src/routes/_authed/redirects/submissions.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/redirects/workflows.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/route.tsx +51 -0
- package/apps/main/src/routes/_authed/settings/-components/new-api-key.tsx +85 -0
- package/apps/main/src/routes/_authed/settings/-components/new-invite.tsx +100 -0
- package/apps/main/src/routes/_authed/settings/analytics.tsx +1710 -0
- package/apps/main/src/routes/_authed/settings/billing/-components/price-table.tsx +129 -0
- package/apps/main/src/routes/_authed/settings/billing/-fns.ts +76 -0
- package/apps/main/src/routes/_authed/settings/billing/index.tsx +119 -0
- package/apps/main/src/routes/_authed/settings/embed.tsx +337 -0
- package/apps/main/src/routes/_authed/settings/index.tsx +12 -0
- package/apps/main/src/routes/_authed/settings/keys.tsx +157 -0
- package/apps/main/src/routes/_authed/settings/members.tsx +276 -0
- package/apps/main/src/routes/_authed/settings/route.tsx +22 -0
- package/apps/main/src/routes/_authed/settings/user.tsx +87 -0
- package/apps/main/src/routes/_authed/settings/workspace.tsx +206 -0
- package/apps/main/src/routes/_public/-components/sign-in-up.tsx +96 -0
- package/apps/main/src/routes/_public/embedded.tsx +57 -0
- package/apps/main/src/routes/_public/invite.$inviteId.tsx +143 -0
- package/apps/main/src/routes/_public/no-access.tsx +38 -0
- package/apps/main/src/routes/_public/no-invite.tsx +39 -0
- package/apps/main/src/routes/_public/otp.tsx +103 -0
- package/apps/main/src/routes/_public/route.tsx +15 -0
- package/apps/main/src/routes/_public/sign-in.tsx +111 -0
- package/apps/main/src/routes/_public/sign-up.tsx +114 -0
- package/apps/main/src/routes/api/auth/$.ts +11 -0
- package/apps/main/src/routes/api/billing/paid.ts +42 -0
- package/apps/main/src/routes/api/billing/webhooks.ts +70 -0
- package/apps/main/src/routes/api/chat/agent.ts +40 -0
- package/apps/main/src/routes/api/chat/key.ts +42 -0
- package/apps/main/src/routes/api/chat/member.ts +35 -0
- package/apps/main/src/routes/api/test/index.ts +19 -0
- package/apps/main/src/server.tsx +6 -0
- package/apps/main/src/styles/app.css +23 -0
- package/apps/main/src/vite-env.d.ts +7 -0
- package/apps/main/test.http +6 -0
- package/apps/main/tsconfig.json +17 -0
- package/apps/main/vite.config.ts +24 -0
- package/apps/main/vitest.config.js +17 -0
- package/apps/mcp/README.md +171 -0
- package/apps/mcp/eslint.config.mjs +3 -0
- package/apps/mcp/package.json +37 -0
- package/apps/mcp/src/index.ts +414 -0
- package/apps/mcp/tsconfig.json +19 -0
- package/apps/mcp/vite.config.ts +22 -0
- package/apps/posthog-proxy/index.html +9 -0
- package/apps/workers/.prettierignore +7 -0
- package/apps/workers/.prettierrc.js +3 -0
- package/apps/workers/dotenvx-safe.sh +11 -0
- package/apps/workers/eslint.config.mjs +3 -0
- package/apps/workers/package.json +65 -0
- package/apps/workers/src/lib/config.ts +7 -0
- package/apps/workers/src/lib/messages.ts +0 -0
- package/apps/workers/src/lib/posthog.ts +25 -0
- package/apps/workers/src/main.ts +58 -0
- package/apps/workers/src/workflows/extraction.ts +866 -0
- package/apps/workers/src/workflows/index.ts +3 -0
- package/apps/workers/src/workflows/pipeline-dag.ts +210 -0
- package/apps/workers/src/workflows/pipeline-steps.ts +1393 -0
- package/apps/workers/tsconfig.json +16 -0
- package/apps/workers/vite.config.ts +35 -0
- package/docs/CHANGELOG.md +84 -0
- package/docs/agent-templates-and-runs.md +859 -0
- package/docs/aws-migration-plan.md +267 -0
- package/docs/impl-p0-form-builder-improvements.md +683 -0
- package/docs/on-prem-deployment-spec.docx +0 -0
- package/docs/on-prem-deployment-spec.md +378 -0
- package/docs/prd-form-builder-strategy.md +1120 -0
- package/docs/widget-ng-apf-packaging-spec.md +43 -0
- package/infra/k8s/charts/seadotdev/Chart.yaml +6 -0
- package/infra/k8s/charts/seadotdev/templates/_helpers.tpl +27 -0
- package/infra/k8s/charts/seadotdev/templates/api-v2.yaml +105 -0
- package/infra/k8s/charts/seadotdev/templates/external-secrets.yaml +83 -0
- package/infra/k8s/charts/seadotdev/templates/ingress.yaml +54 -0
- package/infra/k8s/charts/seadotdev/templates/main-app.yaml +104 -0
- package/infra/k8s/charts/seadotdev/templates/workers.yaml +182 -0
- package/infra/k8s/charts/seadotdev/values.yaml +143 -0
- package/infra/terraform/main.tf +399 -0
- package/libs/ai/.prettierignore +2 -0
- package/libs/ai/.prettierrc.js +5 -0
- package/libs/ai/README.md +139 -0
- package/libs/ai/eslint.config.mjs +3 -0
- package/libs/ai/package.json +42 -0
- package/libs/ai/src/index.ts +5 -0
- package/libs/ai/src/models.ts +19 -0
- package/libs/ai/src/rag/index.ts +1 -0
- package/libs/ai/src/rag/rag.test.ts +99 -0
- package/libs/ai/src/rag/rag.ts +510 -0
- package/libs/ai/tsconfig.json +21 -0
- package/libs/ai/vite.config.ts +38 -0
- package/libs/cache/.prettierignore +2 -0
- package/libs/cache/eslint.config.mjs +3 -0
- package/libs/cache/package.json +35 -0
- package/libs/cache/src/feedback.ts +77 -0
- package/libs/cache/src/index.ts +2 -0
- package/libs/cache/tsconfig.json +19 -0
- package/libs/cache/vite.config.ts +36 -0
- package/libs/clients/.prettierignore +6 -0
- package/libs/clients/eslint.config.mjs +3 -0
- package/libs/clients/package.json +59 -0
- package/libs/clients/src/azure.ts +249 -0
- package/libs/clients/src/gcp.ts +220 -0
- package/libs/clients/src/hatchet.ts +86 -0
- package/libs/clients/src/index.ts +8 -0
- package/libs/clients/src/loops.ts +86 -0
- package/libs/clients/src/polar.ts +77 -0
- package/libs/clients/src/posthog.ts +55 -0
- package/libs/clients/tsconfig.json +19 -0
- package/libs/clients/vite.config.ts +35 -0
- package/libs/config/.prettierignore +6 -0
- package/libs/config/.prettierrc.js +12 -0
- package/libs/config/eslint.config.mjs +3 -0
- package/libs/config/package.json +50 -0
- package/libs/config/src/azure.ts +54 -0
- package/libs/config/src/db.ts +18 -0
- package/libs/config/src/gcp.ts +53 -0
- package/libs/config/src/google.ts +17 -0
- package/libs/config/src/hatchet.ts +20 -0
- package/libs/config/src/index.ts +108 -0
- package/libs/config/src/llm.ts +17 -0
- package/libs/config/src/polar.ts +24 -0
- package/libs/config/src/util.ts +8 -0
- package/libs/config/src/vercel.ts +26 -0
- package/libs/config/tsconfig.json +19 -0
- package/libs/config/vite.config.ts +34 -0
- package/libs/core/.prettierignore +2 -0
- package/libs/core/eslint.config.mjs +3 -0
- package/libs/core/package.json +59 -0
- package/libs/core/src/chat/derived.ts +97 -0
- package/libs/core/src/chat/feedback.ts +293 -0
- package/libs/core/src/chat/index.ts +6 -0
- package/libs/core/src/chat/model.ts +92 -0
- package/libs/core/src/chat/prepare-tools.ts +286 -0
- package/libs/core/src/chat/prompts.ts +623 -0
- package/libs/core/src/chat/stream.ts +311 -0
- package/libs/core/src/chat/summarize.ts +168 -0
- package/libs/core/src/chat/tools/agent.ts +403 -0
- package/libs/core/src/chat/tools/chart-agent.ts +526 -0
- package/libs/core/src/chat/tools/chart-helpers/sandbox.ts +47 -0
- package/libs/core/src/chat/tools/chart.ts +86 -0
- package/libs/core/src/chat/tools/credit-agent.ts +1383 -0
- package/libs/core/src/chat/tools/credit.ts +1435 -0
- package/libs/core/src/chat/tools/deep-dive-agent.ts +100 -0
- package/libs/core/src/chat/tools/deep-dive.ts +141 -0
- package/libs/core/src/chat/tools/form.ts +449 -0
- package/libs/core/src/chat/tools/helpers.ts +91 -0
- package/libs/core/src/chat/tools/index.ts +42 -0
- package/libs/core/src/chat/tools/pipeline-artifact.ts +76 -0
- package/libs/core/src/chat/tools/report.ts +40 -0
- package/libs/core/src/chat/tools/search.ts +390 -0
- package/libs/core/src/chat/tools/submission.ts +227 -0
- package/libs/core/src/chat/tools/workflow.ts +684 -0
- package/libs/core/src/chat/types.ts +3 -0
- package/libs/core/src/data-extraction/classification/azure.ts +168 -0
- package/libs/core/src/data-extraction/classification/index.ts +1 -0
- package/libs/core/src/data-extraction/dal.ts +246 -0
- package/libs/core/src/data-extraction/form-structure-extractor.ts +294 -0
- package/libs/core/src/data-extraction/index.ts +4 -0
- package/libs/core/src/data-extraction/layout/azure.ts +730 -0
- package/libs/core/src/data-extraction/layout/excel.ts +180 -0
- package/libs/core/src/data-extraction/layout/gcp.ts +1071 -0
- package/libs/core/src/data-extraction/layout/index.ts +266 -0
- package/libs/core/src/data-extraction/layout/plaintext.ts +45 -0
- package/libs/core/src/data-extraction/models.ts +38 -0
- package/libs/core/src/data-extraction/pdf-utils.ts +96 -0
- package/libs/core/src/data-extraction/structuring/bank-statement.ts +1182 -0
- package/libs/core/src/data-extraction/structuring/custom.ts +495 -0
- package/libs/core/src/data-extraction/structuring/index.ts +290 -0
- package/libs/core/src/data-extraction/structuring/prompts.ts +69 -0
- package/libs/core/src/data-extraction/type-guards.ts +110 -0
- package/libs/core/src/data-extraction/types.ts +84 -0
- package/libs/core/src/data-extraction/utils.ts +31 -0
- package/libs/core/src/data-extraction/validation/bank-statement.ts +127 -0
- package/libs/core/src/deals.ts +17 -0
- package/libs/core/src/documents.ts +152 -0
- package/libs/core/src/index.ts +5 -0
- package/libs/core/src/pipelines/display.ts +678 -0
- package/libs/core/src/pipelines/execute.ts +2342 -0
- package/libs/core/src/pipelines/index.ts +4 -0
- package/libs/core/src/pipelines/list.ts +12 -0
- package/libs/core/src/pipelines/runs.ts +53 -0
- package/libs/core/tsconfig.json +20 -0
- package/libs/core/vite.config.ts +56 -0
- package/libs/dal/.prettierignore +6 -0
- package/libs/dal/.prettierrc.js +12 -0
- package/libs/dal/eslint.config.mjs +3 -0
- package/libs/dal/package.json +57 -0
- package/libs/dal/src/_tests/db.test.ts +19 -0
- package/libs/dal/src/_tests/mock-db.ts +60 -0
- package/libs/dal/src/api-key.test.ts +397 -0
- package/libs/dal/src/api-key.ts +110 -0
- package/libs/dal/src/billing.ts +23 -0
- package/libs/dal/src/conversation.test.ts +655 -0
- package/libs/dal/src/conversation.ts +532 -0
- package/libs/dal/src/deal.test.ts +45 -0
- package/libs/dal/src/deal.ts +87 -0
- package/libs/dal/src/defaults-consumer-lending-uk.ts +33 -0
- package/libs/dal/src/defaults-consumer-lending-us.ts +33 -0
- package/libs/dal/src/defaults-private-credit.ts +57 -0
- package/libs/dal/src/defaults-private-equity.ts +51 -0
- package/libs/dal/src/defaults-smb-lending-us.ts +1569 -0
- package/libs/dal/src/defaults-sme-lending-uk-express.ts +1527 -0
- package/libs/dal/src/defaults-sme-lending-uk.ts +1669 -0
- package/libs/dal/src/defaults-types.ts +23 -0
- package/libs/dal/src/defaults.ts +550 -0
- package/libs/dal/src/document.test.ts +70 -0
- package/libs/dal/src/document.ts +192 -0
- package/libs/dal/src/feedback.ts +255 -0
- package/libs/dal/src/form.test.ts +637 -0
- package/libs/dal/src/form.ts +1165 -0
- package/libs/dal/src/index.ts +20 -0
- package/libs/dal/src/invitation.test.ts +746 -0
- package/libs/dal/src/invitation.ts +207 -0
- package/libs/dal/src/member.test.ts +185 -0
- package/libs/dal/src/member.ts +80 -0
- package/libs/dal/src/organization.ts +116 -0
- package/libs/dal/src/permission.ts +25 -0
- package/libs/dal/src/pipeline.test.ts +388 -0
- package/libs/dal/src/pipeline.ts +4222 -0
- package/libs/dal/src/report.ts +199 -0
- package/libs/dal/src/result.ts +16 -0
- package/libs/dal/src/search.ts +172 -0
- package/libs/dal/src/session.test.ts +110 -0
- package/libs/dal/src/session.ts +31 -0
- package/libs/dal/src/submission.test.ts +1304 -0
- package/libs/dal/src/submission.ts +1396 -0
- package/libs/dal/src/tool.ts +159 -0
- package/libs/dal/src/user.ts +16 -0
- package/libs/dal/src/workflow.test.ts +89 -0
- package/libs/dal/src/workflow.ts +262 -0
- package/libs/dal/tsconfig.build.json +4 -0
- package/libs/dal/tsconfig.json +22 -0
- package/libs/dal/vite.config.ts +34 -0
- package/libs/db/.prettierignore +6 -0
- package/libs/db/.prettierrc.js +12 -0
- package/libs/db/eslint.config.mjs +3 -0
- package/libs/db/package.json +52 -0
- package/libs/db/src/index.ts +24 -0
- package/libs/db/src/relations.ts +549 -0
- package/libs/db/src/schema.ts +2 -0
- package/libs/db/src/schemas/api.ts +35 -0
- package/libs/db/src/schemas/conversations.ts +175 -0
- package/libs/db/src/schemas/core.ts +359 -0
- package/libs/db/src/schemas/documents.ts +181 -0
- package/libs/db/src/schemas/feedback.ts +40 -0
- package/libs/db/src/schemas/index.ts +26 -0
- package/libs/db/src/schemas/organisations.ts +97 -0
- package/libs/db/src/schemas/pipelines.ts +440 -0
- package/libs/db/src/schemas/users.ts +95 -0
- package/libs/db/src/types.ts +190 -0
- package/libs/db/src/utils.ts +14 -0
- package/libs/db/tsconfig.json +19 -0
- package/libs/db/vite.config.ts +31 -0
- package/libs/lint/.prettierignore +6 -0
- package/libs/lint/eslint.config.mjs +61 -0
- package/libs/lint/package.json +29 -0
- package/libs/lint/prettier.config.js +12 -0
- package/libs/schemas/.prettierignore +6 -0
- package/libs/schemas/.prettierrc.js +12 -0
- package/libs/schemas/README.md +15 -0
- package/libs/schemas/eslint.config.mjs +3 -0
- package/libs/schemas/package.json +67 -0
- package/libs/schemas/src/core/chat.ts +67 -0
- package/libs/schemas/src/core/core-result.ts +15 -0
- package/libs/schemas/src/core/data-extraction.ts +184 -0
- package/libs/schemas/src/core/layout.ts +478 -0
- package/libs/schemas/src/core/pipeline.ts +128 -0
- package/libs/schemas/src/core/submission.ts +97 -0
- package/libs/schemas/src/db/account.ts +57 -0
- package/libs/schemas/src/db/apiKey.ts +57 -0
- package/libs/schemas/src/db/context.ts +33 -0
- package/libs/schemas/src/db/conversation.ts +65 -0
- package/libs/schemas/src/db/deal.ts +42 -0
- package/libs/schemas/src/db/document.ts +103 -0
- package/libs/schemas/src/db/documentCitation.ts +58 -0
- package/libs/schemas/src/db/documentExtraction.ts +69 -0
- package/libs/schemas/src/db/fieldCorrection.ts +85 -0
- package/libs/schemas/src/db/form.ts +45 -0
- package/libs/schemas/src/db/formField.ts +59 -0
- package/libs/schemas/src/db/formGroup.ts +42 -0
- package/libs/schemas/src/db/impersonation.ts +39 -0
- package/libs/schemas/src/db/index.ts +25 -0
- package/libs/schemas/src/db/invitation.ts +42 -0
- package/libs/schemas/src/db/member.ts +36 -0
- package/libs/schemas/src/db/message.ts +58 -0
- package/libs/schemas/src/db/organization.ts +62 -0
- package/libs/schemas/src/db/session.ts +48 -0
- package/libs/schemas/src/db/submission.ts +54 -0
- package/libs/schemas/src/db/submissionGroup.ts +36 -0
- package/libs/schemas/src/db/submissionItem.ts +33 -0
- package/libs/schemas/src/db/submissionItemVersion.ts +70 -0
- package/libs/schemas/src/db/user.ts +51 -0
- package/libs/schemas/src/db/utils.ts +3 -0
- package/libs/schemas/src/db/verification.ts +36 -0
- package/libs/schemas/src/db/workflow.ts +42 -0
- package/libs/schemas/src/index.ts +10 -0
- package/libs/schemas/tsconfig.json +21 -0
- package/libs/schemas/vite.config.ts +38 -0
- package/libs/ui/.prettierignore +6 -0
- package/libs/ui/.prettierrc.js +12 -0
- package/libs/ui/components.json +24 -0
- package/libs/ui/eslint.config.mjs +3 -0
- package/libs/ui/package.json +142 -0
- package/libs/ui/src/components/chart-viz/chart.tsx +255 -0
- package/libs/ui/src/components/chart-viz/converters.ts +474 -0
- package/libs/ui/src/components/chart-viz/dashboard.tsx +146 -0
- package/libs/ui/src/components/chart-viz/index.ts +37 -0
- package/libs/ui/src/components/chart-viz/markdown.tsx +344 -0
- package/libs/ui/src/components/chart-viz/table.tsx +446 -0
- package/libs/ui/src/components/chart-viz/theme-context.tsx +70 -0
- package/libs/ui/src/components/chart-viz/themes/dark.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/index.ts +69 -0
- package/libs/ui/src/components/chart-viz/themes/light.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/tailwind.ts +326 -0
- package/libs/ui/src/components/chart-viz/themes/types.ts +99 -0
- package/libs/ui/src/components/chart-viz/tool-display.tsx +150 -0
- package/libs/ui/src/components/chart-viz/types.ts +95 -0
- package/libs/ui/src/components/doc-viewers/excel/index.tsx +431 -0
- package/libs/ui/src/components/doc-viewers/excel/themes.ts +160 -0
- package/libs/ui/src/components/doc-viewers/image/index.tsx +410 -0
- package/libs/ui/src/components/doc-viewers/pdf/index.tsx +258 -0
- package/libs/ui/src/components/doc-viewers/pdf/virtualized-pdf.tsx +556 -0
- package/libs/ui/src/components/misc/rel-date.tsx +52 -0
- package/libs/ui/src/components/misc/styled-link.tsx +2 -0
- package/libs/ui/src/components/table/data-table.tsx +546 -0
- package/libs/ui/src/components/table/report-table.tsx +305 -0
- package/libs/ui/src/components/table/sortable-column.tsx +34 -0
- package/libs/ui/src/components/ui/accordion.tsx +62 -0
- package/libs/ui/src/components/ui/alert-dialog.tsx +142 -0
- package/libs/ui/src/components/ui/alert.tsx +62 -0
- package/libs/ui/src/components/ui/artifact.tsx +118 -0
- package/libs/ui/src/components/ui/attachments.tsx +388 -0
- package/libs/ui/src/components/ui/avatar.tsx +39 -0
- package/libs/ui/src/components/ui/badge.tsx +43 -0
- package/libs/ui/src/components/ui/breadcrumb.tsx +102 -0
- package/libs/ui/src/components/ui/button-group.tsx +78 -0
- package/libs/ui/src/components/ui/button.tsx +79 -0
- package/libs/ui/src/components/ui/card.tsx +32 -0
- package/libs/ui/src/components/ui/carousel.tsx +228 -0
- package/libs/ui/src/components/ui/chain-of-thought.tsx +198 -0
- package/libs/ui/src/components/ui/checkbox.tsx +27 -0
- package/libs/ui/src/components/ui/citation.tsx +34 -0
- package/libs/ui/src/components/ui/code-block.tsx +500 -0
- package/libs/ui/src/components/ui/collapsible.tsx +19 -0
- package/libs/ui/src/components/ui/command.tsx +161 -0
- package/libs/ui/src/components/ui/conversation.tsx +90 -0
- package/libs/ui/src/components/ui/dialog.tsx +142 -0
- package/libs/ui/src/components/ui/dropdown-menu.tsx +246 -0
- package/libs/ui/src/components/ui/highlight.tsx +3 -0
- package/libs/ui/src/components/ui/hover-card.tsx +36 -0
- package/libs/ui/src/components/ui/inline-citation.tsx +251 -0
- package/libs/ui/src/components/ui/input-group.tsx +156 -0
- package/libs/ui/src/components/ui/input-otp.tsx +78 -0
- package/libs/ui/src/components/ui/input.tsx +21 -0
- package/libs/ui/src/components/ui/label.tsx +19 -0
- package/libs/ui/src/components/ui/model-selector.tsx +174 -0
- package/libs/ui/src/components/ui/multisidebar.tsx +750 -0
- package/libs/ui/src/components/ui/popover.tsx +43 -0
- package/libs/ui/src/components/ui/progress.tsx +28 -0
- package/libs/ui/src/components/ui/reasoning.tsx +178 -0
- package/libs/ui/src/components/ui/resizable.tsx +49 -0
- package/libs/ui/src/components/ui/scroll-area.tsx +54 -0
- package/libs/ui/src/components/ui/select.tsx +171 -0
- package/libs/ui/src/components/ui/separator.tsx +26 -0
- package/libs/ui/src/components/ui/sheet.tsx +128 -0
- package/libs/ui/src/components/ui/shimmer.tsx +53 -0
- package/libs/ui/src/components/ui/skeleton.tsx +13 -0
- package/libs/ui/src/components/ui/sonner.tsx +23 -0
- package/libs/ui/src/components/ui/switch.tsx +26 -0
- package/libs/ui/src/components/ui/table.tsx +96 -0
- package/libs/ui/src/components/ui/tabs.tsx +52 -0
- package/libs/ui/src/components/ui/textarea.tsx +41 -0
- package/libs/ui/src/components/ui/tool.tsx +209 -0
- package/libs/ui/src/components/ui/tooltip.tsx +58 -0
- package/libs/ui/src/components/ui/typography.tsx +113 -0
- package/libs/ui/src/fonts/manrope-v15-latin-300.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-400.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-500.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-600.woff2 +0 -0
- package/libs/ui/src/hooks/use-mobile.ts +19 -0
- package/libs/ui/src/lib/utils.ts +6 -0
- package/libs/ui/src/styles/fonts.css +35 -0
- package/libs/ui/src/styles/style.css +218 -0
- package/libs/ui/tsconfig.json +21 -0
- package/libs/ui/vite.config.ts +80 -0
- package/libs/ui-lit/README.md +245 -0
- package/libs/ui-lit/TESTING_GUIDE.md +296 -0
- package/libs/ui-lit/eslint.config.mjs +3 -0
- package/libs/ui-lit/package.json +41 -0
- package/libs/ui-lit/scripts/build-css.js +43 -0
- package/libs/ui-lit/src/components/sea-alert.ts +132 -0
- package/libs/ui-lit/src/components/sea-button.ts +95 -0
- package/libs/ui-lit/src/components/sea-card.ts +113 -0
- package/libs/ui-lit/src/components/sea-input.ts +184 -0
- package/libs/ui-lit/src/components/sea-spinner.ts +65 -0
- package/libs/ui-lit/src/index.ts +15 -0
- package/libs/ui-lit/src/lib/utils.ts +6 -0
- package/libs/ui-lit/src/styles/tailwind.css +76 -0
- package/libs/ui-lit/src/theme.css +66 -0
- package/libs/ui-lit/src/theme.ts +79 -0
- package/libs/ui-lit/src/vite-env.d.ts +6 -0
- package/libs/ui-lit/tailwind.config.ts +50 -0
- package/libs/ui-lit/test.html +289 -0
- package/libs/ui-lit/tsconfig.json +23 -0
- package/libs/ui-lit/vite.config.ts +31 -0
- package/libs/ui-lit/vite.css.config.ts +20 -0
- package/libs/util/.prettierignore +6 -0
- package/libs/util/.prettierrc.js +12 -0
- package/libs/util/eslint.config.mjs +3 -0
- package/libs/util/package.json +45 -0
- package/libs/util/src/billing.ts +10 -0
- package/libs/util/src/data-transform.ts +19 -0
- package/libs/util/src/encryption.ts +45 -0
- package/libs/util/src/fmt.test.ts +9 -0
- package/libs/util/src/fmt.ts +71 -0
- package/libs/util/src/fuzzy.ts +47 -0
- package/libs/util/src/id.ts +24 -0
- package/libs/util/src/invariant.ts +31 -0
- package/libs/util/src/sub-name.ts +7 -0
- package/libs/util/tsconfig.json +19 -0
- package/libs/util/vite.config.ts +34 -0
- package/package.json +28 -0
- package/packages/widget/.prettierignore +6 -0
- package/packages/widget/.prettierrc.js +12 -0
- package/packages/widget/README.md +95 -0
- package/packages/widget/eslint.config.mjs +11 -0
- package/packages/widget/openapi-ts.config.ts +8 -0
- package/packages/widget/package.json +89 -0
- package/packages/widget/postcss.config.mjs +10 -0
- package/packages/widget/src/clients/api/client/client.ts +187 -0
- package/packages/widget/src/clients/api/client/index.ts +22 -0
- package/packages/widget/src/clients/api/client/types.ts +192 -0
- package/packages/widget/src/clients/api/client/utils.ts +394 -0
- package/packages/widget/src/clients/api/client.gen.ts +18 -0
- package/packages/widget/src/clients/api/core/auth.ts +39 -0
- package/packages/widget/src/clients/api/core/bodySerializer.ts +74 -0
- package/packages/widget/src/clients/api/core/params.ts +132 -0
- package/packages/widget/src/clients/api/core/pathSerializer.ts +169 -0
- package/packages/widget/src/clients/api/core/types.ts +80 -0
- package/packages/widget/src/clients/api/index.ts +3 -0
- package/packages/widget/src/clients/api/sdk.gen.ts +805 -0
- package/packages/widget/src/clients/api/types.gen.ts +2085 -0
- package/packages/widget/src/components/container.tsx +42 -0
- package/packages/widget/src/components/data-display.tsx +384 -0
- package/packages/widget/src/components/data-viewer.tsx +311 -0
- package/packages/widget/src/components/doc-list.tsx +102 -0
- package/packages/widget/src/components/field-correction-modal.tsx +265 -0
- package/packages/widget/src/components/header.tsx +71 -0
- package/packages/widget/src/components/new-submission.tsx +290 -0
- package/packages/widget/src/components/sidebar-right.tsx +19 -0
- package/packages/widget/src/components/submission-card.tsx +66 -0
- package/packages/widget/src/components/submission-page.tsx +75 -0
- package/packages/widget/src/components/upload-doc.tsx +241 -0
- package/packages/widget/src/components/widget.tsx +101 -0
- package/packages/widget/src/index.tsx +167 -0
- package/packages/widget/src/lib/config.ts +2 -0
- package/packages/widget/src/lib/util.ts +40 -0
- package/packages/widget/src/styles/index.css +5 -0
- package/packages/widget/src/styles/tw-properties.css +337 -0
- package/packages/widget/src/vite-env.d.ts +3 -0
- package/packages/widget/tsconfig.app.json +35 -0
- package/packages/widget/tsconfig.json +4 -0
- package/packages/widget/tsconfig.node.json +24 -0
- package/packages/widget/vite.config.ts +116 -0
- package/packages/widget-lit/BOTTLENECKS.md +250 -0
- package/packages/widget-lit/IMPLEMENTATION_SUMMARY.md +295 -0
- package/packages/widget-lit/README.md +232 -0
- package/packages/widget-lit/eslint.config.mjs +3 -0
- package/packages/widget-lit/package.json +52 -0
- package/packages/widget-lit/src/api-client.ts +230 -0
- package/packages/widget-lit/src/api-client.ts.backup +218 -0
- package/packages/widget-lit/src/components/sea-chat.ts +382 -0
- package/packages/widget-lit/src/components/sea-submission-viewer.ts +267 -0
- package/packages/widget-lit/src/components/sea-widget.ts +317 -0
- package/packages/widget-lit/src/index.ts +48 -0
- package/packages/widget-lit/src/react.ts +58 -0
- package/packages/widget-lit/src/style.css +47 -0
- package/packages/widget-lit/tsconfig.json +24 -0
- package/packages/widget-lit/vite.config.ts +29 -0
- package/packages/widget-ng/DEVELOPMENT.md +74 -0
- package/packages/widget-ng/README.md +657 -0
- package/packages/widget-ng/dev.sh +14 -0
- package/packages/widget-ng/eslint.config.mjs +24 -0
- package/packages/widget-ng/ng-package.json +9 -0
- package/packages/widget-ng/package.json +85 -0
- package/packages/widget-ng/src/index.ts +45 -0
- package/packages/widget-ng/src/lib/components/sea-chat.component.ts +737 -0
- package/packages/widget-ng/src/lib/components/sea-data-viewer.component.ts +2240 -0
- package/packages/widget-ng/src/lib/components/sea-deal-form-modal.component.ts +702 -0
- package/packages/widget-ng/src/lib/components/sea-document-list.component.ts +350 -0
- package/packages/widget-ng/src/lib/components/sea-feedback-modal.component.ts +461 -0
- package/packages/widget-ng/src/lib/components/sea-file-upload.component.ts +655 -0
- package/packages/widget-ng/src/lib/components/sea-model-selection-modal.component.ts +367 -0
- package/packages/widget-ng/src/lib/components/sea-new-submission-modal.component.ts +414 -0
- package/packages/widget-ng/src/lib/components/sea-pdf-viewer.component.ts +869 -0
- package/packages/widget-ng/src/lib/components/sea-submission-card.component.ts +251 -0
- package/packages/widget-ng/src/lib/components/sea-widget.component.ts +684 -0
- package/packages/widget-ng/src/lib/models/submission.model.ts +170 -0
- package/packages/widget-ng/src/lib/pipes/markdown.pipe.ts +57 -0
- package/packages/widget-ng/src/lib/services/api-client.service.ts +715 -0
- package/packages/widget-ng/src/lib/services/chat.service.ts +330 -0
- package/packages/widget-ng/src/lib/services/config.service.ts +107 -0
- package/packages/widget-ng/src/web-component.ts +56 -0
- package/packages/widget-ng/tsconfig.json +25 -0
- package/packages/widget-ng/tsconfig.lib.json +9 -0
- package/packages/widget-ng/vite.config.elements.ts +26 -0
- package/packages/widget-ng/vitest.config.ts +19 -0
- package/packages/widget-ng/vitest.setup.ts +13 -0
- package/pnpm-workspace.yaml +18 -0
- package/render.yaml +136 -0
- package/scripts/README.md +57 -0
- package/scripts/package.json +22 -0
- package/scripts/python/.python-version +1 -0
- package/scripts/python/README.md +3 -0
- package/scripts/python/export-org-data.py +693 -0
- package/scripts/python/pyproject.toml +29 -0
- package/scripts/python/requirements-dev.lock +36 -0
- package/scripts/python/requirements.lock +36 -0
- package/scripts/python/src/gen.py +297 -0
- package/scripts/python/test.py +34 -0
- package/scripts/src/fix-storage-provider-mismatch.ts +239 -0
- package/scripts/src/sync-render-yaml.ts +290 -0
- package/scripts/src/test-chat-stream.ts +300 -0
- package/scripts/src/test-reconciliation.ts +230 -0
- package/scripts/tsconfig.json +15 -0
- package/tests/angular-test-app/.vscode/extensions.json +4 -0
- package/tests/angular-test-app/.vscode/launch.json +13 -0
- package/tests/angular-test-app/.vscode/tasks.json +24 -0
- package/tests/angular-test-app/README.md +59 -0
- package/tests/angular-test-app/angular.json +111 -0
- package/tests/angular-test-app/clean-start.sh +14 -0
- package/tests/angular-test-app/package.json +36 -0
- package/tests/angular-test-app/public/favicon.ico +0 -0
- package/tests/angular-test-app/src/app/app.component.ts +220 -0
- package/tests/angular-test-app/src/app/app.config.ts +5 -0
- package/tests/angular-test-app/src/env.d.ts +13 -0
- package/tests/angular-test-app/src/index.html +13 -0
- package/tests/angular-test-app/src/main.ts +6 -0
- package/tests/angular-test-app/src/styles.css +8 -0
- package/tests/angular-test-app/tsconfig.app.json +15 -0
- package/tests/angular-test-app/tsconfig.json +27 -0
- package/tests/crm-viewer-app/API_INTEGRATION_SUMMARY.md +295 -0
- package/tests/crm-viewer-app/CURRENT_ASSETS_FIELDS.md +148 -0
- package/tests/crm-viewer-app/FIELD_ID_MAPPING.md +206 -0
- package/tests/crm-viewer-app/INTEGRATION_GUIDE.md +309 -0
- package/tests/crm-viewer-app/README.md +174 -0
- package/tests/crm-viewer-app/REAL_API_INTEGRATION.md +240 -0
- package/tests/crm-viewer-app/UPDATED_IMPLEMENTATION.md +279 -0
- package/tests/crm-viewer-app/angular.json +114 -0
- package/tests/crm-viewer-app/package.json +35 -0
- package/tests/crm-viewer-app/src/app/app.component.ts +534 -0
- package/tests/crm-viewer-app/src/app/citation.service.ts +316 -0
- package/tests/crm-viewer-app/src/env.d.ts +16 -0
- package/tests/crm-viewer-app/src/index.html +19 -0
- package/tests/crm-viewer-app/src/main.ts +7 -0
- package/tests/crm-viewer-app/src/styles.css +409 -0
- package/tests/crm-viewer-app/src/template.html +2678 -0
- package/tests/crm-viewer-app/tsconfig.app.json +15 -0
- package/tests/crm-viewer-app/tsconfig.json +27 -0
- package/tests/e2e/package.json +17 -0
- package/tests/e2e/playwright.config.ts +75 -0
- package/tests/e2e/tests/api/health.spec.ts +10 -0
- package/tests/e2e/tests/app/example.spec.ts +10 -0
- package/tests/widget-test-app/.prettierignore +6 -0
- package/tests/widget-test-app/README.md +48 -0
- package/tests/widget-test-app/index.html +12 -0
- package/tests/widget-test-app/package.json +24 -0
- package/tests/widget-test-app/src/App.css +192 -0
- package/tests/widget-test-app/src/App.tsx +80 -0
- package/tests/widget-test-app/src/main.tsx +9 -0
- package/tests/widget-test-app/src/vite-env.d.ts +4 -0
- package/tests/widget-test-app/tsconfig.json +25 -0
- package/tests/widget-test-app/tsconfig.node.json +11 -0
- package/tests/widget-test-app/vite.config.ts +14 -0
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
import { db, schema } from "@sea/db";
|
|
2
|
+
import type { InsertFormDerived, InsertFormField, InsertFormGroup, Member } from "@sea/db/types";
|
|
3
|
+
import { nanoid } from "@sea/util/id";
|
|
4
|
+
import { invariant, InvariantError } from "@sea/util/invariant";
|
|
5
|
+
import { and, asc, desc, eq, gte, inArray, isNull, sql } from "drizzle-orm";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Retrieves all non-deleted forms for a member's organization
|
|
9
|
+
* @param member - The member whose organization's forms to retrieve
|
|
10
|
+
* @returns Array of forms with their associated workflows
|
|
11
|
+
*/
|
|
12
|
+
export async function getForms({ member }: { member: Member }) {
|
|
13
|
+
const forms = await db.query.form.findMany({
|
|
14
|
+
with: { workflow: true },
|
|
15
|
+
where: and(
|
|
16
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
17
|
+
isNull(schema.form.deletedAt),
|
|
18
|
+
),
|
|
19
|
+
});
|
|
20
|
+
return forms;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Retrieves a specific form with its groups, fields, and derived fields
|
|
25
|
+
* @param member - The member whose organization the form belongs to
|
|
26
|
+
* @param formId - The ID of the form to retrieve
|
|
27
|
+
* @returns Form with nested groups, fields, and derived fields, ordered by their order field
|
|
28
|
+
* @throws Error if form is not found
|
|
29
|
+
*/
|
|
30
|
+
export async function getForm({ member, formId }: { member: Member; formId: string }) {
|
|
31
|
+
const form = await db.query.form.findFirst({
|
|
32
|
+
where: and(
|
|
33
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
34
|
+
isNull(schema.form.deletedAt),
|
|
35
|
+
eq(schema.form.id, formId),
|
|
36
|
+
),
|
|
37
|
+
with: {
|
|
38
|
+
groups: {
|
|
39
|
+
with: {
|
|
40
|
+
fields: {
|
|
41
|
+
orderBy: asc(schema.formField.order),
|
|
42
|
+
},
|
|
43
|
+
derived: true,
|
|
44
|
+
},
|
|
45
|
+
orderBy: asc(schema.formGroup.order),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
invariant(form, `no form found for ${formId}`);
|
|
50
|
+
return form;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a new form with an associated conversation
|
|
55
|
+
* @param member - The member creating the form
|
|
56
|
+
* @param workflowId - The workflow the form belongs to
|
|
57
|
+
* @param name - The name of the form
|
|
58
|
+
* @param hint - Optional hint text for the form
|
|
59
|
+
* @returns The newly created form
|
|
60
|
+
*/
|
|
61
|
+
export async function createForm({
|
|
62
|
+
member,
|
|
63
|
+
workflowId,
|
|
64
|
+
name,
|
|
65
|
+
hint = "",
|
|
66
|
+
}: {
|
|
67
|
+
member: Member;
|
|
68
|
+
workflowId: string;
|
|
69
|
+
name: string;
|
|
70
|
+
hint?: string;
|
|
71
|
+
}) {
|
|
72
|
+
return await db.transaction(async (tx) => {
|
|
73
|
+
// Create conversation for the form
|
|
74
|
+
const [conversation] = await tx
|
|
75
|
+
.insert(schema.conversation)
|
|
76
|
+
.values({ organizationId: member.organizationId })
|
|
77
|
+
.returning();
|
|
78
|
+
invariant(conversation);
|
|
79
|
+
|
|
80
|
+
// Create the form
|
|
81
|
+
const [newForm] = await tx
|
|
82
|
+
.insert(schema.form)
|
|
83
|
+
.values({
|
|
84
|
+
workflowId,
|
|
85
|
+
organizationId: member.organizationId,
|
|
86
|
+
conversationId: conversation.id,
|
|
87
|
+
name,
|
|
88
|
+
hint,
|
|
89
|
+
})
|
|
90
|
+
.returning();
|
|
91
|
+
invariant(newForm);
|
|
92
|
+
|
|
93
|
+
return newForm;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new form with groups and fields in a single transaction
|
|
99
|
+
* @param member - The member creating the form
|
|
100
|
+
* @param workflowId - The workflow the form belongs to
|
|
101
|
+
* @param name - The name of the form
|
|
102
|
+
* @param hint - Optional hint text for the form
|
|
103
|
+
* @param groups - Array of groups with their fields to create
|
|
104
|
+
* @returns The complete form with all nested groups and fields
|
|
105
|
+
*/
|
|
106
|
+
export async function createFormWithGroups({
|
|
107
|
+
member,
|
|
108
|
+
workflowId,
|
|
109
|
+
name,
|
|
110
|
+
hint = "",
|
|
111
|
+
groups,
|
|
112
|
+
}: {
|
|
113
|
+
member: Member;
|
|
114
|
+
workflowId: string;
|
|
115
|
+
name: string;
|
|
116
|
+
hint?: string;
|
|
117
|
+
groups: Array<{
|
|
118
|
+
name: string;
|
|
119
|
+
multi?: boolean;
|
|
120
|
+
hint?: string;
|
|
121
|
+
fields: Array<{
|
|
122
|
+
name: string;
|
|
123
|
+
type?:
|
|
124
|
+
| "string"
|
|
125
|
+
| "string-long"
|
|
126
|
+
| "number"
|
|
127
|
+
| "boolean"
|
|
128
|
+
| "date"
|
|
129
|
+
| "email"
|
|
130
|
+
| "money"
|
|
131
|
+
| "url"
|
|
132
|
+
| "enum";
|
|
133
|
+
hint?: string;
|
|
134
|
+
}>;
|
|
135
|
+
}>;
|
|
136
|
+
}) {
|
|
137
|
+
const { organizationId } = member;
|
|
138
|
+
|
|
139
|
+
return await db.transaction(async (tx) => {
|
|
140
|
+
// Create conversation for the form
|
|
141
|
+
const [conversation] = await tx
|
|
142
|
+
.insert(schema.conversation)
|
|
143
|
+
.values({ organizationId })
|
|
144
|
+
.returning();
|
|
145
|
+
invariant(conversation);
|
|
146
|
+
|
|
147
|
+
// Create the form
|
|
148
|
+
const [newForm] = await tx
|
|
149
|
+
.insert(schema.form)
|
|
150
|
+
.values({
|
|
151
|
+
workflowId,
|
|
152
|
+
organizationId,
|
|
153
|
+
conversationId: conversation.id,
|
|
154
|
+
name,
|
|
155
|
+
hint,
|
|
156
|
+
})
|
|
157
|
+
.returning();
|
|
158
|
+
invariant(newForm);
|
|
159
|
+
|
|
160
|
+
// Create groups and fields
|
|
161
|
+
const formGroups: InsertFormGroup[] = [];
|
|
162
|
+
const formFields: InsertFormField[] = [];
|
|
163
|
+
|
|
164
|
+
groups.forEach((group, groupIndex) => {
|
|
165
|
+
const groupId = nanoid();
|
|
166
|
+
formGroups.push({
|
|
167
|
+
id: groupId,
|
|
168
|
+
formId: newForm.id,
|
|
169
|
+
name: group.name,
|
|
170
|
+
multi: group.multi ?? false,
|
|
171
|
+
hint: group.hint ?? "",
|
|
172
|
+
order: groupIndex,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
group.fields.forEach((field, fieldIndex) => {
|
|
176
|
+
formFields.push({
|
|
177
|
+
formGroupId: groupId,
|
|
178
|
+
name: field.name,
|
|
179
|
+
type: field.type ?? "string",
|
|
180
|
+
hint: field.hint ?? "",
|
|
181
|
+
order: fieldIndex,
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Insert groups and fields
|
|
187
|
+
if (formGroups.length > 0) {
|
|
188
|
+
await tx.insert(schema.formGroup).values(formGroups);
|
|
189
|
+
}
|
|
190
|
+
if (formFields.length > 0) {
|
|
191
|
+
await tx.insert(schema.formField).values(formFields);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Return the complete form with groups and fields
|
|
195
|
+
const completeForm = await tx.query.form.findFirst({
|
|
196
|
+
where: eq(schema.form.id, newForm.id),
|
|
197
|
+
with: {
|
|
198
|
+
groups: {
|
|
199
|
+
with: {
|
|
200
|
+
fields: {
|
|
201
|
+
orderBy: asc(schema.formField.order),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
orderBy: asc(schema.formGroup.order),
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
invariant(completeForm, "Failed to retrieve created form");
|
|
210
|
+
return completeForm;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Updates a form's name and/or hint
|
|
216
|
+
* @param member - The member whose organization the form belongs to
|
|
217
|
+
* @param formId - The ID of the form to update
|
|
218
|
+
* @param name - Optional new name for the form
|
|
219
|
+
* @param hint - Optional new hint text for the form
|
|
220
|
+
* @returns The updated form
|
|
221
|
+
* @throws Error if form is not found
|
|
222
|
+
*/
|
|
223
|
+
export async function updateForm({
|
|
224
|
+
member,
|
|
225
|
+
formId,
|
|
226
|
+
name,
|
|
227
|
+
hint,
|
|
228
|
+
}: {
|
|
229
|
+
member: Member;
|
|
230
|
+
formId: string;
|
|
231
|
+
name?: string;
|
|
232
|
+
hint?: string;
|
|
233
|
+
}) {
|
|
234
|
+
// First verify the form exists and belongs to the organization
|
|
235
|
+
const existingForm = await db.query.form.findFirst({
|
|
236
|
+
where: and(
|
|
237
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
238
|
+
isNull(schema.form.deletedAt),
|
|
239
|
+
eq(schema.form.id, formId),
|
|
240
|
+
),
|
|
241
|
+
});
|
|
242
|
+
invariant(existingForm, `no form found for ${formId}`);
|
|
243
|
+
|
|
244
|
+
// Build update object with only provided fields
|
|
245
|
+
const updateData: { name?: string; hint?: string } = {};
|
|
246
|
+
if (name !== undefined) updateData.name = name;
|
|
247
|
+
if (hint !== undefined) updateData.hint = hint;
|
|
248
|
+
|
|
249
|
+
const [updatedForm] = await db
|
|
250
|
+
.update(schema.form)
|
|
251
|
+
.set(updateData)
|
|
252
|
+
.where(eq(schema.form.id, formId))
|
|
253
|
+
.returning();
|
|
254
|
+
|
|
255
|
+
return updatedForm;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Soft deletes a form by setting its deletedAt timestamp
|
|
260
|
+
* @param member - The member whose organization the form belongs to
|
|
261
|
+
* @param formId - The ID of the form to delete
|
|
262
|
+
*/
|
|
263
|
+
export async function deleteForm({ member, formId }: { member: Member; formId: string }) {
|
|
264
|
+
await db
|
|
265
|
+
.update(schema.form)
|
|
266
|
+
.set({ deletedAt: new Date() })
|
|
267
|
+
.where(and(eq(schema.form.organizationId, member.organizationId), eq(schema.form.id, formId)));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Moves a form to a different workflow
|
|
272
|
+
* @param member - The member whose organization the form and workflow belong to
|
|
273
|
+
* @param formId - The ID of the form to move
|
|
274
|
+
* @param workflowId - The ID of the target workflow
|
|
275
|
+
* @returns The updated form with its groups and fields
|
|
276
|
+
* @throws Error if form or workflow is not found
|
|
277
|
+
*/
|
|
278
|
+
export async function moveFormToWorkflow({
|
|
279
|
+
member,
|
|
280
|
+
formId,
|
|
281
|
+
workflowId,
|
|
282
|
+
}: {
|
|
283
|
+
member: Member;
|
|
284
|
+
formId: string;
|
|
285
|
+
workflowId: string;
|
|
286
|
+
}) {
|
|
287
|
+
const { organizationId } = member;
|
|
288
|
+
|
|
289
|
+
// First verify the form exists and belongs to the organization
|
|
290
|
+
const existingForm = await db.query.form.findFirst({
|
|
291
|
+
where: and(
|
|
292
|
+
eq(schema.form.organizationId, organizationId),
|
|
293
|
+
isNull(schema.form.deletedAt),
|
|
294
|
+
eq(schema.form.id, formId),
|
|
295
|
+
),
|
|
296
|
+
});
|
|
297
|
+
invariant(existingForm, `no form found for ${formId}`);
|
|
298
|
+
|
|
299
|
+
// Verify the target workflow exists and belongs to the organization
|
|
300
|
+
const targetWorkflow = await db.query.workflow.findFirst({
|
|
301
|
+
where: and(
|
|
302
|
+
eq(schema.workflow.organizationId, organizationId),
|
|
303
|
+
isNull(schema.workflow.deletedAt),
|
|
304
|
+
eq(schema.workflow.id, workflowId),
|
|
305
|
+
),
|
|
306
|
+
});
|
|
307
|
+
invariant(targetWorkflow, `no workflow found for ${workflowId}`);
|
|
308
|
+
|
|
309
|
+
// Update the form's workflow
|
|
310
|
+
await db.update(schema.form).set({ workflowId }).where(eq(schema.form.id, formId));
|
|
311
|
+
|
|
312
|
+
// Return the updated form
|
|
313
|
+
return await getForm({ member, formId });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Creates a copy of an existing form with all its groups, fields, and derived fields
|
|
318
|
+
* @param member - The member whose organization the form belongs to
|
|
319
|
+
* @param formId - The ID of the form to clone
|
|
320
|
+
* @param targetWorkflowId - Optional workflow ID to place the cloned form in (defaults to source form's workflow)
|
|
321
|
+
* @returns Object containing the newly created form
|
|
322
|
+
* @throws Error if form or target workflow is not found
|
|
323
|
+
*/
|
|
324
|
+
export async function cloneForm({
|
|
325
|
+
member,
|
|
326
|
+
formId,
|
|
327
|
+
targetWorkflowId,
|
|
328
|
+
}: {
|
|
329
|
+
member: Member;
|
|
330
|
+
formId: string;
|
|
331
|
+
targetWorkflowId?: string;
|
|
332
|
+
}) {
|
|
333
|
+
const existingForm = await db.query.form.findFirst({
|
|
334
|
+
where: and(
|
|
335
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
336
|
+
isNull(schema.form.deletedAt),
|
|
337
|
+
eq(schema.form.id, formId),
|
|
338
|
+
),
|
|
339
|
+
with: {
|
|
340
|
+
groups: {
|
|
341
|
+
with: { fields: true, derived: true },
|
|
342
|
+
orderBy: asc(schema.formGroup.order),
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
invariant(existingForm, `no existing form found for ${formId}`);
|
|
347
|
+
|
|
348
|
+
// If targetWorkflowId is provided, verify access to it
|
|
349
|
+
if (targetWorkflowId) {
|
|
350
|
+
const targetWorkflow = await db.query.workflow.findFirst({
|
|
351
|
+
where: and(
|
|
352
|
+
eq(schema.workflow.organizationId, member.organizationId),
|
|
353
|
+
isNull(schema.workflow.deletedAt),
|
|
354
|
+
eq(schema.workflow.id, targetWorkflowId),
|
|
355
|
+
),
|
|
356
|
+
});
|
|
357
|
+
invariant(targetWorkflow, `no workflow found for ${targetWorkflowId}`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const newForm = await db.transaction(async (tx) => {
|
|
361
|
+
const [conversation] = await tx
|
|
362
|
+
.insert(schema.conversation)
|
|
363
|
+
.values({ organizationId: member.organizationId })
|
|
364
|
+
.returning();
|
|
365
|
+
invariant(conversation);
|
|
366
|
+
const { id: conversationId } = conversation;
|
|
367
|
+
const [newForm] = await tx
|
|
368
|
+
.insert(schema.form)
|
|
369
|
+
.values({
|
|
370
|
+
...existingForm,
|
|
371
|
+
id: undefined,
|
|
372
|
+
workflowId: targetWorkflowId || existingForm.workflowId,
|
|
373
|
+
name: `${existingForm.name} (copy)`,
|
|
374
|
+
organizationId: member.organizationId,
|
|
375
|
+
conversationId,
|
|
376
|
+
})
|
|
377
|
+
.returning();
|
|
378
|
+
invariant(newForm);
|
|
379
|
+
|
|
380
|
+
const groups: InsertFormGroup[] = [];
|
|
381
|
+
const fields: InsertFormField[] = [];
|
|
382
|
+
const derived: InsertFormDerived[] = [];
|
|
383
|
+
|
|
384
|
+
existingForm.groups.forEach((group) => {
|
|
385
|
+
const formGroupId = nanoid();
|
|
386
|
+
groups.push({ ...group, id: formGroupId, formId: newForm.id });
|
|
387
|
+
|
|
388
|
+
group.fields.forEach((field) => {
|
|
389
|
+
fields.push({ ...field, id: undefined, formGroupId });
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Copy derived fields if any
|
|
393
|
+
group.derived?.forEach((derivedField) => {
|
|
394
|
+
derived.push({ ...derivedField, id: undefined, formGroupId });
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
if (groups.length > 0) {
|
|
398
|
+
await tx.insert(schema.formGroup).values(groups);
|
|
399
|
+
}
|
|
400
|
+
if (fields.length > 0) {
|
|
401
|
+
await tx.insert(schema.formField).values(fields);
|
|
402
|
+
}
|
|
403
|
+
if (derived.length > 0) {
|
|
404
|
+
await tx.insert(schema.formDerived).values(derived);
|
|
405
|
+
}
|
|
406
|
+
return newForm;
|
|
407
|
+
});
|
|
408
|
+
return { newForm };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Retrieves a specific form group within a form
|
|
413
|
+
* @param member - The member whose organization the form belongs to
|
|
414
|
+
* @param formId - The ID of the form containing the group
|
|
415
|
+
* @param formGroupId - The ID of the group to retrieve
|
|
416
|
+
* @returns The form group
|
|
417
|
+
* @throws Error if form or group is not found
|
|
418
|
+
*/
|
|
419
|
+
export async function getFormGroup({
|
|
420
|
+
member,
|
|
421
|
+
formId,
|
|
422
|
+
formGroupId,
|
|
423
|
+
}: {
|
|
424
|
+
member: Member;
|
|
425
|
+
formId: string;
|
|
426
|
+
formGroupId: string;
|
|
427
|
+
}) {
|
|
428
|
+
const form = await db.query.form.findFirst({
|
|
429
|
+
where: and(
|
|
430
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
431
|
+
isNull(schema.form.deletedAt),
|
|
432
|
+
eq(schema.form.id, formId),
|
|
433
|
+
),
|
|
434
|
+
with: {
|
|
435
|
+
groups: {
|
|
436
|
+
where: eq(schema.formGroup.id, formGroupId),
|
|
437
|
+
orderBy: asc(schema.formGroup.order),
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
invariant(form, `No form found for ${formId}`);
|
|
442
|
+
const [group] = form.groups;
|
|
443
|
+
invariant(group);
|
|
444
|
+
|
|
445
|
+
return group;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Creates a new group within a form
|
|
450
|
+
* @param member - The member whose organization the form belongs to
|
|
451
|
+
* @param formId - The ID of the form to add the group to
|
|
452
|
+
* @param name - The name of the group
|
|
453
|
+
* @param multi - Whether this group can have multiple instances (default: false)
|
|
454
|
+
* @param hint - Optional hint text for the group
|
|
455
|
+
* @param order - Optional position in the form (defaults to end)
|
|
456
|
+
* @returns The newly created group
|
|
457
|
+
* @throws Error if form is not found
|
|
458
|
+
*/
|
|
459
|
+
export async function createFormGroup({
|
|
460
|
+
member,
|
|
461
|
+
formId,
|
|
462
|
+
name,
|
|
463
|
+
multi = false,
|
|
464
|
+
hint = "",
|
|
465
|
+
order,
|
|
466
|
+
}: {
|
|
467
|
+
member: Member;
|
|
468
|
+
formId: string;
|
|
469
|
+
name: string;
|
|
470
|
+
multi?: boolean;
|
|
471
|
+
hint?: string;
|
|
472
|
+
order?: number;
|
|
473
|
+
}) {
|
|
474
|
+
const form = await db.query.form.findFirst({
|
|
475
|
+
where: and(
|
|
476
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
477
|
+
isNull(schema.form.deletedAt),
|
|
478
|
+
eq(schema.form.id, formId),
|
|
479
|
+
),
|
|
480
|
+
with: {
|
|
481
|
+
groups: {
|
|
482
|
+
orderBy: asc(schema.formGroup.order),
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
invariant(form, `no form found for ${formId}`);
|
|
487
|
+
|
|
488
|
+
const group = await db.transaction(async (tx) => {
|
|
489
|
+
// If order not provided, append to the end
|
|
490
|
+
const maxOrder = form.groups.length ? Math.max(...form.groups.map((g) => g.order)) + 1 : 0;
|
|
491
|
+
const groupOrder = order !== undefined ? order : maxOrder;
|
|
492
|
+
|
|
493
|
+
// If order provided, make sure to shift all subsequent groups forward
|
|
494
|
+
if (order !== undefined) {
|
|
495
|
+
await tx
|
|
496
|
+
.update(schema.formGroup)
|
|
497
|
+
.set({ order: sql`${schema.formGroup.order} + 1` })
|
|
498
|
+
.where(and(eq(schema.formGroup.formId, formId), gte(schema.formGroup.order, order)));
|
|
499
|
+
}
|
|
500
|
+
const [group] = await tx
|
|
501
|
+
.insert(schema.formGroup)
|
|
502
|
+
.values({
|
|
503
|
+
formId,
|
|
504
|
+
name,
|
|
505
|
+
multi,
|
|
506
|
+
hint,
|
|
507
|
+
order: groupOrder,
|
|
508
|
+
})
|
|
509
|
+
.returning();
|
|
510
|
+
return group;
|
|
511
|
+
});
|
|
512
|
+
invariant(group);
|
|
513
|
+
|
|
514
|
+
return group;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Updates a form group's properties
|
|
519
|
+
* @param member - The member whose organization the form belongs to
|
|
520
|
+
* @param formId - The ID of the form containing the group
|
|
521
|
+
* @param groupId - The ID of the group to update
|
|
522
|
+
* @param name - Optional new name for the group
|
|
523
|
+
* @param multi - Optional new multi value
|
|
524
|
+
* @param hint - Optional new hint text
|
|
525
|
+
* @param order - Optional new position (will shift other groups if needed)
|
|
526
|
+
* @returns The updated group
|
|
527
|
+
* @throws Error if group is not found or member lacks access
|
|
528
|
+
*/
|
|
529
|
+
export async function updateFormGroup({
|
|
530
|
+
member,
|
|
531
|
+
formGroupId,
|
|
532
|
+
formId,
|
|
533
|
+
name,
|
|
534
|
+
type,
|
|
535
|
+
multi,
|
|
536
|
+
hint,
|
|
537
|
+
order,
|
|
538
|
+
}: {
|
|
539
|
+
member: Member;
|
|
540
|
+
formId: string;
|
|
541
|
+
formGroupId: string;
|
|
542
|
+
name?: string;
|
|
543
|
+
type?: "custom" | "bank_statement";
|
|
544
|
+
multi?: boolean;
|
|
545
|
+
hint?: string;
|
|
546
|
+
order?: number;
|
|
547
|
+
}) {
|
|
548
|
+
// Validate access
|
|
549
|
+
const group = await getFormGroup({ member, formId, formGroupId });
|
|
550
|
+
|
|
551
|
+
const updateData: Partial<InsertFormGroup> = {};
|
|
552
|
+
if (name !== undefined) updateData.name = name;
|
|
553
|
+
if (type !== undefined) updateData.type = type;
|
|
554
|
+
if (multi !== undefined) updateData.multi = multi;
|
|
555
|
+
if (hint !== undefined) updateData.hint = hint;
|
|
556
|
+
if (order !== undefined) updateData.order = order;
|
|
557
|
+
|
|
558
|
+
if (Object.keys(updateData).length === 0) return group;
|
|
559
|
+
return await db.transaction(async (tx) => {
|
|
560
|
+
if (order !== undefined) {
|
|
561
|
+
await tx
|
|
562
|
+
.update(schema.formGroup)
|
|
563
|
+
.set({ order: sql`${schema.formGroup.order} + 1` })
|
|
564
|
+
.where(and(eq(schema.formGroup.formId, formId), gte(schema.formGroup.order, order)));
|
|
565
|
+
}
|
|
566
|
+
const [updatedGroup] = await tx
|
|
567
|
+
.update(schema.formGroup)
|
|
568
|
+
.set(updateData)
|
|
569
|
+
.where(eq(schema.formGroup.id, group.id))
|
|
570
|
+
.returning();
|
|
571
|
+
invariant(updatedGroup);
|
|
572
|
+
return updatedGroup;
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Deletes a form group and all its fields (via cascade)
|
|
578
|
+
* @param member - The member whose organization the form belongs to
|
|
579
|
+
* @param formId - The ID of the form containing the group
|
|
580
|
+
* @param groupId - The ID of the group to delete
|
|
581
|
+
* @throws Error if form or group is not found
|
|
582
|
+
*/
|
|
583
|
+
export async function deleteFormGroup({
|
|
584
|
+
member,
|
|
585
|
+
formId,
|
|
586
|
+
formGroupId,
|
|
587
|
+
}: {
|
|
588
|
+
member: Member;
|
|
589
|
+
formId: string;
|
|
590
|
+
formGroupId: string;
|
|
591
|
+
}) {
|
|
592
|
+
// Validate access
|
|
593
|
+
const group = await getFormGroup({ member, formId, formGroupId });
|
|
594
|
+
await db.delete(schema.formGroup).where(eq(schema.formGroup.id, group.id));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Retrieves a specific form field and validates access
|
|
599
|
+
* @param member - The member whose organization the form belongs to
|
|
600
|
+
* @param formId - The ID of the form containing the field
|
|
601
|
+
* @param formFieldId - The ID of the field to retrieve
|
|
602
|
+
* @returns The form field
|
|
603
|
+
* @throws InvariantError if field is not found or doesn't belong to the specified form
|
|
604
|
+
*/
|
|
605
|
+
export async function getFormField({
|
|
606
|
+
member,
|
|
607
|
+
formId,
|
|
608
|
+
formFieldId,
|
|
609
|
+
}: {
|
|
610
|
+
member: Member;
|
|
611
|
+
formId: string;
|
|
612
|
+
formFieldId: string;
|
|
613
|
+
}) {
|
|
614
|
+
const fieldWithGroup = await db.query.formField.findFirst({
|
|
615
|
+
with: {
|
|
616
|
+
group: {
|
|
617
|
+
with: { form: true },
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
where: eq(schema.formField.id, formFieldId),
|
|
621
|
+
});
|
|
622
|
+
invariant(fieldWithGroup, `no formField found for ${formFieldId}`);
|
|
623
|
+
|
|
624
|
+
const { group, ...field } = fieldWithGroup;
|
|
625
|
+
if (group.form.organizationId !== member.organizationId || group.form.id !== formId) {
|
|
626
|
+
throw new InvariantError(`no formField found for ${formFieldId} in form ${formId}`);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return field;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Creates a new field within a form group
|
|
634
|
+
* @param member - The member whose organization the form belongs to
|
|
635
|
+
* @param formId - The ID of the form containing the group
|
|
636
|
+
* @param groupId - The ID of the group to add the field to
|
|
637
|
+
* @param name - The name of the field
|
|
638
|
+
* @param type - The field type (default: "string")
|
|
639
|
+
* @param hint - Optional hint text for the field
|
|
640
|
+
* @param order - Optional position in the group (defaults to end)
|
|
641
|
+
* @returns The newly created field
|
|
642
|
+
* @throws Error if form or group is not found
|
|
643
|
+
*/
|
|
644
|
+
export async function createFormField({
|
|
645
|
+
member,
|
|
646
|
+
formId,
|
|
647
|
+
groupId,
|
|
648
|
+
name,
|
|
649
|
+
type = "string",
|
|
650
|
+
hint = "",
|
|
651
|
+
order,
|
|
652
|
+
}: {
|
|
653
|
+
member: Member;
|
|
654
|
+
formId: string;
|
|
655
|
+
groupId: string;
|
|
656
|
+
name: string;
|
|
657
|
+
type?:
|
|
658
|
+
| "string"
|
|
659
|
+
| "string-long"
|
|
660
|
+
| "number"
|
|
661
|
+
| "boolean"
|
|
662
|
+
| "date"
|
|
663
|
+
| "email"
|
|
664
|
+
| "money"
|
|
665
|
+
| "url"
|
|
666
|
+
| "enum";
|
|
667
|
+
hint?: string;
|
|
668
|
+
order?: number;
|
|
669
|
+
}) {
|
|
670
|
+
// Verify the form and group belong to the organization
|
|
671
|
+
const form = await db.query.form.findFirst({
|
|
672
|
+
where: and(
|
|
673
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
674
|
+
isNull(schema.form.deletedAt),
|
|
675
|
+
eq(schema.form.id, formId),
|
|
676
|
+
),
|
|
677
|
+
with: {
|
|
678
|
+
groups: {
|
|
679
|
+
where: eq(schema.formGroup.id, groupId),
|
|
680
|
+
with: {
|
|
681
|
+
fields: {
|
|
682
|
+
orderBy: asc(schema.formField.order),
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
invariant(form, `no form found for ${formId}`);
|
|
689
|
+
const [group] = form.groups;
|
|
690
|
+
invariant(group, `group ${groupId} not found in form ${formId}`);
|
|
691
|
+
|
|
692
|
+
const maxOrder = group.fields.length ? Math.max(...group.fields.map((f) => f.order)) + 1 : 0;
|
|
693
|
+
const fieldOrder = order !== undefined ? order : maxOrder;
|
|
694
|
+
const [field] = await db
|
|
695
|
+
.insert(schema.formField)
|
|
696
|
+
.values({
|
|
697
|
+
formGroupId: groupId,
|
|
698
|
+
name,
|
|
699
|
+
type,
|
|
700
|
+
hint,
|
|
701
|
+
order: fieldOrder,
|
|
702
|
+
})
|
|
703
|
+
.returning();
|
|
704
|
+
invariant(field);
|
|
705
|
+
|
|
706
|
+
return field;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Updates a form field's properties
|
|
711
|
+
* @param member - The member whose organization the form belongs to
|
|
712
|
+
* @param formId - The ID of the form to which the field belongs to
|
|
713
|
+
* @param formFieldId - The ID of the field to update
|
|
714
|
+
* @param name - Optional new name for the field
|
|
715
|
+
* @param hint - Optional new hint text
|
|
716
|
+
* @param type - Optional new field type
|
|
717
|
+
* @param order - Optional new position (will shift other fields if needed)
|
|
718
|
+
* @returns The updated field
|
|
719
|
+
* @throws Error if field is not found or member lacks access
|
|
720
|
+
*/
|
|
721
|
+
export async function updateFormField({
|
|
722
|
+
member,
|
|
723
|
+
formId,
|
|
724
|
+
formFieldId,
|
|
725
|
+
name,
|
|
726
|
+
hint,
|
|
727
|
+
type,
|
|
728
|
+
order,
|
|
729
|
+
}: {
|
|
730
|
+
member: Member;
|
|
731
|
+
formId: string;
|
|
732
|
+
formFieldId: string;
|
|
733
|
+
name?: string;
|
|
734
|
+
hint?: string;
|
|
735
|
+
type?:
|
|
736
|
+
| "string"
|
|
737
|
+
| "string-long"
|
|
738
|
+
| "number"
|
|
739
|
+
| "boolean"
|
|
740
|
+
| "date"
|
|
741
|
+
| "email"
|
|
742
|
+
| "money"
|
|
743
|
+
| "url"
|
|
744
|
+
| "enum";
|
|
745
|
+
order?: number;
|
|
746
|
+
}) {
|
|
747
|
+
const field = await getFormField({ member, formId, formFieldId });
|
|
748
|
+
const updateData: Partial<InsertFormField> = {};
|
|
749
|
+
if (name !== undefined) updateData.name = name;
|
|
750
|
+
if (hint !== undefined) updateData.hint = hint;
|
|
751
|
+
if (type !== undefined) updateData.type = type;
|
|
752
|
+
if (order !== undefined) updateData.order = order;
|
|
753
|
+
if (Object.keys(updateData).length === 0) return field;
|
|
754
|
+
|
|
755
|
+
return await db.transaction(async (tx) => {
|
|
756
|
+
if (order !== undefined) {
|
|
757
|
+
await tx
|
|
758
|
+
.update(schema.formField)
|
|
759
|
+
.set({ order: sql`${schema.formField.order} + 1` })
|
|
760
|
+
.where(
|
|
761
|
+
and(
|
|
762
|
+
eq(schema.formField.formGroupId, field.formGroupId),
|
|
763
|
+
gte(schema.formField.order, order),
|
|
764
|
+
),
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
const [formField] = await tx
|
|
768
|
+
.update(schema.formField)
|
|
769
|
+
.set(updateData)
|
|
770
|
+
.where(eq(schema.formField.id, formFieldId))
|
|
771
|
+
.returning();
|
|
772
|
+
invariant(formField);
|
|
773
|
+
return formField;
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Updates only a form field's hint text
|
|
779
|
+
* @param member - The member whose organization the form belongs to
|
|
780
|
+
* @param formFieldId - The ID of the field to update
|
|
781
|
+
* @param hint - The new hint text
|
|
782
|
+
* @throws Error if field is not found or member lacks access
|
|
783
|
+
*/
|
|
784
|
+
export async function updateFormFieldHint({
|
|
785
|
+
member,
|
|
786
|
+
formFieldId,
|
|
787
|
+
hint,
|
|
788
|
+
}: {
|
|
789
|
+
member: Member;
|
|
790
|
+
formFieldId: string;
|
|
791
|
+
hint: string;
|
|
792
|
+
}) {
|
|
793
|
+
const field = await db.query.formField.findFirst({
|
|
794
|
+
where: eq(schema.formField.id, formFieldId),
|
|
795
|
+
with: { group: { with: { form: true } } },
|
|
796
|
+
});
|
|
797
|
+
if (field?.group.form.organizationId !== member.organizationId) {
|
|
798
|
+
throw new InvariantError(`formField not found for ${formFieldId}`);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Get the field to validate access
|
|
802
|
+
await db.update(schema.formField).set({ hint }).where(eq(schema.formField.id, field.id));
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Deletes a form field
|
|
807
|
+
* @param member - The member whose organization the form belongs to
|
|
808
|
+
* @param formId - The ID of the form containing the field
|
|
809
|
+
* @param fieldId - The ID of the field to delete
|
|
810
|
+
* @throws Error if form or field is not found
|
|
811
|
+
*/
|
|
812
|
+
export async function deleteFormField({
|
|
813
|
+
member,
|
|
814
|
+
formId,
|
|
815
|
+
formFieldId,
|
|
816
|
+
}: {
|
|
817
|
+
member: Member;
|
|
818
|
+
formId: string;
|
|
819
|
+
formFieldId: string;
|
|
820
|
+
}) {
|
|
821
|
+
// Get the field to validate access
|
|
822
|
+
const field = await getFormField({ member, formId, formFieldId });
|
|
823
|
+
await db.delete(schema.formField).where(eq(schema.formField.id, field.id));
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Creates a derived field within a form group (computed/AI-generated field)
|
|
828
|
+
* @param member - The member whose organization the form belongs to
|
|
829
|
+
* @param formId - The ID of the form containing the group
|
|
830
|
+
* @param groupId - The ID of the group to add the derived field to
|
|
831
|
+
* @param name - The name of the derived field
|
|
832
|
+
* @param prompt - Optional prompt text for generating the derived value
|
|
833
|
+
* @returns The newly created derived field
|
|
834
|
+
* @throws Error if form or group is not found
|
|
835
|
+
*/
|
|
836
|
+
export async function createFormDerived({
|
|
837
|
+
member,
|
|
838
|
+
formId,
|
|
839
|
+
groupId,
|
|
840
|
+
name,
|
|
841
|
+
prompt = "",
|
|
842
|
+
}: {
|
|
843
|
+
member: Member;
|
|
844
|
+
formId: string;
|
|
845
|
+
groupId: string;
|
|
846
|
+
name: string;
|
|
847
|
+
prompt?: string;
|
|
848
|
+
}) {
|
|
849
|
+
// Verify the form and group belong to the organization
|
|
850
|
+
const form = await db.query.form.findFirst({
|
|
851
|
+
where: and(
|
|
852
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
853
|
+
isNull(schema.form.deletedAt),
|
|
854
|
+
eq(schema.form.id, formId),
|
|
855
|
+
),
|
|
856
|
+
with: {
|
|
857
|
+
groups: {
|
|
858
|
+
where: eq(schema.formGroup.id, groupId),
|
|
859
|
+
},
|
|
860
|
+
},
|
|
861
|
+
});
|
|
862
|
+
invariant(form, `no form found for ${formId}`);
|
|
863
|
+
invariant(form.groups.length > 0, `group ${groupId} not found in form ${formId}`);
|
|
864
|
+
|
|
865
|
+
const [derived] = await db
|
|
866
|
+
.insert(schema.formDerived)
|
|
867
|
+
.values({
|
|
868
|
+
formGroupId: groupId,
|
|
869
|
+
name,
|
|
870
|
+
prompt,
|
|
871
|
+
})
|
|
872
|
+
.returning();
|
|
873
|
+
invariant(derived);
|
|
874
|
+
|
|
875
|
+
return derived;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Updates a derived field's properties
|
|
880
|
+
* @param member - The member whose organization the form belongs to
|
|
881
|
+
* @param derivedId - The ID of the derived field to update
|
|
882
|
+
* @param name - Optional new name for the derived field
|
|
883
|
+
* @param prompt - Optional new prompt text
|
|
884
|
+
* @returns The updated derived field
|
|
885
|
+
* @throws Error if derived field is not found or member lacks access
|
|
886
|
+
*/
|
|
887
|
+
export async function updateFormDerived({
|
|
888
|
+
member,
|
|
889
|
+
formId,
|
|
890
|
+
derivedId,
|
|
891
|
+
name,
|
|
892
|
+
prompt,
|
|
893
|
+
}: {
|
|
894
|
+
member: Member;
|
|
895
|
+
formId: string;
|
|
896
|
+
derivedId: string;
|
|
897
|
+
name?: string;
|
|
898
|
+
prompt?: string;
|
|
899
|
+
}) {
|
|
900
|
+
// Verify the form and derived field belong to the organization
|
|
901
|
+
const formDerived = await db.query.formDerived.findFirst({
|
|
902
|
+
with: {
|
|
903
|
+
group: {
|
|
904
|
+
with: {
|
|
905
|
+
form: true,
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
where: eq(schema.formDerived.id, derivedId),
|
|
910
|
+
});
|
|
911
|
+
invariant(formDerived, `no formDerived found for ${derivedId}`);
|
|
912
|
+
const { group, ...currentDerived } = formDerived;
|
|
913
|
+
|
|
914
|
+
if (group.form.organizationId !== member.organizationId) {
|
|
915
|
+
throw new InvariantError(`no form found for ${formId}`);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const updateData: Partial<InsertFormDerived> = {};
|
|
919
|
+
if (name !== undefined) updateData.name = name;
|
|
920
|
+
if (prompt !== undefined) updateData.prompt = prompt;
|
|
921
|
+
|
|
922
|
+
if (Object.keys(updateData).length === 0) return currentDerived;
|
|
923
|
+
const [updatedFormDerived] = await db
|
|
924
|
+
.update(schema.formDerived)
|
|
925
|
+
.set(updateData)
|
|
926
|
+
.where(eq(schema.formDerived.id, derivedId))
|
|
927
|
+
.returning();
|
|
928
|
+
|
|
929
|
+
return updatedFormDerived;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Deletes a derived field
|
|
934
|
+
* @param member - The member whose organization the form belongs to
|
|
935
|
+
* @param formId - The ID of the form containing the derived field
|
|
936
|
+
* @param derivedId - The ID of the derived field to delete
|
|
937
|
+
* @throws Error if form or derived field is not found
|
|
938
|
+
*/
|
|
939
|
+
export async function deleteFormDerived({
|
|
940
|
+
member,
|
|
941
|
+
formId,
|
|
942
|
+
derivedId,
|
|
943
|
+
}: {
|
|
944
|
+
member: Member;
|
|
945
|
+
formId: string;
|
|
946
|
+
derivedId: string;
|
|
947
|
+
}) {
|
|
948
|
+
// Verify the form and derived field belong to the organization
|
|
949
|
+
const form = await db.query.form.findFirst({
|
|
950
|
+
where: and(
|
|
951
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
952
|
+
isNull(schema.form.deletedAt),
|
|
953
|
+
eq(schema.form.id, formId),
|
|
954
|
+
),
|
|
955
|
+
with: {
|
|
956
|
+
groups: {
|
|
957
|
+
with: {
|
|
958
|
+
derived: {
|
|
959
|
+
where: eq(schema.formDerived.id, derivedId),
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
});
|
|
965
|
+
invariant(form, `no form found for ${formId}`);
|
|
966
|
+
|
|
967
|
+
const derivedBelongsToForm = form.groups.some((group) =>
|
|
968
|
+
group.derived.some((d) => d.id === derivedId),
|
|
969
|
+
);
|
|
970
|
+
invariant(derivedBelongsToForm, `derived field ${derivedId} not found in form ${formId}`);
|
|
971
|
+
|
|
972
|
+
await db.delete(schema.formDerived).where(eq(schema.formDerived.id, derivedId));
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Creates or updates a merged form for a template based on its workflow
|
|
977
|
+
* Combines all groups from all forms in the workflow into a single form
|
|
978
|
+
* @param member - The member whose organization the template belongs to
|
|
979
|
+
* @param templateId - The ID of the template
|
|
980
|
+
* @param workflowId - The ID of the workflow to merge forms from
|
|
981
|
+
* @param existingMergedFormId - Optional ID of existing merged form to update
|
|
982
|
+
* @returns The merged form ID and a hash of the workflow's forms for staleness checking
|
|
983
|
+
*/
|
|
984
|
+
export async function getOrCreateMergedFormForTemplate({
|
|
985
|
+
member,
|
|
986
|
+
templateId,
|
|
987
|
+
workflowId,
|
|
988
|
+
existingMergedFormId,
|
|
989
|
+
}: {
|
|
990
|
+
member: Member;
|
|
991
|
+
templateId: string;
|
|
992
|
+
workflowId: string;
|
|
993
|
+
existingMergedFormId?: string | null;
|
|
994
|
+
}): Promise<{ formId: string; formHash: string }> {
|
|
995
|
+
// Get all forms in the workflow with their groups and fields
|
|
996
|
+
const workflowForms = await db.query.form.findMany({
|
|
997
|
+
where: and(
|
|
998
|
+
eq(schema.form.workflowId, workflowId),
|
|
999
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
1000
|
+
isNull(schema.form.deletedAt),
|
|
1001
|
+
),
|
|
1002
|
+
with: {
|
|
1003
|
+
groups: {
|
|
1004
|
+
with: {
|
|
1005
|
+
fields: {
|
|
1006
|
+
orderBy: asc(schema.formField.order),
|
|
1007
|
+
},
|
|
1008
|
+
derived: true,
|
|
1009
|
+
},
|
|
1010
|
+
orderBy: asc(schema.formGroup.order),
|
|
1011
|
+
},
|
|
1012
|
+
},
|
|
1013
|
+
orderBy: asc(schema.form.createdAt),
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
if (workflowForms.length === 0) {
|
|
1017
|
+
throw new InvariantError(`No forms found in workflow ${workflowId}`);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Create a hash of form IDs and their updated timestamps to detect changes
|
|
1021
|
+
const formHash = workflowForms.map((f) => `${f.id}:${f.updatedAt.getTime()}`).join("|");
|
|
1022
|
+
|
|
1023
|
+
// Check if existing merged form is still valid
|
|
1024
|
+
if (existingMergedFormId) {
|
|
1025
|
+
const existingForm = await db.query.form.findFirst({
|
|
1026
|
+
where: and(
|
|
1027
|
+
eq(schema.form.id, existingMergedFormId),
|
|
1028
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
1029
|
+
isNull(schema.form.deletedAt),
|
|
1030
|
+
),
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
// If form exists with the same name pattern, check if it's still valid
|
|
1034
|
+
// We store the hash in the form name for now since metadata field doesn't exist
|
|
1035
|
+
if (existingForm && existingForm.name.includes(formHash.substring(0, 10))) {
|
|
1036
|
+
return { formId: existingMergedFormId, formHash };
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// Need to create new merged form
|
|
1041
|
+
return await db.transaction(async (tx) => {
|
|
1042
|
+
// Create conversation for the merged form
|
|
1043
|
+
const [conversation] = await tx
|
|
1044
|
+
.insert(schema.conversation)
|
|
1045
|
+
.values({ organizationId: member.organizationId })
|
|
1046
|
+
.returning();
|
|
1047
|
+
invariant(conversation);
|
|
1048
|
+
|
|
1049
|
+
// Get workflow name for form name
|
|
1050
|
+
const workflow = await tx.query.workflow.findFirst({
|
|
1051
|
+
where: eq(schema.workflow.id, workflowId),
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// Create the merged form
|
|
1055
|
+
const [mergedForm] = await tx
|
|
1056
|
+
.insert(schema.form)
|
|
1057
|
+
.values({
|
|
1058
|
+
organizationId: member.organizationId,
|
|
1059
|
+
workflowId,
|
|
1060
|
+
conversationId: conversation.id,
|
|
1061
|
+
name: `Merged: ${workflow?.name || workflowId} [${formHash.substring(0, 10)}]`,
|
|
1062
|
+
hint: `Auto-generated merged form for template ${templateId}`,
|
|
1063
|
+
})
|
|
1064
|
+
.returning();
|
|
1065
|
+
invariant(mergedForm);
|
|
1066
|
+
|
|
1067
|
+
// Merge all groups from all forms
|
|
1068
|
+
let groupOrder = 0;
|
|
1069
|
+
for (const sourceForm of workflowForms) {
|
|
1070
|
+
for (const sourceGroup of sourceForm.groups) {
|
|
1071
|
+
// Insert group with new IDs
|
|
1072
|
+
const [newGroup] = await tx
|
|
1073
|
+
.insert(schema.formGroup)
|
|
1074
|
+
.values({
|
|
1075
|
+
formId: mergedForm.id,
|
|
1076
|
+
name: `${sourceForm.name} - ${sourceGroup.name}`,
|
|
1077
|
+
order: groupOrder++,
|
|
1078
|
+
type: sourceGroup.type,
|
|
1079
|
+
multi: sourceGroup.multi,
|
|
1080
|
+
hint: sourceGroup.hint,
|
|
1081
|
+
})
|
|
1082
|
+
.returning();
|
|
1083
|
+
invariant(newGroup);
|
|
1084
|
+
|
|
1085
|
+
// Copy all fields
|
|
1086
|
+
for (const sourceField of sourceGroup.fields) {
|
|
1087
|
+
await tx.insert(schema.formField).values({
|
|
1088
|
+
formGroupId: newGroup.id,
|
|
1089
|
+
name: sourceField.name,
|
|
1090
|
+
type: sourceField.type,
|
|
1091
|
+
order: sourceField.order,
|
|
1092
|
+
hint: sourceField.hint,
|
|
1093
|
+
options: sourceField.options,
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// Copy all derived fields
|
|
1098
|
+
for (const sourceDerived of sourceGroup.derived) {
|
|
1099
|
+
await tx.insert(schema.formDerived).values({
|
|
1100
|
+
formGroupId: newGroup.id,
|
|
1101
|
+
name: sourceDerived.name,
|
|
1102
|
+
prompt: sourceDerived.prompt,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
return { formId: mergedForm.id, formHash };
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Get all forms for an organization with pagination
|
|
1114
|
+
*/
|
|
1115
|
+
export async function getFormsForOrganization({
|
|
1116
|
+
member,
|
|
1117
|
+
limit = 100,
|
|
1118
|
+
}: {
|
|
1119
|
+
member: Member;
|
|
1120
|
+
limit?: number;
|
|
1121
|
+
}) {
|
|
1122
|
+
const forms = await db.query.form.findMany({
|
|
1123
|
+
where: and(
|
|
1124
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
1125
|
+
isNull(schema.form.deletedAt),
|
|
1126
|
+
),
|
|
1127
|
+
columns: {
|
|
1128
|
+
id: true,
|
|
1129
|
+
name: true,
|
|
1130
|
+
updatedAt: true,
|
|
1131
|
+
},
|
|
1132
|
+
orderBy: [desc(schema.form.updatedAt)],
|
|
1133
|
+
limit,
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
return { ok: true, data: forms };
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Get multiple forms by their IDs
|
|
1141
|
+
*/
|
|
1142
|
+
export async function getFormsByIds({ member, formIds }: { member: Member; formIds: string[] }) {
|
|
1143
|
+
if (formIds.length === 0) {
|
|
1144
|
+
return { ok: true, data: [] };
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const forms = await db.query.form.findMany({
|
|
1148
|
+
where: and(
|
|
1149
|
+
eq(schema.form.organizationId, member.organizationId),
|
|
1150
|
+
inArray(schema.form.id, formIds),
|
|
1151
|
+
isNull(schema.form.deletedAt),
|
|
1152
|
+
),
|
|
1153
|
+
columns: {
|
|
1154
|
+
id: true,
|
|
1155
|
+
name: true,
|
|
1156
|
+
organizationId: true,
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
if (forms.length !== formIds.length) {
|
|
1161
|
+
return { ok: false, error: "not-found" };
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return { ok: true, data: forms };
|
|
1165
|
+
}
|