feat: team module, multi-stage Dockerfile, doctor utils, AI config overhaul

- Team module: POST /api/team/members (in-place employee creation with
  temp password + Redis cache), PUT /api/team/members/:id, GET temp
  password endpoint. Uses signUpInWorkspace — no email invites.
- Dockerfile: rewritten as multi-stage build (builder + runtime) so
  native modules compile for target arch. Fixes darwin→linux crash.
- .dockerignore: exclude dist, node_modules, .env, .git, data/
- package-lock.json: regenerated against public npmjs.org (was
  pointing at localhost:4873 Verdaccio — broke docker builds)
- Doctor utils: shared DOCTOR_VISIT_SLOTS_FRAGMENT + normalizeDoctors
  helper for visit-slot-aware queries across 6 consumers
- AI config: full admin CRUD (GET/PUT/POST reset), workspace-scoped
  setup-state with workspace ID isolation, AI prompt defaults overhaul
- Agent config: camelCase field fix for SDK-synced workspaces
- Session service: workspace-scoped Redis key prefixing for setup state
- Recordings/supervisor/widget services: updated to use doctor-utils
  shared fragments instead of inline visitingHours queries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 08:37:58 +05:30
parent eacfce6970
commit 695f119c2b
25 changed files with 2756 additions and 1936 deletions

View File

@@ -108,11 +108,14 @@ export class SupervisorService implements OnModuleInit {
}
async getTeamPerformance(date: string): Promise<any> {
// Get all agents from platform
// Get all agents from platform. Field names are label-derived
// camelCase on the current platform schema — see
// agent-config.service.ts for the canonical explanation of the
// legacy lowercase names that used to exist on staging.
const agentData = await this.platform.query<any>(
`{ agents(first: 20) { edges { node {
id name ozonetelagentid npsscore
maxidleminutes minnpsthreshold minconversionpercent
id name ozonetelAgentId npsScore
maxIdleMinutes minNpsThreshold minConversion
} } } }`,
);
const agents = agentData?.agents?.edges?.map((e: any) => e.node) ?? [];
@@ -120,12 +123,12 @@ export class SupervisorService implements OnModuleInit {
// Fetch Ozonetel time summary per agent
const summaries = await Promise.all(
agents.map(async (agent: any) => {
if (!agent.ozonetelagentid) return { ...agent, timeBreakdown: null };
if (!agent.ozonetelAgentId) return { ...agent, timeBreakdown: null };
try {
const summary = await this.ozonetel.getAgentSummary(agent.ozonetelagentid, date);
const summary = await this.ozonetel.getAgentSummary(agent.ozonetelAgentId, date);
return { ...agent, timeBreakdown: summary };
} catch (err) {
this.logger.warn(`Failed to get summary for ${agent.ozonetelagentid}: ${err}`);
this.logger.warn(`Failed to get summary for ${agent.ozonetelAgentId}: ${err}`);
return { ...agent, timeBreakdown: null };
}
}),