elevenlabs-webhook-nodejs 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/settings.local.json +27 -0
- package/.dockerignore +6 -0
- package/.env.example +43 -0
- package/BEFORE-PRODUCTION.md +32 -0
- package/Dockerfile +23 -0
- package/SYSTEM_OVERVIEW.md +942 -0
- package/SYSTEM_STATUS.md +332 -0
- package/backup_script/main.py +146 -0
- package/baileys/.dockerignore +7 -0
- package/baileys/Dockerfile +13 -0
- package/baileys/README.md +412 -0
- package/baileys/index.js +499 -0
- package/baileys/package-lock.json +2532 -0
- package/baileys/package.json +25 -0
- package/baileys/server.js +96 -0
- package/baileys/src/config.js +55 -0
- package/baileys/src/middleware/api-key.js +16 -0
- package/baileys/src/routes/accounts.js +51 -0
- package/baileys/src/routes/chats.js +103 -0
- package/baileys/src/routes/webhooks.js +34 -0
- package/baileys/src/services/account-store.js +259 -0
- package/baileys/src/services/webhook-dispatcher.js +161 -0
- package/baileys/src/services/worker-manager.js +597 -0
- package/baileys/src/utils/jid.js +79 -0
- package/baileys/src/utils/logger.js +16 -0
- package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
- package/baileys/worker.js +721 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +84 -0
- package/dist/app.js.map +1 -0
- package/dist/config/agentPrompts.json +19 -0
- package/dist/config/database.d.ts +1 -0
- package/dist/config/database.js +26 -0
- package/dist/config/database.js.map +1 -0
- package/dist/config/env.d.ts +41 -0
- package/dist/config/env.js +81 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +73 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controllers/webhook.controller.d.ts +10 -0
- package/dist/controllers/webhook.controller.js +128 -0
- package/dist/controllers/webhook.controller.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/hooks/webhookValidator.d.ts +2 -0
- package/dist/hooks/webhookValidator.js +47 -0
- package/dist/hooks/webhookValidator.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/001_session-architecture.d.ts +3 -0
- package/dist/migrations/001_session-architecture.js +119 -0
- package/dist/migrations/001_session-architecture.js.map +1 -0
- package/dist/migrations/002_agent-ref.d.ts +3 -0
- package/dist/migrations/002_agent-ref.js +55 -0
- package/dist/migrations/002_agent-ref.js.map +1 -0
- package/dist/migrations/003_shift-schedule.d.ts +3 -0
- package/dist/migrations/003_shift-schedule.js +48 -0
- package/dist/migrations/003_shift-schedule.js.map +1 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
- package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
- package/dist/migrations/migration.model.d.ts +18 -0
- package/dist/migrations/migration.model.js +45 -0
- package/dist/migrations/migration.model.js.map +1 -0
- package/dist/migrations/migration.routes.d.ts +2 -0
- package/dist/migrations/migration.routes.js +37 -0
- package/dist/migrations/migration.routes.js.map +1 -0
- package/dist/migrations/migration.runner.d.ts +19 -0
- package/dist/migrations/migration.runner.js +74 -0
- package/dist/migrations/migration.runner.js.map +1 -0
- package/dist/models/ConversationClaim.d.ts +13 -0
- package/dist/models/ConversationClaim.js +44 -0
- package/dist/models/ConversationClaim.js.map +1 -0
- package/dist/models/ConversationEvaluation.d.ts +23 -0
- package/dist/models/ConversationEvaluation.js +92 -0
- package/dist/models/ConversationEvaluation.js.map +1 -0
- package/dist/models/Summary.d.ts +37 -0
- package/dist/models/Summary.js +78 -0
- package/dist/models/Summary.js.map +1 -0
- package/dist/models/Transcription.d.ts +34 -0
- package/dist/models/Transcription.js +71 -0
- package/dist/models/Transcription.js.map +1 -0
- package/dist/models/WhatsAppRecipient.d.ts +23 -0
- package/dist/models/WhatsAppRecipient.js +53 -0
- package/dist/models/WhatsAppRecipient.js.map +1 -0
- package/dist/modules/admin/admin.routes.d.ts +2 -0
- package/dist/modules/admin/admin.routes.js +1966 -0
- package/dist/modules/admin/admin.routes.js.map +1 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
- package/dist/modules/agents/agent.model.d.ts +38 -0
- package/dist/modules/agents/agent.model.js +92 -0
- package/dist/modules/agents/agent.model.js.map +1 -0
- package/dist/modules/agents/agent.routes.d.ts +2 -0
- package/dist/modules/agents/agent.routes.js +61 -0
- package/dist/modules/agents/agent.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
- package/dist/modules/auth/auth.model.d.ts +17 -0
- package/dist/modules/auth/auth.model.js +64 -0
- package/dist/modules/auth/auth.model.js.map +1 -0
- package/dist/modules/auth/auth.routes.d.ts +2 -0
- package/dist/modules/auth/auth.routes.js +202 -0
- package/dist/modules/auth/auth.routes.js.map +1 -0
- package/dist/modules/auth/auth.service.d.ts +28 -0
- package/dist/modules/auth/auth.service.js +183 -0
- package/dist/modules/auth/auth.service.js.map +1 -0
- package/dist/modules/auth/refresh-token.model.d.ts +13 -0
- package/dist/modules/auth/refresh-token.model.js +52 -0
- package/dist/modules/auth/refresh-token.model.js.map +1 -0
- package/dist/modules/billing/billing-alert.model.d.ts +16 -0
- package/dist/modules/billing/billing-alert.model.js +47 -0
- package/dist/modules/billing/billing-alert.model.js.map +1 -0
- package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
- package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
- package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
- package/dist/modules/billing/billing.model.d.ts +18 -0
- package/dist/modules/billing/billing.model.js +62 -0
- package/dist/modules/billing/billing.model.js.map +1 -0
- package/dist/modules/billing/billing.routes.d.ts +2 -0
- package/dist/modules/billing/billing.routes.js +63 -0
- package/dist/modules/billing/billing.routes.js.map +1 -0
- package/dist/modules/billing/billing.service.d.ts +69 -0
- package/dist/modules/billing/billing.service.js +498 -0
- package/dist/modules/billing/billing.service.js.map +1 -0
- package/dist/modules/billing/payment.model.d.ts +24 -0
- package/dist/modules/billing/payment.model.js +57 -0
- package/dist/modules/billing/payment.model.js.map +1 -0
- package/dist/modules/calls/call.model.d.ts +41 -0
- package/dist/modules/calls/call.model.js +97 -0
- package/dist/modules/calls/call.model.js.map +1 -0
- package/dist/modules/calls/call.routes.d.ts +2 -0
- package/dist/modules/calls/call.routes.js +103 -0
- package/dist/modules/calls/call.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.model.d.ts +45 -0
- package/dist/modules/campaigns/campaign.model.js +98 -0
- package/dist/modules/campaigns/campaign.model.js.map +1 -0
- package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
- package/dist/modules/campaigns/campaign.routes.js +323 -0
- package/dist/modules/campaigns/campaign.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.service.d.ts +11 -0
- package/dist/modules/campaigns/campaign.service.js +86 -0
- package/dist/modules/campaigns/campaign.service.js.map +1 -0
- package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
- package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
- package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
- package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
- package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
- package/dist/modules/leads/lead.model.d.ts +24 -0
- package/dist/modules/leads/lead.model.js +54 -0
- package/dist/modules/leads/lead.model.js.map +1 -0
- package/dist/modules/leads/lead.routes.d.ts +2 -0
- package/dist/modules/leads/lead.routes.js +201 -0
- package/dist/modules/leads/lead.routes.js.map +1 -0
- package/dist/modules/plans/plan.model.d.ts +25 -0
- package/dist/modules/plans/plan.model.js +59 -0
- package/dist/modules/plans/plan.model.js.map +1 -0
- package/dist/modules/surveys/survey.model.d.ts +30 -0
- package/dist/modules/surveys/survey.model.js +75 -0
- package/dist/modules/surveys/survey.model.js.map +1 -0
- package/dist/modules/surveys/survey.routes.d.ts +2 -0
- package/dist/modules/surveys/survey.routes.js +153 -0
- package/dist/modules/surveys/survey.routes.js.map +1 -0
- package/dist/modules/tenants/tenant.model.d.ts +86 -0
- package/dist/modules/tenants/tenant.model.js +127 -0
- package/dist/modules/tenants/tenant.model.js.map +1 -0
- package/dist/modules/tenants/tenant.routes.d.ts +2 -0
- package/dist/modules/tenants/tenant.routes.js +65 -0
- package/dist/modules/tenants/tenant.routes.js.map +1 -0
- package/dist/modules/users/user.routes.d.ts +2 -0
- package/dist/modules/users/user.routes.js +106 -0
- package/dist/modules/users/user.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
- package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
- package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
- package/dist/modules/webhooks/elevenlabs.service.js +512 -0
- package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
- package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
- package/dist/modules/webhooks/unipile.routes.js +780 -0
- package/dist/modules/webhooks/unipile.routes.js.map +1 -0
- package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
- package/dist/modules/whatsapp/appointment.model.js +58 -0
- package/dist/modules/whatsapp/appointment.model.js.map +1 -0
- package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
- package/dist/modules/whatsapp/operator-request.model.js +65 -0
- package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
- package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
- package/dist/modules/whatsapp/stt-usage.model.js +53 -0
- package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
- package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
- package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
- package/dist/plugins/cors.d.ts +3 -0
- package/dist/plugins/cors.js +11 -0
- package/dist/plugins/cors.js.map +1 -0
- package/dist/plugins/jwt.d.ts +3 -0
- package/dist/plugins/jwt.js +22 -0
- package/dist/plugins/jwt.js.map +1 -0
- package/dist/plugins/rawBody.d.ts +3 -0
- package/dist/plugins/rawBody.js +16 -0
- package/dist/plugins/rawBody.js.map +1 -0
- package/dist/plugins/rbac.d.ts +5 -0
- package/dist/plugins/rbac.js +29 -0
- package/dist/plugins/rbac.js.map +1 -0
- package/dist/routes/admin.routes.d.ts +2 -0
- package/dist/routes/admin.routes.js +169 -0
- package/dist/routes/admin.routes.js.map +1 -0
- package/dist/routes/health.routes.d.ts +2 -0
- package/dist/routes/health.routes.js +16 -0
- package/dist/routes/health.routes.js.map +1 -0
- package/dist/routes/webhook.routes.d.ts +2 -0
- package/dist/routes/webhook.routes.js +17 -0
- package/dist/routes/webhook.routes.js.map +1 -0
- package/dist/services/ai/base.ai.d.ts +19 -0
- package/dist/services/ai/base.ai.js +120 -0
- package/dist/services/ai/base.ai.js.map +1 -0
- package/dist/services/ai/gemini.service.d.ts +11 -0
- package/dist/services/ai/gemini.service.js +43 -0
- package/dist/services/ai/gemini.service.js.map +1 -0
- package/dist/services/ai/index.d.ts +2 -0
- package/dist/services/ai/index.js +26 -0
- package/dist/services/ai/index.js.map +1 -0
- package/dist/services/ai/openai.service.d.ts +11 -0
- package/dist/services/ai/openai.service.js +50 -0
- package/dist/services/ai/openai.service.js.map +1 -0
- package/dist/services/elevenlabs.service.d.ts +52 -0
- package/dist/services/elevenlabs.service.js +447 -0
- package/dist/services/elevenlabs.service.js.map +1 -0
- package/dist/services/google-calendar.service.d.ts +60 -0
- package/dist/services/google-calendar.service.js +494 -0
- package/dist/services/google-calendar.service.js.map +1 -0
- package/dist/services/inbound-call-schedule.service.d.ts +11 -0
- package/dist/services/inbound-call-schedule.service.js +162 -0
- package/dist/services/inbound-call-schedule.service.js.map +1 -0
- package/dist/services/netgsm.service.d.ts +41 -0
- package/dist/services/netgsm.service.js +89 -0
- package/dist/services/netgsm.service.js.map +1 -0
- package/dist/services/unipile.service.d.ts +41 -0
- package/dist/services/unipile.service.js +149 -0
- package/dist/services/unipile.service.js.map +1 -0
- package/dist/services/whatsapp-agent.service.d.ts +139 -0
- package/dist/services/whatsapp-agent.service.js +2055 -0
- package/dist/services/whatsapp-agent.service.js.map +1 -0
- package/dist/services/whatsapp.service.d.ts +26 -0
- package/dist/services/whatsapp.service.js +206 -0
- package/dist/services/whatsapp.service.js.map +1 -0
- package/dist/templates/index.d.ts +39 -0
- package/dist/templates/index.js +35 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/receptionist.d.ts +2 -0
- package/dist/templates/receptionist.js +39 -0
- package/dist/templates/receptionist.js.map +1 -0
- package/dist/templates/survey.d.ts +2 -0
- package/dist/templates/survey.js +41 -0
- package/dist/templates/survey.js.map +1 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.nestjs.yml +89 -0
- package/docker-compose.yml +78 -0
- package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
- package/docs/API.md +1193 -0
- package/docs/API_ENDPOINTS.md +344 -0
- package/docs/ARCHITECTURE.md +305 -0
- package/docs/AUTH_API.md +252 -0
- package/docs/BILLING_SMS_ALERTS.md +94 -0
- package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
- package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
- package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
- package/docs/FRONTEND_CHECKLIST.md +227 -0
- package/docs/IMPLEMENTATION_STATUS.md +470 -0
- package/docs/MIGRATION_GUIDE.md +96 -0
- package/docs/MISSINGS_REPORT.md +507 -0
- package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
- package/docs/PROJECT_DESCRIPTION.md +1038 -0
- package/docs/SCALING.md +148 -0
- package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
- package/docs/WHATSAPP_AGENT.md +1086 -0
- package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
- package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
- package/docs/architecture/02-AUTHENTICATION.md +1040 -0
- package/docs/architecture/03-MULTI-CLINIC.md +742 -0
- package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
- package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
- package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
- package/docs/architecture/07-APPOINTMENTS.md +849 -0
- package/docs/architecture/08-VOICE-CALLS.md +470 -0
- package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
- package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
- package/docs/architecture/11-BILLING.md +458 -0
- package/docs/architecture/12-SECURITY.md +216 -0
- package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
- package/docs/architecture/14-META-BUSINESS-API.md +454 -0
- package/docs/architecture/15-AI-MODULE.md +479 -0
- package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
- package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
- package/docs/architecture/18-FILE-STORAGE.md +410 -0
- package/docs/architecture/19-PATIENTS.md +1034 -0
- package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
- package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
- package/docs/database.md +456 -0
- package/docs/ornek-randevu-onay.csv +3 -0
- package/ecosystem.config.js +16 -0
- package/elevenlabs-convai-api-reference.md +1171 -0
- package/frontend/.dockerignore +2 -0
- package/frontend/Dockerfile +24 -0
- package/frontend/README.md +75 -0
- package/frontend/components.json +25 -0
- package/frontend/eslint.config.js +23 -0
- package/frontend/index.html +13 -0
- package/frontend/nginx.conf +37 -0
- package/frontend/package-lock.json +8709 -0
- package/frontend/package.json +71 -0
- package/frontend/public/favicon.svg +1 -0
- package/frontend/public/icons.svg +24 -0
- package/frontend/src/App.tsx +125 -0
- package/frontend/src/components/error-boundary.tsx +50 -0
- package/frontend/src/components/shared/activity-timeline.tsx +66 -0
- package/frontend/src/components/shared/appointment-calendar.css +80 -0
- package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
- package/frontend/src/components/shared/chat-bubble.tsx +72 -0
- package/frontend/src/components/shared/combobox.tsx +119 -0
- package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
- package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
- package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
- package/frontend/src/components/shared/empty-state.tsx +27 -0
- package/frontend/src/components/shared/file-upload.tsx +140 -0
- package/frontend/src/components/shared/index.ts +20 -0
- package/frontend/src/components/shared/language-switcher.tsx +29 -0
- package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
- package/frontend/src/components/shared/page-header.tsx +23 -0
- package/frontend/src/components/shared/stat-card.tsx +37 -0
- package/frontend/src/components/shared/status-badge.tsx +70 -0
- package/frontend/src/components/shared/status-dot.tsx +43 -0
- package/frontend/src/components/ui/alert-dialog.tsx +187 -0
- package/frontend/src/components/ui/alert.tsx +76 -0
- package/frontend/src/components/ui/avatar.tsx +109 -0
- package/frontend/src/components/ui/badge.tsx +52 -0
- package/frontend/src/components/ui/breadcrumb.tsx +125 -0
- package/frontend/src/components/ui/button.tsx +60 -0
- package/frontend/src/components/ui/calendar.tsx +219 -0
- package/frontend/src/components/ui/card.tsx +103 -0
- package/frontend/src/components/ui/chart.tsx +371 -0
- package/frontend/src/components/ui/checkbox.tsx +29 -0
- package/frontend/src/components/ui/collapsible.tsx +19 -0
- package/frontend/src/components/ui/command.tsx +194 -0
- package/frontend/src/components/ui/dialog.tsx +158 -0
- package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
- package/frontend/src/components/ui/input-group.tsx +156 -0
- package/frontend/src/components/ui/input.tsx +20 -0
- package/frontend/src/components/ui/label.tsx +20 -0
- package/frontend/src/components/ui/pagination.tsx +130 -0
- package/frontend/src/components/ui/popover.tsx +90 -0
- package/frontend/src/components/ui/progress.tsx +83 -0
- package/frontend/src/components/ui/radio-group.tsx +36 -0
- package/frontend/src/components/ui/scroll-area.tsx +54 -0
- package/frontend/src/components/ui/select.tsx +199 -0
- package/frontend/src/components/ui/separator.tsx +23 -0
- package/frontend/src/components/ui/sheet.tsx +138 -0
- package/frontend/src/components/ui/sidebar.tsx +723 -0
- package/frontend/src/components/ui/skeleton.tsx +13 -0
- package/frontend/src/components/ui/sonner.tsx +47 -0
- package/frontend/src/components/ui/switch.tsx +30 -0
- package/frontend/src/components/ui/table.tsx +114 -0
- package/frontend/src/components/ui/tabs.tsx +82 -0
- package/frontend/src/components/ui/textarea.tsx +18 -0
- package/frontend/src/components/ui/toggle-group.tsx +89 -0
- package/frontend/src/components/ui/toggle.tsx +43 -0
- package/frontend/src/components/ui/tooltip.tsx +64 -0
- package/frontend/src/hooks/use-mobile.ts +19 -0
- package/frontend/src/i18n/index.ts +20 -0
- package/frontend/src/i18n/locales/en.json +786 -0
- package/frontend/src/i18n/locales/tr.json +786 -0
- package/frontend/src/index.css +134 -0
- package/frontend/src/layouts/admin-layout.tsx +111 -0
- package/frontend/src/layouts/app-header.tsx +130 -0
- package/frontend/src/layouts/app-layout.tsx +19 -0
- package/frontend/src/layouts/app-sidebar.tsx +212 -0
- package/frontend/src/layouts/auth-guard.tsx +31 -0
- package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
- package/frontend/src/lib/api.ts +68 -0
- package/frontend/src/lib/hooks/use-appointments.ts +224 -0
- package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
- package/frontend/src/lib/hooks/use-chats.ts +236 -0
- package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
- package/frontend/src/lib/hooks/use-clinics.ts +37 -0
- package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
- package/frontend/src/lib/hooks/use-examinations.ts +89 -0
- package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
- package/frontend/src/lib/hooks/use-patients.ts +296 -0
- package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
- package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
- package/frontend/src/lib/hooks/use-treatments.ts +125 -0
- package/frontend/src/lib/hooks/use-users.ts +172 -0
- package/frontend/src/lib/utils.ts +6 -0
- package/frontend/src/main.tsx +17 -0
- package/frontend/src/pages/admin/agents/detail.tsx +774 -0
- package/frontend/src/pages/admin/agents/import.tsx +280 -0
- package/frontend/src/pages/admin/agents/index.tsx +245 -0
- package/frontend/src/pages/admin/ai-playground.tsx +543 -0
- package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
- package/frontend/src/pages/appointments/index.tsx +860 -0
- package/frontend/src/pages/audit/index.tsx +24 -0
- package/frontend/src/pages/auth/login.tsx +194 -0
- package/frontend/src/pages/billing/index.tsx +24 -0
- package/frontend/src/pages/calendar/index.tsx +704 -0
- package/frontend/src/pages/calendar-connections/index.tsx +295 -0
- package/frontend/src/pages/calls/index.tsx +24 -0
- package/frontend/src/pages/campaigns/index.tsx +24 -0
- package/frontend/src/pages/chats/index.tsx +981 -0
- package/frontend/src/pages/clinics/index.tsx +224 -0
- package/frontend/src/pages/clinics/settings.tsx +412 -0
- package/frontend/src/pages/components-showcase.tsx +773 -0
- package/frontend/src/pages/connections/index.tsx +328 -0
- package/frontend/src/pages/dashboard.tsx +50 -0
- package/frontend/src/pages/leads/index.tsx +24 -0
- package/frontend/src/pages/my-schedule/index.tsx +496 -0
- package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
- package/frontend/src/pages/patients/detail.tsx +1195 -0
- package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
- package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
- package/frontend/src/pages/patients/index.tsx +381 -0
- package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
- package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
- package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
- package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
- package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
- package/frontend/src/pages/phone-numbers/index.tsx +455 -0
- package/frontend/src/pages/platform/index.tsx +454 -0
- package/frontend/src/pages/settings/index.tsx +126 -0
- package/frontend/src/pages/treatments/index.tsx +487 -0
- package/frontend/src/pages/users/doctor-profile.tsx +672 -0
- package/frontend/src/pages/users/edit.tsx +329 -0
- package/frontend/src/pages/users/index.tsx +407 -0
- package/frontend/src/pages/validation/index.tsx +24 -0
- package/frontend/src/stores/auth.store.ts +108 -0
- package/frontend/src/stores/ui.store.ts +41 -0
- package/frontend/tsconfig.app.json +32 -0
- package/frontend/tsconfig.json +13 -0
- package/frontend/tsconfig.node.json +26 -0
- package/frontend/vite.config.ts +29 -0
- package/nestjs/.dockerignore +5 -0
- package/nestjs/.env.docker +64 -0
- package/nestjs/.prettierrc +4 -0
- package/nestjs/Dockerfile +36 -0
- package/nestjs/README.md +98 -0
- package/nestjs/eslint.config.mjs +35 -0
- package/nestjs/nest-cli.json +8 -0
- package/nestjs/package-lock.json +13390 -0
- package/nestjs/package.json +114 -0
- package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
- package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
- package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
- package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
- package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
- package/nestjs/prisma/migrations/migration_lock.toml +3 -0
- package/nestjs/prisma/schema.prisma +1843 -0
- package/nestjs/prisma/seed.ts +375 -0
- package/nestjs/prisma.config.ts +14 -0
- package/nestjs/src/admin/admin.controller.ts +27 -0
- package/nestjs/src/admin/admin.module.ts +16 -0
- package/nestjs/src/admin/admin.service.ts +91 -0
- package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
- package/nestjs/src/admin/ai-test.controller.ts +191 -0
- package/nestjs/src/admin/superadmin.controller.ts +106 -0
- package/nestjs/src/agents/agents.controller.ts +262 -0
- package/nestjs/src/agents/agents.module.ts +13 -0
- package/nestjs/src/agents/agents.service.ts +733 -0
- package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
- package/nestjs/src/agents/dto/index.ts +2 -0
- package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
- package/nestjs/src/app.module.ts +115 -0
- package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
- package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
- package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
- package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
- package/nestjs/src/appointment-validation/dto/index.ts +1 -0
- package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
- package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
- package/nestjs/src/appointments/appointments.controller.ts +268 -0
- package/nestjs/src/appointments/appointments.module.ts +13 -0
- package/nestjs/src/appointments/appointments.service.ts +773 -0
- package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
- package/nestjs/src/appointments/dto/index.ts +4 -0
- package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
- package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
- package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
- package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
- package/nestjs/src/audit/audit.controller.ts +84 -0
- package/nestjs/src/audit/audit.decorator.ts +20 -0
- package/nestjs/src/audit/audit.interceptor.ts +67 -0
- package/nestjs/src/audit/audit.interfaces.ts +15 -0
- package/nestjs/src/audit/audit.module.ts +12 -0
- package/nestjs/src/audit/audit.service.ts +177 -0
- package/nestjs/src/auth/auth.controller.ts +116 -0
- package/nestjs/src/auth/auth.module.ts +25 -0
- package/nestjs/src/auth/auth.service.ts +612 -0
- package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
- package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
- package/nestjs/src/auth/dto/index.ts +6 -0
- package/nestjs/src/auth/dto/login.dto.ts +13 -0
- package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
- package/nestjs/src/auth/dto/register.dto.ts +28 -0
- package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
- package/nestjs/src/auth/permissions.config.ts +85 -0
- package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
- package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
- package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
- package/nestjs/src/billing/billing.controller.ts +104 -0
- package/nestjs/src/billing/billing.module.ts +12 -0
- package/nestjs/src/billing/billing.service.ts +398 -0
- package/nestjs/src/billing/dto/index.ts +2 -0
- package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
- package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
- package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
- package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
- package/nestjs/src/calls/calls.controller.ts +92 -0
- package/nestjs/src/calls/calls.module.ts +10 -0
- package/nestjs/src/calls/calls.service.ts +359 -0
- package/nestjs/src/calls/dto/index.ts +1 -0
- package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
- package/nestjs/src/clinics/clinics.controller.ts +226 -0
- package/nestjs/src/clinics/clinics.module.ts +11 -0
- package/nestjs/src/clinics/clinics.service.ts +203 -0
- package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
- package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
- package/nestjs/src/clinics/dto/index.ts +4 -0
- package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
- package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
- package/nestjs/src/clinics/meta-connections.service.ts +210 -0
- package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
- package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
- package/nestjs/src/common/decorators/features.decorator.ts +7 -0
- package/nestjs/src/common/decorators/index.ts +4 -0
- package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
- package/nestjs/src/common/gateways/events.gateway.ts +157 -0
- package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
- package/nestjs/src/common/guards/features.guard.ts +38 -0
- package/nestjs/src/common/guards/index.ts +5 -0
- package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
- package/nestjs/src/common/guards/permissions.guard.ts +50 -0
- package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
- package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
- package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
- package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
- package/nestjs/src/config/config.module.ts +13 -0
- package/nestjs/src/database/database.module.ts +9 -0
- package/nestjs/src/database/prisma.service.ts +20 -0
- package/nestjs/src/database/tenant-context.ts +27 -0
- package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
- package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
- package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
- package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
- package/nestjs/src/integrations/ai/ai.module.ts +11 -0
- package/nestjs/src/integrations/ai/ai.service.ts +148 -0
- package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
- package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
- package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
- package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
- package/nestjs/src/integrations/image/image.module.ts +9 -0
- package/nestjs/src/integrations/image/image.service.ts +61 -0
- package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
- package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
- package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
- package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
- package/nestjs/src/integrations/minio/minio.module.ts +9 -0
- package/nestjs/src/integrations/minio/minio.service.ts +88 -0
- package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
- package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
- package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
- package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
- package/nestjs/src/leads/dto/index.ts +2 -0
- package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
- package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
- package/nestjs/src/leads/leads.controller.ts +184 -0
- package/nestjs/src/leads/leads.module.ts +10 -0
- package/nestjs/src/leads/leads.service.ts +375 -0
- package/nestjs/src/logging/logging.controller.ts +82 -0
- package/nestjs/src/logging/logging.module.ts +11 -0
- package/nestjs/src/logging/logging.service.ts +180 -0
- package/nestjs/src/main.ts +86 -0
- package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
- package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
- package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
- package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
- package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
- package/nestjs/src/patients/documents.service.ts +231 -0
- package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
- package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
- package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
- package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
- package/nestjs/src/patients/dto/index.ts +10 -0
- package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
- package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
- package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
- package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
- package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
- package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
- package/nestjs/src/patients/examinations.service.ts +271 -0
- package/nestjs/src/patients/medical-history.service.ts +149 -0
- package/nestjs/src/patients/notes.service.ts +172 -0
- package/nestjs/src/patients/patients.controller.ts +485 -0
- package/nestjs/src/patients/patients.module.ts +22 -0
- package/nestjs/src/patients/patients.service.ts +412 -0
- package/nestjs/src/patients/photo-sets.service.ts +389 -0
- package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
- package/nestjs/src/phone-numbers/dto/index.ts +3 -0
- package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
- package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
- package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
- package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
- package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
- package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
- package/nestjs/src/plans/dto/index.ts +2 -0
- package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
- package/nestjs/src/plans/plans.controller.ts +50 -0
- package/nestjs/src/plans/plans.module.ts +10 -0
- package/nestjs/src/plans/plans.service.ts +115 -0
- package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
- package/nestjs/src/platform/dto/index.ts +2 -0
- package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
- package/nestjs/src/platform/platform.controller.ts +79 -0
- package/nestjs/src/platform/platform.module.ts +10 -0
- package/nestjs/src/platform/platform.service.ts +301 -0
- package/nestjs/src/queue/queue.module.ts +56 -0
- package/nestjs/src/redis/redis.module.ts +20 -0
- package/nestjs/src/tenants/dto/index.ts +1 -0
- package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
- package/nestjs/src/tenants/tenants.controller.ts +45 -0
- package/nestjs/src/tenants/tenants.module.ts +10 -0
- package/nestjs/src/tenants/tenants.service.ts +41 -0
- package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
- package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
- package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
- package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
- package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
- package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
- package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
- package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
- package/nestjs/src/tools/tool-registry.service.ts +221 -0
- package/nestjs/src/tools/tool.decorator.ts +16 -0
- package/nestjs/src/tools/tool.interfaces.ts +26 -0
- package/nestjs/src/tools/tools.module.ts +50 -0
- package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
- package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
- package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
- package/nestjs/src/treatments/dto/index.ts +6 -0
- package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
- package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
- package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
- package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
- package/nestjs/src/treatments/treatments.controller.ts +265 -0
- package/nestjs/src/treatments/treatments.module.ts +14 -0
- package/nestjs/src/treatments/treatments.service.ts +165 -0
- package/nestjs/src/users/doctor-profiles.service.ts +202 -0
- package/nestjs/src/users/dto/index.ts +4 -0
- package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
- package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
- package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
- package/nestjs/src/users/dto/update-user.dto.ts +41 -0
- package/nestjs/src/users/users.controller.ts +142 -0
- package/nestjs/src/users/users.module.ts +11 -0
- package/nestjs/src/users/users.service.ts +250 -0
- package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
- package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
- package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
- package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
- package/nestjs/src/webhooks/webhooks.module.ts +17 -0
- package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
- package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
- package/nestjs/src/whatsapp/dto/index.ts +2 -0
- package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
- package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
- package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
- package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
- package/nestjs/src/whatsapp/operator.service.ts +692 -0
- package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
- package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
- package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
- package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
- package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
- package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
- package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
- package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
- package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
- package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
- package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
- package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
- package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
- package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
- package/nestjs/test/app.e2e-spec.ts +25 -0
- package/nestjs/test/jest-e2e.json +9 -0
- package/nestjs/tsconfig.build.json +4 -0
- package/nestjs/tsconfig.json +25 -0
- package/nginx.example.conf +18 -0
- package/package.json +47 -0
- package/scripts/addRecipient.ts +48 -0
- package/scripts/listRecipients.ts +31 -0
- package/scripts/migrate-agent-ref.ts +86 -0
- package/scripts/migrate-sessions.ts +183 -0
- package/scripts/promote.ts +27 -0
- package/scripts/retrigger.ts +84 -0
- package/scripts/seed.ts +435 -0
- package/scripts/testSend.ts +63 -0
- package/src/app.ts +85 -0
- package/src/config/agentPrompts.json +19 -0
- package/src/config/database.ts +21 -0
- package/src/config/env.ts +94 -0
- package/src/config/index.ts +86 -0
- package/src/controllers/webhook.controller.ts +150 -0
- package/src/errors/index.ts +9 -0
- package/src/hooks/webhookValidator.ts +55 -0
- package/src/index.ts +68 -0
- package/src/migrations/001_session-architecture.ts +138 -0
- package/src/migrations/002_agent-ref.ts +65 -0
- package/src/migrations/003_shift-schedule.ts +55 -0
- package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
- package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
- package/src/migrations/migration.model.ts +27 -0
- package/src/migrations/migration.routes.ts +40 -0
- package/src/migrations/migration.runner.ts +112 -0
- package/src/models/ConversationClaim.ts +17 -0
- package/src/models/ConversationEvaluation.ts +91 -0
- package/src/models/Summary.ts +77 -0
- package/src/models/Transcription.ts +68 -0
- package/src/models/WhatsAppRecipient.ts +37 -0
- package/src/modules/admin/admin.routes.ts +2385 -0
- package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
- package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
- package/src/modules/agents/agent.model.ts +93 -0
- package/src/modules/agents/agent.routes.ts +65 -0
- package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
- package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
- package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
- package/src/modules/auth/auth.model.ts +42 -0
- package/src/modules/auth/auth.routes.ts +199 -0
- package/src/modules/auth/auth.service.ts +210 -0
- package/src/modules/auth/refresh-token.model.ts +26 -0
- package/src/modules/billing/billing-alert.model.ts +28 -0
- package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
- package/src/modules/billing/billing.model.ts +42 -0
- package/src/modules/billing/billing.routes.ts +67 -0
- package/src/modules/billing/billing.service.ts +562 -0
- package/src/modules/billing/payment.model.ts +42 -0
- package/src/modules/calls/call.model.ts +102 -0
- package/src/modules/calls/call.routes.ts +118 -0
- package/src/modules/campaigns/campaign.model.ts +111 -0
- package/src/modules/campaigns/campaign.routes.ts +402 -0
- package/src/modules/campaigns/campaign.service.ts +99 -0
- package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
- package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
- package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
- package/src/modules/leads/lead.model.ts +40 -0
- package/src/modules/leads/lead.routes.ts +246 -0
- package/src/modules/logs/log.model.ts +27 -0
- package/src/modules/logs/log.routes.ts +102 -0
- package/src/modules/plans/plan.model.ts +45 -0
- package/src/modules/surveys/survey.model.ts +70 -0
- package/src/modules/surveys/survey.routes.ts +187 -0
- package/src/modules/tenants/tenant.model.ts +181 -0
- package/src/modules/tenants/tenant.routes.ts +78 -0
- package/src/modules/users/user.routes.ts +126 -0
- package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
- package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
- package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
- package/src/modules/webhooks/elevenlabs.service.ts +565 -0
- package/src/modules/webhooks/unipile.routes.ts +917 -0
- package/src/modules/whatsapp/appointment.model.ts +47 -0
- package/src/modules/whatsapp/operator-request.model.ts +58 -0
- package/src/modules/whatsapp/stt-usage.model.ts +30 -0
- package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
- package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
- package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
- package/src/plugins/cors.ts +7 -0
- package/src/plugins/jwt.ts +18 -0
- package/src/plugins/rawBody.ts +12 -0
- package/src/plugins/rbac.ts +24 -0
- package/src/routes/admin.routes.ts +208 -0
- package/src/routes/health.routes.ts +12 -0
- package/src/routes/webhook.routes.ts +12 -0
- package/src/services/ai/base.ai.ts +132 -0
- package/src/services/ai/gemini.service.ts +41 -0
- package/src/services/ai/index.ts +24 -0
- package/src/services/ai/openai.service.ts +48 -0
- package/src/services/elevenlabs.service.ts +532 -0
- package/src/services/google-calendar.service.ts +656 -0
- package/src/services/inbound-call-schedule.service.ts +174 -0
- package/src/services/netgsm.service.ts +128 -0
- package/src/services/unipile.service.ts +200 -0
- package/src/services/whatsapp-agent.service.ts +2479 -0
- package/src/services/whatsapp.service.ts +245 -0
- package/src/templates/index.ts +71 -0
- package/src/templates/receptionist.ts +44 -0
- package/src/templates/survey.ts +44 -0
- package/src/types/index.ts +218 -0
- package/src/utils/logger.ts +83 -0
- package/tsconfig.json +19 -0
- package/web/.dockerignore +4 -0
- package/web/Dockerfile +18 -0
- package/web/README.md +73 -0
- package/web/components.json +23 -0
- package/web/eslint.config.js +23 -0
- package/web/index.html +14 -0
- package/web/nginx.conf +18 -0
- package/web/package-lock.json +10292 -0
- package/web/package.json +48 -0
- package/web/public/favicon.ico +0 -0
- package/web/public/vite.svg +1 -0
- package/web/src/App.tsx +191 -0
- package/web/src/assets/react.svg +1 -0
- package/web/src/components/Layout.tsx +261 -0
- package/web/src/components/LeadConversation.tsx +251 -0
- package/web/src/components/ProtectedRoute.tsx +20 -0
- package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
- package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
- package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
- package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
- package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
- package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
- package/web/src/components/empty-state.tsx +15 -0
- package/web/src/components/page-header.tsx +19 -0
- package/web/src/components/page-loader.tsx +32 -0
- package/web/src/components/pagination.tsx +38 -0
- package/web/src/components/stat-card.tsx +27 -0
- package/web/src/components/status-badge.tsx +125 -0
- package/web/src/components/ui/alert.tsx +66 -0
- package/web/src/components/ui/badge.tsx +48 -0
- package/web/src/components/ui/button.tsx +64 -0
- package/web/src/components/ui/card.tsx +92 -0
- package/web/src/components/ui/checkbox.tsx +32 -0
- package/web/src/components/ui/dialog.tsx +158 -0
- package/web/src/components/ui/dropdown-menu.tsx +255 -0
- package/web/src/components/ui/input.tsx +21 -0
- package/web/src/components/ui/label.tsx +22 -0
- package/web/src/components/ui/progress.tsx +29 -0
- package/web/src/components/ui/select.tsx +188 -0
- package/web/src/components/ui/separator.tsx +28 -0
- package/web/src/components/ui/sheet.tsx +123 -0
- package/web/src/components/ui/skeleton.tsx +13 -0
- package/web/src/components/ui/sonner.tsx +35 -0
- package/web/src/components/ui/table.tsx +116 -0
- package/web/src/components/ui/tabs.tsx +89 -0
- package/web/src/components/ui/textarea.tsx +18 -0
- package/web/src/components/ui/tooltip.tsx +57 -0
- package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
- package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
- package/web/src/components/whatsapp/ChatList.tsx +107 -0
- package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
- package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
- package/web/src/components/whatsapp/MessageInput.tsx +63 -0
- package/web/src/components/whatsapp/MessageStream.tsx +135 -0
- package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
- package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
- package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
- package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
- package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
- package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
- package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
- package/web/src/contexts/AuthContext.tsx +103 -0
- package/web/src/index.css +130 -0
- package/web/src/lib/api.ts +92 -0
- package/web/src/lib/utils.ts +6 -0
- package/web/src/main.tsx +10 -0
- package/web/src/pages/AppointmentDetail.tsx +206 -0
- package/web/src/pages/AppointmentValidation.tsx +157 -0
- package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
- package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
- package/web/src/pages/Appointments.tsx +283 -0
- package/web/src/pages/Billing.tsx +126 -0
- package/web/src/pages/CallDetail.tsx +293 -0
- package/web/src/pages/CallSettings.tsx +313 -0
- package/web/src/pages/Calls.tsx +188 -0
- package/web/src/pages/CampaignDetail.tsx +216 -0
- package/web/src/pages/CampaignNew.tsx +277 -0
- package/web/src/pages/CampaignResults.tsx +185 -0
- package/web/src/pages/Campaigns.tsx +171 -0
- package/web/src/pages/Dashboard.tsx +336 -0
- package/web/src/pages/InboundSchedule.tsx +246 -0
- package/web/src/pages/LeadDetail.tsx +183 -0
- package/web/src/pages/Leads.tsx +258 -0
- package/web/src/pages/Login.tsx +99 -0
- package/web/src/pages/Register.tsx +129 -0
- package/web/src/pages/Settings.tsx +133 -0
- package/web/src/pages/SurveyDetail.tsx +232 -0
- package/web/src/pages/SurveyPreview.tsx +179 -0
- package/web/src/pages/Surveys.tsx +207 -0
- package/web/src/pages/Users.tsx +199 -0
- package/web/src/pages/WhatsApp.tsx +147 -0
- package/web/src/pages/WhatsAppSettings.tsx +215 -0
- package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
- package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
- package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
- package/web/src/pages/admin/AdminCredits.tsx +467 -0
- package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
- package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
- package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
- package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
- package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
- package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
- package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
- package/web/src/pages/admin/AdminLeads.tsx +339 -0
- package/web/src/pages/admin/AdminLogs.tsx +283 -0
- package/web/src/pages/admin/AdminMigrations.tsx +247 -0
- package/web/src/pages/admin/AdminPlans.tsx +313 -0
- package/web/src/pages/admin/AdminSystem.tsx +391 -0
- package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
- package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
- package/web/src/pages/admin/AdminTenants.tsx +274 -0
- package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
- package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
- package/web/src/types/index.ts +242 -0
- package/web/tsconfig.app.json +32 -0
- package/web/tsconfig.json +13 -0
- package/web/tsconfig.node.json +26 -0
- package/web/vite.config.ts +23 -0
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
# Treatments & Treatment Plans
|
|
2
|
+
|
|
3
|
+
> Service catalog and multi-visit treatment plans -- clinic-defined treatments with pricing and recall, patient-specific plans with ordered items linked to appointments.
|
|
4
|
+
> Last updated: 2026-03-29
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
1. [Overview](#1-overview)
|
|
11
|
+
2. [Treatment Model (Service Catalog)](#2-treatment-model-service-catalog)
|
|
12
|
+
3. [Treatment Plan Model](#3-treatment-plan-model)
|
|
13
|
+
4. [Treatment Plan Item Model](#4-treatment-plan-item-model)
|
|
14
|
+
5. [Treatment Plan Workflow](#5-treatment-plan-workflow)
|
|
15
|
+
6. [AI Integration](#6-ai-integration)
|
|
16
|
+
7. [API Endpoints](#7-api-endpoints)
|
|
17
|
+
8. [Permissions](#8-permissions)
|
|
18
|
+
9. [Recall Automation (P1 -- Design Reference)](#9-recall-automation-p1----design-reference)
|
|
19
|
+
10. [NestJS Module Structure](#10-nestjs-module-structure)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Overview
|
|
24
|
+
|
|
25
|
+
Two core concepts support clinical workflow:
|
|
26
|
+
|
|
27
|
+
- **Treatment** (service catalog) -- What the clinic offers. Each clinic defines its treatments with name, category, duration, price, and recall period. This is the menu of services the clinic sells. Treatments are reusable across patients.
|
|
28
|
+
- **Treatment Plan** -- A multi-visit plan for a specific patient. Contains ordered items, each linked to a treatment from the catalog and optionally to an appointment. This is the doctor's prescription of what needs to happen for this patient, in what order.
|
|
29
|
+
|
|
30
|
+
### Concrete Example
|
|
31
|
+
|
|
32
|
+
A dental clinic's treatment catalog:
|
|
33
|
+
|
|
34
|
+
| Treatment | Category | Duration | Price | Recall |
|
|
35
|
+
|-----------|----------|----------|-------|--------|
|
|
36
|
+
| Root Canal | Endodonti | 60 min | 3,000 TRY | -- |
|
|
37
|
+
| Crown Prep | Protez | 45 min | 2,000 TRY | -- |
|
|
38
|
+
| Crown Fit | Protez | 30 min | 1,500 TRY | -- |
|
|
39
|
+
| Dental Cleaning | Genel | 30 min | 500 TRY | 180 days |
|
|
40
|
+
| Teeth Whitening | Estetik | 60 min | 1,500 TRY | 365 days |
|
|
41
|
+
|
|
42
|
+
A treatment plan for patient Ayse -- **"Tooth 14 Restoration"**:
|
|
43
|
+
|
|
44
|
+
| # | Item | Treatment | Status | Appointment |
|
|
45
|
+
|---|------|-----------|--------|-------------|
|
|
46
|
+
| 1 | Root canal, tooth 14 | Root Canal | completed | 2026-03-15 10:00 |
|
|
47
|
+
| 2 | Crown preparation, tooth 14 | Crown Prep | scheduled | 2026-04-05 14:00 |
|
|
48
|
+
| 3 | Crown fitting, tooth 14 | Crown Fit | planned | -- |
|
|
49
|
+
|
|
50
|
+
The three items reference treatments from the catalog. Item 1 is done, item 2 has an appointment booked, item 3 is still unscheduled. When all items are completed, the plan status moves to `completed`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. Treatment Model (Service Catalog)
|
|
55
|
+
|
|
56
|
+
### Prisma Schema
|
|
57
|
+
|
|
58
|
+
```prisma
|
|
59
|
+
model Treatment {
|
|
60
|
+
treatment_id String @id @default(uuid())
|
|
61
|
+
treatment_tenant_id String
|
|
62
|
+
treatment_clinic_id String? // null = available at all clinics in tenant
|
|
63
|
+
treatment_name String
|
|
64
|
+
treatment_category String @default("") // "Implant", "Ortodonti", "Estetik", "Genel"
|
|
65
|
+
treatment_description String @default("")
|
|
66
|
+
treatment_duration_minutes Int @default(30)
|
|
67
|
+
treatment_price Decimal?
|
|
68
|
+
treatment_currency String @default("TRY")
|
|
69
|
+
treatment_is_active Boolean @default(true)
|
|
70
|
+
treatment_sort_order Int @default(0)
|
|
71
|
+
treatment_recall_days Int? // null = no recall. e.g., 180 for biannual cleaning
|
|
72
|
+
treatment_created_at DateTime @default(now())
|
|
73
|
+
treatment_updated_at DateTime @updatedAt
|
|
74
|
+
|
|
75
|
+
tenant Tenant @relation(fields: [treatment_tenant_id], references: [tenant_id], onDelete: Cascade)
|
|
76
|
+
clinic Clinic? @relation(fields: [treatment_clinic_id], references: [clinic_id], onDelete: SetNull)
|
|
77
|
+
planItems TreatmentPlanItem[]
|
|
78
|
+
appointments Appointment[]
|
|
79
|
+
|
|
80
|
+
@@index([treatment_tenant_id, treatment_is_active])
|
|
81
|
+
@@index([treatment_tenant_id, treatment_category])
|
|
82
|
+
@@index([treatment_clinic_id])
|
|
83
|
+
@@map("treatments")
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Columns
|
|
88
|
+
|
|
89
|
+
| Column | Type | Required | Default | Description |
|
|
90
|
+
|--------|------|----------|---------|-------------|
|
|
91
|
+
| `treatment_id` | UUID | Yes | `uuid()` | Primary key |
|
|
92
|
+
| `treatment_tenant_id` | UUID | Yes | -- | FK to `tenants`. Tenant isolation |
|
|
93
|
+
| `treatment_clinic_id` | UUID | No | `null` | FK to `clinics`. Null = tenant-level treatment available at all clinics. Non-null = only available at that specific clinic |
|
|
94
|
+
| `treatment_name` | String | Yes | -- | Display name. E.g., "Root Canal", "Diş Beyazlatma", "Botox (1 Bölge)" |
|
|
95
|
+
| `treatment_category` | String | Yes | `""` | Free-form category string. E.g., "Implant", "Ortodonti", "Estetik", "Genel", "Cerrahi" |
|
|
96
|
+
| `treatment_description` | String | Yes | `""` | Optional longer description. Shown in treatment detail view and optionally provided to AI |
|
|
97
|
+
| `treatment_duration_minutes` | Int | Yes | `30` | Default appointment duration in minutes. Used when booking an appointment for this treatment |
|
|
98
|
+
| `treatment_price` | Decimal | No | `null` | Price in `treatment_currency`. Null = "price on consultation" or varies. Decimal, not float, for currency safety |
|
|
99
|
+
| `treatment_currency` | String | Yes | `"TRY"` | ISO 4217 currency code. Turkish clinics default to TRY |
|
|
100
|
+
| `treatment_is_active` | Boolean | Yes | `true` | Soft delete flag. Inactive treatments are hidden from the catalog but preserved for historical plan items |
|
|
101
|
+
| `treatment_sort_order` | Int | Yes | `0` | Display order within the catalog. Lower values appear first |
|
|
102
|
+
| `treatment_recall_days` | Int | No | `null` | Number of days after completion to schedule a recall reminder. Null = no recall. E.g., 180 for biannual cleaning, 365 for annual checkup |
|
|
103
|
+
| `treatment_created_at` | DateTime | Yes | `now()` | Record creation timestamp |
|
|
104
|
+
| `treatment_updated_at` | DateTime | Yes | auto | Automatically updated on every write |
|
|
105
|
+
|
|
106
|
+
### Indexes
|
|
107
|
+
|
|
108
|
+
| Index | Columns | Purpose |
|
|
109
|
+
|-------|---------|---------|
|
|
110
|
+
| Primary | `treatment_id` | PK lookup |
|
|
111
|
+
| `idx_treatments_tenant_active` | `(treatment_tenant_id, treatment_is_active)` | List active treatments for a tenant (the default catalog query) |
|
|
112
|
+
| `idx_treatments_tenant_category` | `(treatment_tenant_id, treatment_category)` | Filter by category within tenant |
|
|
113
|
+
| `idx_treatments_clinic` | `treatment_clinic_id` | List treatments scoped to a specific clinic |
|
|
114
|
+
|
|
115
|
+
### Relations
|
|
116
|
+
|
|
117
|
+
| Direction | Target | FK Column | Description |
|
|
118
|
+
|-----------|--------|-----------|-------------|
|
|
119
|
+
| belongs_to | tenants | `treatment_tenant_id` | Parent tenant (cascade delete) |
|
|
120
|
+
| belongs_to | clinics | `treatment_clinic_id` | Owning clinic, nullable (set null on clinic delete) |
|
|
121
|
+
| has_many | treatment_plan_items | -- | Plan items that reference this treatment |
|
|
122
|
+
| has_many | appointments | -- | Appointments for this treatment type |
|
|
123
|
+
|
|
124
|
+
### Key Design Decisions
|
|
125
|
+
|
|
126
|
+
**`treatment_clinic_id` null = tenant-level treatment.** A multi-clinic tenant (e.g., a dental chain with 3 branches) defines most treatments at the tenant level so all clinics share the same catalog. Clinic-specific treatments (e.g., a branch with a specialized laser machine) are scoped with a non-null `treatment_clinic_id`. Single-clinic tenants never set this -- everything is tenant-level, and the default clinic is used implicitly.
|
|
127
|
+
|
|
128
|
+
**`treatment_recall_days` enables recall automation.** When a treatment plan item linked to a treatment with `recall_days` set is marked completed, a BullMQ delayed job can be scheduled to trigger a recall reminder (WhatsApp message, outbound call, or SMS). This is the foundation for recall campaigns -- see Section 9.
|
|
129
|
+
|
|
130
|
+
**Price is optional and Decimal.** Many clinics do not list prices publicly ("muayene sonrası fiyatlandırma" -- pricing after examination). Others have fixed prices. Using `Decimal` instead of `Float` prevents floating-point arithmetic errors in currency calculations. A treatment with `null` price means "consult for pricing."
|
|
131
|
+
|
|
132
|
+
**Category is a free-form string, not an enum.** Turkish dental clinics use categories like "Endodonti", "Protez", "Ortodonti", "Cerrahi", "Estetik". Aesthetic clinics use "Botox", "Dolgu", "Lazer", "Saç Ekimi". Enforcing an enum would either be too restrictive or require constant updates. Free-form strings let each clinic define their own taxonomy. The `GET /treatments/categories` endpoint provides autocomplete suggestions from existing data.
|
|
133
|
+
|
|
134
|
+
**Soft delete via `treatment_is_active`.** Deleting a treatment would orphan historical plan items. Instead, "deleting" a treatment sets `treatment_is_active = false`. The treatment disappears from the catalog but remains in the database so existing plan items retain their `treatment_id` reference.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 3. Treatment Plan Model
|
|
139
|
+
|
|
140
|
+
### Prisma Schema
|
|
141
|
+
|
|
142
|
+
```prisma
|
|
143
|
+
model TreatmentPlan {
|
|
144
|
+
plan_id String @id @default(uuid())
|
|
145
|
+
plan_tenant_id String
|
|
146
|
+
plan_clinic_id String?
|
|
147
|
+
plan_patient_id String
|
|
148
|
+
plan_doctor_id String? // FK to users (doctor who created/manages the plan)
|
|
149
|
+
plan_name String // "Tooth 14 Restoration", "Full Smile Makeover"
|
|
150
|
+
plan_status TreatmentPlanStatus @default(active)
|
|
151
|
+
plan_notes String @default("")
|
|
152
|
+
plan_created_at DateTime @default(now())
|
|
153
|
+
plan_updated_at DateTime @updatedAt
|
|
154
|
+
|
|
155
|
+
tenant Tenant @relation(fields: [plan_tenant_id], references: [tenant_id], onDelete: Cascade)
|
|
156
|
+
clinic Clinic? @relation(fields: [plan_clinic_id], references: [clinic_id], onDelete: SetNull)
|
|
157
|
+
patient Patient @relation(fields: [plan_patient_id], references: [patient_id], onDelete: Cascade)
|
|
158
|
+
doctor User? @relation("PlanDoctor", fields: [plan_doctor_id], references: [user_id], onDelete: SetNull)
|
|
159
|
+
items TreatmentPlanItem[]
|
|
160
|
+
photoSets PatientPhotoSet[] // Before/after photos linked to plan (see doc 21)
|
|
161
|
+
|
|
162
|
+
@@index([plan_patient_id, plan_status])
|
|
163
|
+
@@index([plan_tenant_id, plan_status])
|
|
164
|
+
@@index([plan_doctor_id])
|
|
165
|
+
@@index([plan_clinic_id])
|
|
166
|
+
@@map("treatment_plans")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
enum TreatmentPlanStatus {
|
|
170
|
+
active
|
|
171
|
+
completed
|
|
172
|
+
cancelled
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Columns
|
|
177
|
+
|
|
178
|
+
| Column | Type | Required | Default | Description |
|
|
179
|
+
|--------|------|----------|---------|-------------|
|
|
180
|
+
| `plan_id` | UUID | Yes | `uuid()` | Primary key |
|
|
181
|
+
| `plan_tenant_id` | UUID | Yes | -- | FK to `tenants`. Tenant isolation |
|
|
182
|
+
| `plan_clinic_id` | UUID | No | `null` | FK to `clinics`. Which clinic manages this plan. Null for single-clinic tenants |
|
|
183
|
+
| `plan_patient_id` | UUID | Yes | -- | FK to `patients`. The patient this plan is for |
|
|
184
|
+
| `plan_doctor_id` | UUID | No | `null` | FK to `users`. The doctor who created or manages this plan. Null if created by receptionist or unassigned |
|
|
185
|
+
| `plan_name` | String | Yes | -- | Free-form plan name. E.g., "Diş 14 Restorasyonu", "Full Smile Makeover", "Alt Çene İmplant" |
|
|
186
|
+
| `plan_status` | TreatmentPlanStatus | Yes | `active` | Plan lifecycle state: `active` (in progress), `completed` (all items done), `cancelled` (abandoned) |
|
|
187
|
+
| `plan_notes` | String | Yes | `""` | Free-form notes about the plan. Doctor's observations, special instructions |
|
|
188
|
+
| `plan_created_at` | DateTime | Yes | `now()` | Record creation timestamp |
|
|
189
|
+
| `plan_updated_at` | DateTime | Yes | auto | Automatically updated on every write |
|
|
190
|
+
|
|
191
|
+
### Indexes
|
|
192
|
+
|
|
193
|
+
| Index | Columns | Purpose |
|
|
194
|
+
|-------|---------|---------|
|
|
195
|
+
| Primary | `plan_id` | PK lookup |
|
|
196
|
+
| `idx_plans_patient_status` | `(plan_patient_id, plan_status)` | List active plans for a patient (the primary query on patient detail page) |
|
|
197
|
+
| `idx_plans_tenant_status` | `(plan_tenant_id, plan_status)` | List all active plans across a tenant (dashboard/reporting) |
|
|
198
|
+
| `idx_plans_doctor` | `plan_doctor_id` | List plans managed by a specific doctor |
|
|
199
|
+
| `idx_plans_clinic` | `plan_clinic_id` | List plans at a specific clinic |
|
|
200
|
+
|
|
201
|
+
### Relations
|
|
202
|
+
|
|
203
|
+
| Direction | Target | FK Column | Description |
|
|
204
|
+
|-----------|--------|-----------|-------------|
|
|
205
|
+
| belongs_to | tenants | `plan_tenant_id` | Parent tenant (cascade delete) |
|
|
206
|
+
| belongs_to | clinics | `plan_clinic_id` | Managing clinic, nullable (set null on clinic delete) |
|
|
207
|
+
| belongs_to | patients | `plan_patient_id` | Parent patient (cascade delete -- deleting a patient removes their plans) |
|
|
208
|
+
| belongs_to | users | `plan_doctor_id` | Responsible doctor, nullable (set null on user delete) |
|
|
209
|
+
| has_many | treatment_plan_items | -- | Ordered items in this plan |
|
|
210
|
+
| has_many | patient_photo_sets | -- | Before/after photo sets linked to this plan (see doc 21) |
|
|
211
|
+
|
|
212
|
+
### Key Design Decisions
|
|
213
|
+
|
|
214
|
+
**Plan belongs to a patient and optionally a doctor.** Every plan must have a patient. The doctor is optional because a receptionist might create the plan and assign a doctor later, or a solo practitioner might not bother assigning themselves.
|
|
215
|
+
|
|
216
|
+
**Status is simple: three states.** `active` means the plan is in progress (some items planned, some completed). `completed` means all items are done. `cancelled` means the plan was abandoned (patient didn't return, changed their mind, etc.). No `draft` or `pending_approval` states -- Turkish clinics don't typically have approval workflows for treatment plans. The doctor creates it and it's immediately active.
|
|
217
|
+
|
|
218
|
+
**No financial fields on the plan.** Total cost, payments, and invoicing are deferred per product decision. The plan is a clinical document, not an invoice. Financial tracking will be handled separately when the billing module extends to patient-level charges. For now, treatment prices live on the Treatment model, and plan items inherit the treatment name.
|
|
219
|
+
|
|
220
|
+
**`plan_name` is free-form.** Doctors write whatever describes the plan: "Diş 14 Restorasyonu", "Tam Ağız Rehabilitasyonu", "Botox + Dudak Dolgusu". No templates, no structured naming. Clinics across specialties (dental, aesthetic, dermatology) use completely different naming conventions.
|
|
221
|
+
|
|
222
|
+
**Cascade delete from patient.** If a patient record is deleted, all their treatment plans and plan items are removed. This is consistent with `PatientPhotoSet` cascade behavior (doc 21).
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 4. Treatment Plan Item Model
|
|
227
|
+
|
|
228
|
+
### Prisma Schema
|
|
229
|
+
|
|
230
|
+
```prisma
|
|
231
|
+
model TreatmentPlanItem {
|
|
232
|
+
item_id String @id @default(uuid())
|
|
233
|
+
item_plan_id String
|
|
234
|
+
item_treatment_id String? // FK to treatments (null if custom/one-off)
|
|
235
|
+
item_appointment_id String? // FK to appointments (linked after booking)
|
|
236
|
+
item_name String // Denormalized treatment name (survives treatment deletion)
|
|
237
|
+
item_tooth_number String? // Dental: "14", "21-23", "UL6"
|
|
238
|
+
item_status TreatmentPlanItemStatus @default(planned)
|
|
239
|
+
item_notes String @default("")
|
|
240
|
+
item_sort_order Int @default(0)
|
|
241
|
+
item_completed_at DateTime?
|
|
242
|
+
item_created_at DateTime @default(now())
|
|
243
|
+
item_updated_at DateTime @updatedAt
|
|
244
|
+
|
|
245
|
+
plan TreatmentPlan @relation(fields: [item_plan_id], references: [plan_id], onDelete: Cascade)
|
|
246
|
+
treatment Treatment? @relation(fields: [item_treatment_id], references: [treatment_id], onDelete: SetNull)
|
|
247
|
+
appointment Appointment? @relation(fields: [item_appointment_id], references: [appt_id], onDelete: SetNull)
|
|
248
|
+
|
|
249
|
+
@@index([item_plan_id, item_sort_order])
|
|
250
|
+
@@index([item_appointment_id])
|
|
251
|
+
@@map("treatment_plan_items")
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
enum TreatmentPlanItemStatus {
|
|
255
|
+
planned // Not yet scheduled
|
|
256
|
+
scheduled // Appointment linked
|
|
257
|
+
in_progress // Currently being performed
|
|
258
|
+
completed // Done
|
|
259
|
+
cancelled // Cancelled
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Columns
|
|
264
|
+
|
|
265
|
+
| Column | Type | Required | Default | Description |
|
|
266
|
+
|--------|------|----------|---------|-------------|
|
|
267
|
+
| `item_id` | UUID | Yes | `uuid()` | Primary key |
|
|
268
|
+
| `item_plan_id` | UUID | Yes | -- | FK to `treatment_plans`. The plan this item belongs to |
|
|
269
|
+
| `item_treatment_id` | UUID | No | `null` | FK to `treatments`. The catalog treatment this item represents. Null for custom/one-off items not in the catalog |
|
|
270
|
+
| `item_appointment_id` | UUID | No | `null` | FK to `appointments`. Linked when an appointment is booked for this item. Null until scheduled |
|
|
271
|
+
| `item_name` | String | Yes | -- | Display name for this item. Copied from `treatment_name` on creation, but stored separately (denormalized). E.g., "Root Canal", "Botox (1 Bölge)" |
|
|
272
|
+
| `item_tooth_number` | String | No | `null` | Dental-specific: tooth number(s) this item applies to. E.g., "14", "21-23" (range), "UL6" (Palmer notation). Non-dental specialties leave this null |
|
|
273
|
+
| `item_status` | TreatmentPlanItemStatus | Yes | `planned` | Item lifecycle state. See status transitions below |
|
|
274
|
+
| `item_notes` | String | Yes | `""` | Per-item notes. E.g., "Patient requested sedation", "Use ceramic crown" |
|
|
275
|
+
| `item_sort_order` | Int | Yes | `0` | Display order within the plan. Lower values appear first. Determines the intended sequence of treatment |
|
|
276
|
+
| `item_completed_at` | DateTime | No | `null` | Timestamp when the item was marked completed. Null for non-completed items. Used for recall scheduling |
|
|
277
|
+
| `item_created_at` | DateTime | Yes | `now()` | Record creation timestamp |
|
|
278
|
+
| `item_updated_at` | DateTime | Yes | auto | Automatically updated on every write |
|
|
279
|
+
|
|
280
|
+
### Indexes
|
|
281
|
+
|
|
282
|
+
| Index | Columns | Purpose |
|
|
283
|
+
|-------|---------|---------|
|
|
284
|
+
| Primary | `item_id` | PK lookup |
|
|
285
|
+
| `idx_items_plan_order` | `(item_plan_id, item_sort_order)` | List items in order within a plan (the primary query) |
|
|
286
|
+
| `idx_items_appointment` | `item_appointment_id` | Reverse lookup: which plan item is this appointment for? |
|
|
287
|
+
|
|
288
|
+
### Relations
|
|
289
|
+
|
|
290
|
+
| Direction | Target | FK Column | Description |
|
|
291
|
+
|-----------|--------|-----------|-------------|
|
|
292
|
+
| belongs_to | treatment_plans | `item_plan_id` | Parent plan (cascade delete -- deleting a plan removes all items) |
|
|
293
|
+
| belongs_to | treatments | `item_treatment_id` | Catalog treatment reference, nullable (set null on treatment delete) |
|
|
294
|
+
| belongs_to | appointments | `item_appointment_id` | Linked appointment, nullable (set null on appointment delete) |
|
|
295
|
+
|
|
296
|
+
### Status Transitions
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
planned -----> scheduled -----> in_progress -----> completed
|
|
300
|
+
| | |
|
|
301
|
+
+-------+-------+--------+------+
|
|
302
|
+
| |
|
|
303
|
+
v v
|
|
304
|
+
cancelled cancelled
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
- **planned** -- Item exists in the plan but no appointment is linked. This is the initial state.
|
|
308
|
+
- **scheduled** -- An appointment has been booked for this item (`item_appointment_id` is set). Transition triggered by linking an appointment.
|
|
309
|
+
- **in_progress** -- The treatment is currently being performed. Typically set by the doctor during the appointment. Optional -- small clinics may skip this and go directly from scheduled to completed.
|
|
310
|
+
- **completed** -- The treatment is done. `item_completed_at` is set. If the linked treatment has `treatment_recall_days`, a recall job is scheduled.
|
|
311
|
+
- **cancelled** -- The item was cancelled. Can happen from any state. E.g., doctor decided this step is unnecessary, or patient declined.
|
|
312
|
+
|
|
313
|
+
### Key Design Decisions
|
|
314
|
+
|
|
315
|
+
**`item_treatment_id` is nullable for custom/one-off items.** A doctor might add an item to a plan that doesn't exist in the catalog: "Emergency extraction due to complication" or "Follow-up check (complimentary)". The item still has a name and participates in the plan workflow, it just doesn't reference a catalog treatment.
|
|
316
|
+
|
|
317
|
+
**`item_name` is denormalized.** When a plan item is created from a catalog treatment, `item_name` is copied from `treatment_name`. If the treatment is later renamed ("Root Canal" -> "Root Canal Therapy") or soft-deleted, the plan item still shows the original name. This is critical for medical records -- what was planned must be preserved as-is.
|
|
318
|
+
|
|
319
|
+
**`item_tooth_number` is dental-specific but lives on the generic model.** Dental clinics are the primary user base, and tooth numbers are essential for their workflow. Other specialties (aesthetic, dermatology) simply leave this field null. Adding a separate dental extension table for a single nullable string field would be overengineering.
|
|
320
|
+
|
|
321
|
+
**`item_appointment_id` enables bidirectional tracking.** From the plan: "which items have appointments and which are still unscheduled?" From the appointment: "which plan item is this appointment for?" The `idx_items_appointment` index supports the reverse lookup. When an appointment is deleted (e.g., cancelled), the item's appointment link is set null via `onDelete: SetNull`, and the item status should be reverted to `planned` by the service layer.
|
|
322
|
+
|
|
323
|
+
**Cascade delete on plan.** Deleting a treatment plan removes all its items. This is intentional -- a plan and its items are a single logical unit. Items have no independent existence outside their plan.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 5. Treatment Plan Workflow
|
|
328
|
+
|
|
329
|
+
### 5.1 Full Lifecycle
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
Doctor examines patient
|
|
333
|
+
|
|
|
334
|
+
v
|
|
335
|
+
Doctor creates treatment plan with name and items from catalog
|
|
336
|
+
| POST /patients/:patientId/treatment-plans
|
|
337
|
+
| Body: { name, notes, items: [{ treatment_id, tooth_number, notes }] }
|
|
338
|
+
|
|
|
339
|
+
v
|
|
340
|
+
Plan created with status = active, items with status = planned
|
|
341
|
+
|
|
|
342
|
+
v
|
|
343
|
+
Receptionist books appointments for items
|
|
344
|
+
| PUT /treatment-plans/:id/items/:itemId
|
|
345
|
+
| Body: { appointment_id: "..." }
|
|
346
|
+
| Item status -> scheduled
|
|
347
|
+
|
|
|
348
|
+
v
|
|
349
|
+
Patient attends appointment
|
|
350
|
+
| Doctor marks item as in_progress (optional)
|
|
351
|
+
| PUT /treatment-plans/:id/items/:itemId
|
|
352
|
+
| Body: { status: "in_progress" }
|
|
353
|
+
|
|
|
354
|
+
v
|
|
355
|
+
Treatment completed
|
|
356
|
+
| PUT /treatment-plans/:id/items/:itemId
|
|
357
|
+
| Body: { status: "completed" }
|
|
358
|
+
| Item status -> completed, item_completed_at = now()
|
|
359
|
+
| If treatment.recall_days set -> schedule recall job
|
|
360
|
+
|
|
|
361
|
+
v
|
|
362
|
+
Repeat for remaining items
|
|
363
|
+
|
|
|
364
|
+
v
|
|
365
|
+
All items completed or cancelled
|
|
366
|
+
| Service checks: are all items in terminal state (completed/cancelled)?
|
|
367
|
+
| If yes -> plan status automatically set to completed
|
|
368
|
+
|
|
|
369
|
+
v
|
|
370
|
+
Plan completed
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 5.2 Sequence Diagram: Plan Creation
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
Doctor (Frontend) API TreatmentPlansService DB
|
|
377
|
+
| | | |
|
|
378
|
+
| Create plan | | |
|
|
379
|
+
| POST /patients/ | | |
|
|
380
|
+
| :patientId/ | | |
|
|
381
|
+
| treatment-plans | | |
|
|
382
|
+
| {name, items[]} | | |
|
|
383
|
+
|---------------------->| | |
|
|
384
|
+
| | createPlan(dto) | |
|
|
385
|
+
| |--------------------------->| |
|
|
386
|
+
| | | |
|
|
387
|
+
| | | Validate patient |
|
|
388
|
+
| | | belongs to tenant |
|
|
389
|
+
| | |--------------------->|
|
|
390
|
+
| | | |
|
|
391
|
+
| | | Validate each |
|
|
392
|
+
| | | treatment_id exists |
|
|
393
|
+
| | | and is active |
|
|
394
|
+
| | |--------------------->|
|
|
395
|
+
| | | |
|
|
396
|
+
| | | Transaction: |
|
|
397
|
+
| | | 1. Create plan |
|
|
398
|
+
| | | 2. Create items |
|
|
399
|
+
| | | (copy name from |
|
|
400
|
+
| | | treatment) |
|
|
401
|
+
| | |--------------------->|
|
|
402
|
+
| | | plan + items |
|
|
403
|
+
| | |<---------------------|
|
|
404
|
+
| | | |
|
|
405
|
+
| | plan with items | |
|
|
406
|
+
| |<---------------------------| |
|
|
407
|
+
| 201 Created | | |
|
|
408
|
+
|<----------------------| | |
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 5.3 Sequence Diagram: Item Completion
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
Doctor (Frontend) API TreatmentPlansService DB BullMQ
|
|
415
|
+
| | | | |
|
|
416
|
+
| Complete item | | | |
|
|
417
|
+
| PUT /treatment-plans | | | |
|
|
418
|
+
| /:id/items/:itemId | | | |
|
|
419
|
+
| {status: "completed"}| | | |
|
|
420
|
+
|---------------------->| | | |
|
|
421
|
+
| | updateItem(itemId, dto) | | |
|
|
422
|
+
| |--------------------------->| | |
|
|
423
|
+
| | | | |
|
|
424
|
+
| | | Update item: |
|
|
425
|
+
| | | status = completed |
|
|
426
|
+
| | | completed_at = now |
|
|
427
|
+
| | |--------------------->|
|
|
428
|
+
| | | | |
|
|
429
|
+
| | | Load treatment |
|
|
430
|
+
| | | (check recall_days) |
|
|
431
|
+
| | |--------------------->|
|
|
432
|
+
| | | | |
|
|
433
|
+
| | | recall_days = 180 | |
|
|
434
|
+
| | | | |
|
|
435
|
+
| | | Schedule recall job ----------------->|
|
|
436
|
+
| | | delay = 180 days | |
|
|
437
|
+
| | | | |
|
|
438
|
+
| | | Check: all items | |
|
|
439
|
+
| | | in terminal state? | |
|
|
440
|
+
| | |--------------------->|
|
|
441
|
+
| | | yes (all done) | |
|
|
442
|
+
| | |<---------------------| |
|
|
443
|
+
| | | | |
|
|
444
|
+
| | | Update plan: | |
|
|
445
|
+
| | | status = completed | |
|
|
446
|
+
| | |--------------------->|
|
|
447
|
+
| | | | |
|
|
448
|
+
| | updated item + plan | | |
|
|
449
|
+
| |<---------------------------| | |
|
|
450
|
+
| 200 OK | | | |
|
|
451
|
+
|<----------------------| | | |
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### 5.4 Automatic Plan Completion
|
|
455
|
+
|
|
456
|
+
The service layer checks for automatic plan completion on every item status update:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
async updateItemStatus(itemId: string, newStatus: TreatmentPlanItemStatus): Promise<void> {
|
|
460
|
+
// ... update the item ...
|
|
461
|
+
|
|
462
|
+
// Check if all items are in a terminal state
|
|
463
|
+
const allItems = await this.prisma.treatmentPlanItem.findMany({
|
|
464
|
+
where: { item_plan_id: item.item_plan_id },
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const allTerminal = allItems.every(
|
|
468
|
+
i => i.item_status === 'completed' || i.item_status === 'cancelled'
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
if (allTerminal) {
|
|
472
|
+
await this.prisma.treatmentPlan.update({
|
|
473
|
+
where: { plan_id: item.item_plan_id },
|
|
474
|
+
data: { plan_status: 'completed' },
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
This runs inside the same database transaction as the item update. No separate background job needed -- the check is a simple array scan over a small number of items (typical plans have 2-10 items).
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## 6. AI Integration
|
|
485
|
+
|
|
486
|
+
Treatment plans interact with the AI agent at several points. These integrations build on the existing client tools system (doc 10).
|
|
487
|
+
|
|
488
|
+
### 6.1 Appointment Booking with Plan Item Link
|
|
489
|
+
|
|
490
|
+
When the AI books an appointment via the `reserve_slot` tool, it can optionally link the appointment to a treatment plan item. This enables the system to automatically update the item status from `planned` to `scheduled`.
|
|
491
|
+
|
|
492
|
+
```
|
|
493
|
+
AI calls reserve_slot({
|
|
494
|
+
patient_name: "Ayse Yilmaz",
|
|
495
|
+
iso_start: "2026-04-05T14:00:00+03:00",
|
|
496
|
+
iso_end: "2026-04-05T14:45:00+03:00",
|
|
497
|
+
treatment_plan_item_id: "item-uuid-here" // Optional new parameter
|
|
498
|
+
})
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
When `treatment_plan_item_id` is provided in the `reserve_slot` call:
|
|
502
|
+
|
|
503
|
+
1. The appointment is created as normal (Google Calendar event + DB record).
|
|
504
|
+
2. The plan item's `item_appointment_id` is set to the new appointment ID.
|
|
505
|
+
3. The plan item's `item_status` is updated from `planned` to `scheduled`.
|
|
506
|
+
|
|
507
|
+
This is a backward-compatible extension -- existing `reserve_slot` calls without `treatment_plan_item_id` continue to work unchanged.
|
|
508
|
+
|
|
509
|
+
### 6.2 Doctor-Specific Availability
|
|
510
|
+
|
|
511
|
+
When a treatment plan has a `plan_doctor_id`, the `check_availability` tool should consider that doctor's working hours. The AI context includes the doctor's user ID, and the availability calculation filters by the doctor's schedule in addition to the clinic's calendar.
|
|
512
|
+
|
|
513
|
+
This does not change the `check_availability` tool interface -- the doctor filter is applied server-side based on the tool context (which patient, which plan, which doctor).
|
|
514
|
+
|
|
515
|
+
### 6.3 Patient History Context
|
|
516
|
+
|
|
517
|
+
The `check_patient_history` tool returns an enriched patient context that now includes treatment plans:
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
// Added to PatientContext returned by check_patient_history
|
|
521
|
+
{
|
|
522
|
+
// ... existing fields (name, phone, sessions, notes, tags) ...
|
|
523
|
+
treatment_plans: [
|
|
524
|
+
{
|
|
525
|
+
name: "Tooth 14 Restoration",
|
|
526
|
+
status: "active",
|
|
527
|
+
doctor: "Dr. Mehmet Kaya",
|
|
528
|
+
items: [
|
|
529
|
+
{ name: "Root Canal", status: "completed", tooth: "14" },
|
|
530
|
+
{ name: "Crown Prep", status: "scheduled", appointment: "2026-04-05 14:00" },
|
|
531
|
+
{ name: "Crown Fit", status: "planned" }
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
This gives the AI full awareness of the patient's treatment journey. The AI can say: "I see you have a crown preparation appointment next week for tooth 14. Would you like to schedule the crown fitting after that?"
|
|
539
|
+
|
|
540
|
+
### 6.4 Future: Next-Item Slot Suggestion
|
|
541
|
+
|
|
542
|
+
When a patient contacts the clinic and has an active plan with `planned` items, the AI could proactively suggest booking the next unscheduled item:
|
|
543
|
+
|
|
544
|
+
```
|
|
545
|
+
AI: "Merhaba Ayse Hanım! Diş 14 restorasyon planınızda kron takma işlemi henüz
|
|
546
|
+
planlanmamış. Bu işlem için bir randevu ayarlamak ister misiniz?"
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
This is a future enhancement, not part of the initial implementation. It requires the AI prompt to include treatment plan context via `dynamic_variables` or `contextual_update`.
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## 7. API Endpoints
|
|
554
|
+
|
|
555
|
+
### 7.1 Treatments (Service Catalog)
|
|
556
|
+
|
|
557
|
+
All treatment endpoints are tenant-scoped. Clinic filtering is optional -- if `clinicId` query parameter is provided, results include both tenant-level treatments (null `treatment_clinic_id`) and treatments scoped to that clinic.
|
|
558
|
+
|
|
559
|
+
| Method | Path | Permission | Description |
|
|
560
|
+
|--------|------|------------|-------------|
|
|
561
|
+
| GET | `/treatments` | `treatments:read` | List treatments. Query params: `clinicId`, `category`, `active` (default true), `search`, `page`, `limit`. Returns tenant-level + clinic-specific treatments |
|
|
562
|
+
| POST | `/treatments` | `treatments:manage` | Create a treatment. Body: `{ name, category, description, duration_minutes, price, currency, clinic_id, recall_days, sort_order }` |
|
|
563
|
+
| GET | `/treatments/:id` | `treatments:read` | Treatment detail with usage stats (how many plan items reference this treatment) |
|
|
564
|
+
| PUT | `/treatments/:id` | `treatments:manage` | Update treatment fields. Updating `name` does not change existing plan item names (denormalized) |
|
|
565
|
+
| DELETE | `/treatments/:id` | `treatments:manage` | Soft-delete: sets `treatment_is_active = false`. Returns 409 Conflict if treatment has active (non-completed, non-cancelled) plan items -- require user confirmation |
|
|
566
|
+
| GET | `/treatments/categories` | `treatments:read` | List unique category strings across the tenant's treatments. For autocomplete in the "category" field. Returns `string[]` |
|
|
567
|
+
|
|
568
|
+
### 7.2 Treatment Plans
|
|
569
|
+
|
|
570
|
+
Plan endpoints are nested under patients for creation and listing, but accessed directly by ID for updates. All endpoints enforce tenant isolation and clinic access via guards.
|
|
571
|
+
|
|
572
|
+
| Method | Path | Permission | Description |
|
|
573
|
+
|--------|------|------------|-------------|
|
|
574
|
+
| GET | `/patients/:patientId/treatment-plans` | `patients:read` | List plans for a patient. Query params: `status` (active/completed/cancelled), `page`, `limit`. Returns plans with items included |
|
|
575
|
+
| POST | `/patients/:patientId/treatment-plans` | `treatment_plans:manage` | Create plan with items in a single request. Body: `{ name, clinic_id, doctor_id, notes, items: [{ treatment_id, name, tooth_number, notes, sort_order }] }`. Items with `treatment_id` auto-populate `item_name` from the catalog |
|
|
576
|
+
| GET | `/treatment-plans/:id` | `patients:read` | Plan detail with all items, linked treatments, linked appointments, and doctor info |
|
|
577
|
+
| PUT | `/treatment-plans/:id` | `treatment_plans:manage` | Update plan fields: `name`, `notes`, `status`, `doctor_id`. Manually setting `status = completed` is allowed (doctor override) |
|
|
578
|
+
| DELETE | `/treatment-plans/:id` | `treatment_plans:manage` | Delete plan and all items. Returns 409 Conflict if any items have `status = completed` -- completed work should not be silently deleted |
|
|
579
|
+
| POST | `/treatment-plans/:id/items` | `treatment_plans:manage` | Add a new item to an existing plan. Body: `{ treatment_id, name, tooth_number, notes, sort_order }` |
|
|
580
|
+
| PUT | `/treatment-plans/:id/items/:itemId` | `treatment_plans:manage` | Update item: `status`, `notes`, `appointment_id`, `tooth_number`, `sort_order`. Status changes trigger side effects (recall scheduling, auto-plan-completion) |
|
|
581
|
+
| DELETE | `/treatment-plans/:id/items/:itemId` | `treatment_plans:manage` | Remove item from plan. Returns 409 if item `status = completed` |
|
|
582
|
+
|
|
583
|
+
### 7.3 Response Formats
|
|
584
|
+
|
|
585
|
+
**Plan list response** (GET `/patients/:patientId/treatment-plans`):
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
{
|
|
589
|
+
data: [
|
|
590
|
+
{
|
|
591
|
+
plan_id: "uuid",
|
|
592
|
+
plan_name: "Tooth 14 Restoration",
|
|
593
|
+
plan_status: "active",
|
|
594
|
+
plan_notes: "",
|
|
595
|
+
plan_created_at: "2026-03-15T10:00:00Z",
|
|
596
|
+
doctor: { user_id: "uuid", user_full_name: "Dr. Mehmet Kaya" } | null,
|
|
597
|
+
items: [
|
|
598
|
+
{
|
|
599
|
+
item_id: "uuid",
|
|
600
|
+
item_name: "Root Canal",
|
|
601
|
+
item_status: "completed",
|
|
602
|
+
item_tooth_number: "14",
|
|
603
|
+
item_completed_at: "2026-03-15T11:00:00Z",
|
|
604
|
+
item_sort_order: 0,
|
|
605
|
+
treatment: { treatment_id: "uuid", treatment_name: "Root Canal", treatment_price: 3000 } | null,
|
|
606
|
+
appointment: { appt_id: "uuid", appt_start_time: "2026-03-15T10:00:00Z" } | null,
|
|
607
|
+
},
|
|
608
|
+
// ...
|
|
609
|
+
],
|
|
610
|
+
_counts: { total: 3, completed: 1, scheduled: 1, planned: 1, cancelled: 0 }
|
|
611
|
+
}
|
|
612
|
+
],
|
|
613
|
+
meta: { total: 1, page: 1, limit: 20 }
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
The `_counts` field provides item status breakdown without requiring the frontend to count items client-side.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## 8. Permissions
|
|
622
|
+
|
|
623
|
+
### New Permission Strings
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
treatments:read - View treatment catalog
|
|
627
|
+
treatments:manage - Create, update, soft-delete treatments
|
|
628
|
+
treatment_plans:manage - Create, update, delete treatment plans and items
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Treatment plan reading is covered by `patients:read` -- if you can view a patient, you can view their treatment plans. This is the same pattern used for patient photos (doc 21).
|
|
632
|
+
|
|
633
|
+
### Role Mapping
|
|
634
|
+
|
|
635
|
+
| Role | `treatments:read` | `treatments:manage` | `patients:read` | `treatment_plans:manage` |
|
|
636
|
+
|------|-------------------|---------------------|-----------------|--------------------------|
|
|
637
|
+
| owner | Yes | Yes | Yes | Yes |
|
|
638
|
+
| admin | Yes | Yes | Yes | Yes |
|
|
639
|
+
| doctor | Yes | No | Yes | Yes |
|
|
640
|
+
| receptionist | Yes | No | Yes | No |
|
|
641
|
+
|
|
642
|
+
**Rationale:**
|
|
643
|
+
|
|
644
|
+
- **owner/admin** -- Full access to everything. They manage the clinic's service catalog and can create/edit treatment plans.
|
|
645
|
+
- **doctor** -- Can read the treatment catalog but cannot modify it (catalog management is an admin task). Can create and manage treatment plans (this is core clinical work -- doctors prescribe treatment plans).
|
|
646
|
+
- **receptionist** -- Can read the catalog (needed to know treatment durations when booking appointments) and view treatment plans (needed to understand what appointments to book). Cannot create or modify plans -- that is the doctor's responsibility.
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## 9. Recall Automation (P1 -- Design Reference)
|
|
651
|
+
|
|
652
|
+
This section describes how `treatment_recall_days` will be used for automated patient recall. This is P1 (not part of initial implementation) but the data model is designed to support it.
|
|
653
|
+
|
|
654
|
+
### How It Works
|
|
655
|
+
|
|
656
|
+
When a treatment plan item is marked `completed`, the service checks if the linked treatment has `treatment_recall_days` set. If it does, a BullMQ delayed job is scheduled:
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
Item completed (status -> completed, completed_at = now())
|
|
660
|
+
|
|
|
661
|
+
v
|
|
662
|
+
Load treatment: treatment.recall_days = 180
|
|
663
|
+
|
|
|
664
|
+
v
|
|
665
|
+
Schedule BullMQ delayed job:
|
|
666
|
+
Queue: treatment:recall
|
|
667
|
+
Delay: 180 days (15,552,000,000 ms)
|
|
668
|
+
JobId: "recall:{itemId}" (prevents duplicates)
|
|
669
|
+
Data: {
|
|
670
|
+
tenantId, clinicId, patientId, planId, itemId,
|
|
671
|
+
treatmentId, treatmentName, recallDays: 180
|
|
672
|
+
}
|
|
673
|
+
|
|
|
674
|
+
v
|
|
675
|
+
... 180 days later ...
|
|
676
|
+
|
|
|
677
|
+
v
|
|
678
|
+
Job fires:
|
|
679
|
+
1. Check patient still exists and has not had this treatment repeated
|
|
680
|
+
2. Create a WhatsApp reminder message or outbound call
|
|
681
|
+
3. Optionally create a new outbound campaign entry for batch recall
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### Integration with Outbound Campaigns
|
|
685
|
+
|
|
686
|
+
Recall jobs can feed into the existing outbound campaigns system (doc 09). Instead of sending individual reminders, the system can batch recall-eligible patients into an outbound campaign:
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
Daily BullMQ cron job: treatment:recall-batch (09:00 Istanbul time)
|
|
690
|
+
|
|
|
691
|
+
v
|
|
692
|
+
Query: treatment_plan_items WHERE
|
|
693
|
+
item_status = 'completed'
|
|
694
|
+
AND item_completed_at + treatment.recall_days <= now()
|
|
695
|
+
AND no existing recall campaign entry for this item
|
|
696
|
+
|
|
|
697
|
+
v
|
|
698
|
+
Group by tenant + clinic + treatment
|
|
699
|
+
|
|
|
700
|
+
v
|
|
701
|
+
For each group: create outbound campaign entries
|
|
702
|
+
or send individual WhatsApp reminders (based on tenant config)
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Example Recall Scenarios
|
|
706
|
+
|
|
707
|
+
| Treatment | Recall Days | Trigger |
|
|
708
|
+
|-----------|-------------|---------|
|
|
709
|
+
| Dental Cleaning | 180 | "Merhaba, son diş temizliğinizin üzerinden 6 ay geçti. Yeni bir randevu almak ister misiniz?" |
|
|
710
|
+
| Annual Checkup | 365 | "Yıllık kontrol zamanınız geldi. Size uygun bir randevu ayarlayalım mı?" |
|
|
711
|
+
| Botox | 120 | "Botox uygulamanızın tazeleme zamanı yaklaşıyor. Randevu için bize ulaşabilirsiniz." |
|
|
712
|
+
| Teeth Whitening | 365 | "Diş beyazlatma işleminizin üzerinden 1 yıl geçti. Yenileme seansı düşünür müsünüz?" |
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## 10. NestJS Module Structure
|
|
717
|
+
|
|
718
|
+
Treatments and treatment plans live in a single `TreatmentsModule`. They are tightly coupled -- plan items reference treatments, and the plan workflow depends on treatment properties (duration, recall days). Splitting them into two modules would create circular dependencies without meaningful separation of concerns.
|
|
719
|
+
|
|
720
|
+
```
|
|
721
|
+
src/treatments/
|
|
722
|
+
treatments.module.ts # Imports PrismaModule, BullModule (for recall queue)
|
|
723
|
+
treatments.controller.ts # /treatments/* endpoints (catalog CRUD)
|
|
724
|
+
treatments.service.ts # Treatment catalog CRUD, category listing, soft-delete
|
|
725
|
+
treatment-plans.controller.ts # /treatment-plans/* and /patients/:id/treatment-plans
|
|
726
|
+
treatment-plans.service.ts # Plan + item CRUD, status management, auto-completion, recall scheduling
|
|
727
|
+
dto/
|
|
728
|
+
create-treatment.dto.ts # name, category, duration, price, clinic_id, recall_days
|
|
729
|
+
update-treatment.dto.ts # PartialType of create
|
|
730
|
+
create-treatment-plan.dto.ts # name, patient_id, doctor_id, clinic_id, notes, items[]
|
|
731
|
+
update-treatment-plan.dto.ts # name, notes, status, doctor_id
|
|
732
|
+
create-plan-item.dto.ts # treatment_id, name, tooth_number, notes, sort_order
|
|
733
|
+
update-plan-item.dto.ts # status, notes, appointment_id, tooth_number, sort_order
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Module Registration
|
|
737
|
+
|
|
738
|
+
```typescript
|
|
739
|
+
// src/treatments/treatments.module.ts
|
|
740
|
+
|
|
741
|
+
@Module({
|
|
742
|
+
imports: [
|
|
743
|
+
PrismaModule,
|
|
744
|
+
BullModule.registerQueue({ name: 'treatment:recall' }),
|
|
745
|
+
],
|
|
746
|
+
controllers: [
|
|
747
|
+
TreatmentsController,
|
|
748
|
+
TreatmentPlansController,
|
|
749
|
+
],
|
|
750
|
+
providers: [
|
|
751
|
+
TreatmentsService,
|
|
752
|
+
TreatmentPlansService,
|
|
753
|
+
],
|
|
754
|
+
exports: [
|
|
755
|
+
TreatmentsService,
|
|
756
|
+
TreatmentPlansService, // Exported for AI tools module to query plans
|
|
757
|
+
],
|
|
758
|
+
})
|
|
759
|
+
export class TreatmentsModule {}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Service Dependencies
|
|
763
|
+
|
|
764
|
+
```
|
|
765
|
+
TreatmentsService
|
|
766
|
+
<- PrismaService (CRUD)
|
|
767
|
+
|
|
768
|
+
TreatmentPlansService
|
|
769
|
+
<- PrismaService (CRUD)
|
|
770
|
+
<- TreatmentsService (validate treatment_id, get recall_days)
|
|
771
|
+
<- BullMQ Queue 'treatment:recall' (schedule recall jobs)
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
The `TreatmentPlansService` is exported so the `ToolsModule` can query treatment plans when building patient context for `check_patient_history` and when linking appointments in `reserve_slot`.
|