dialog-survey 1.1.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/CHANGELOG.md +95 -0
- package/README.md +242 -0
- package/VERSION +1 -0
- package/dist/api/admin-templates.d.ts +15 -0
- package/dist/api/admin-templates.d.ts.map +1 -0
- package/dist/api/admin-templates.js +611 -0
- package/dist/api/admin-templates.js.map +1 -0
- package/dist/api/analysis.d.ts +8 -0
- package/dist/api/analysis.d.ts.map +1 -0
- package/dist/api/analysis.js +106 -0
- package/dist/api/analysis.js.map +1 -0
- package/dist/api/health.d.ts +6 -0
- package/dist/api/health.d.ts.map +1 -0
- package/dist/api/health.js +116 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/plans.d.ts +3 -0
- package/dist/api/plans.d.ts.map +1 -0
- package/dist/api/plans.js +184 -0
- package/dist/api/plans.js.map +1 -0
- package/dist/api/templates.d.ts +9 -0
- package/dist/api/templates.d.ts.map +1 -0
- package/dist/api/templates.js +211 -0
- package/dist/api/templates.js.map +1 -0
- package/dist/core/graph.d.ts +7 -0
- package/dist/core/graph.d.ts.map +1 -0
- package/dist/core/graph.js +31 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/nodes/analyzing.d.ts +3 -0
- package/dist/core/nodes/analyzing.d.ts.map +1 -0
- package/dist/core/nodes/analyzing.js +38 -0
- package/dist/core/nodes/analyzing.js.map +1 -0
- package/dist/core/nodes/completed.d.ts +3 -0
- package/dist/core/nodes/completed.d.ts.map +1 -0
- package/dist/core/nodes/completed.js +8 -0
- package/dist/core/nodes/completed.js.map +1 -0
- package/dist/core/nodes/interviewing.d.ts +13 -0
- package/dist/core/nodes/interviewing.d.ts.map +1 -0
- package/dist/core/nodes/interviewing.js +130 -0
- package/dist/core/nodes/interviewing.js.map +1 -0
- package/dist/core/nodes/planning.d.ts +15 -0
- package/dist/core/nodes/planning.d.ts.map +1 -0
- package/dist/core/nodes/planning.js +40 -0
- package/dist/core/nodes/planning.js.map +1 -0
- package/dist/core/types/index.d.ts +69 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +49 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/domains/interview/entities/interview.entity.d.ts +21 -0
- package/dist/domains/interview/entities/interview.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/interview.entity.js +20 -0
- package/dist/domains/interview/entities/interview.entity.js.map +1 -0
- package/dist/domains/interview/entities/message.entity.d.ts +15 -0
- package/dist/domains/interview/entities/message.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/message.entity.js +11 -0
- package/dist/domains/interview/entities/message.entity.js.map +1 -0
- package/dist/domains/interview/entities/response.entity.d.ts +17 -0
- package/dist/domains/interview/entities/response.entity.d.ts.map +1 -0
- package/dist/domains/interview/entities/response.entity.js +12 -0
- package/dist/domains/interview/entities/response.entity.js.map +1 -0
- package/dist/domains/interview/services/interview.domain.d.ts +8 -0
- package/dist/domains/interview/services/interview.domain.d.ts.map +1 -0
- package/dist/domains/interview/services/interview.domain.js +30 -0
- package/dist/domains/interview/services/interview.domain.js.map +1 -0
- package/dist/integrations/dingtalk/client.d.ts +32 -0
- package/dist/integrations/dingtalk/client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/client.js +89 -0
- package/dist/integrations/dingtalk/client.js.map +1 -0
- package/dist/integrations/dingtalk/message-sender.d.ts +24 -0
- package/dist/integrations/dingtalk/message-sender.d.ts.map +1 -0
- package/dist/integrations/dingtalk/message-sender.js +81 -0
- package/dist/integrations/dingtalk/message-sender.js.map +1 -0
- package/dist/integrations/dingtalk/middleware.d.ts +4 -0
- package/dist/integrations/dingtalk/middleware.d.ts.map +1 -0
- package/dist/integrations/dingtalk/middleware.js +29 -0
- package/dist/integrations/dingtalk/middleware.js.map +1 -0
- package/dist/integrations/dingtalk/stream-client.d.ts +112 -0
- package/dist/integrations/dingtalk/stream-client.d.ts.map +1 -0
- package/dist/integrations/dingtalk/stream-client.js +305 -0
- package/dist/integrations/dingtalk/stream-client.js.map +1 -0
- package/dist/integrations/dingtalk/token-manager.d.ts +22 -0
- package/dist/integrations/dingtalk/token-manager.d.ts.map +1 -0
- package/dist/integrations/dingtalk/token-manager.js +109 -0
- package/dist/integrations/dingtalk/token-manager.js.map +1 -0
- package/dist/integrations/llm/alibaba.d.ts +10 -0
- package/dist/integrations/llm/alibaba.d.ts.map +1 -0
- package/dist/integrations/llm/alibaba.js +60 -0
- package/dist/integrations/llm/alibaba.js.map +1 -0
- package/dist/integrations/llm/base.d.ts +28 -0
- package/dist/integrations/llm/base.d.ts.map +1 -0
- package/dist/integrations/llm/base.js +2 -0
- package/dist/integrations/llm/base.js.map +1 -0
- package/dist/integrations/llm/volcengine.d.ts +12 -0
- package/dist/integrations/llm/volcengine.d.ts.map +1 -0
- package/dist/integrations/llm/volcengine.js +80 -0
- package/dist/integrations/llm/volcengine.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +3 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +37 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/api-auth.d.ts +10 -0
- package/dist/middleware/api-auth.d.ts.map +1 -0
- package/dist/middleware/api-auth.js +30 -0
- package/dist/middleware/api-auth.js.map +1 -0
- package/dist/repositories/interview-state-mapper.d.ts +28 -0
- package/dist/repositories/interview-state-mapper.d.ts.map +1 -0
- package/dist/repositories/interview-state-mapper.js +32 -0
- package/dist/repositories/interview-state-mapper.js.map +1 -0
- package/dist/repositories/interview-state.repository.d.ts +34 -0
- package/dist/repositories/interview-state.repository.d.ts.map +1 -0
- package/dist/repositories/interview-state.repository.js +220 -0
- package/dist/repositories/interview-state.repository.js.map +1 -0
- package/dist/repositories/interview.repository.d.ts +29 -0
- package/dist/repositories/interview.repository.d.ts.map +1 -0
- package/dist/repositories/interview.repository.js +36 -0
- package/dist/repositories/interview.repository.js.map +1 -0
- package/dist/repositories/message.repository.d.ts +17 -0
- package/dist/repositories/message.repository.d.ts.map +1 -0
- package/dist/repositories/message.repository.js +37 -0
- package/dist/repositories/message.repository.js.map +1 -0
- package/dist/repositories/template.repository.d.ts +117 -0
- package/dist/repositories/template.repository.d.ts.map +1 -0
- package/dist/repositories/template.repository.js +280 -0
- package/dist/repositories/template.repository.js.map +1 -0
- package/dist/schemas/dimensions.d.ts +61 -0
- package/dist/schemas/dimensions.d.ts.map +1 -0
- package/dist/schemas/dimensions.js +44 -0
- package/dist/schemas/dimensions.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analysis.service.d.ts +81 -0
- package/dist/services/analysis.service.d.ts.map +1 -0
- package/dist/services/analysis.service.js +229 -0
- package/dist/services/analysis.service.js.map +1 -0
- package/dist/services/analytics.service.d.ts +44 -0
- package/dist/services/analytics.service.d.ts.map +1 -0
- package/dist/services/analytics.service.js +222 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/asr.service.d.ts +9 -0
- package/dist/services/asr.service.d.ts.map +1 -0
- package/dist/services/asr.service.js +41 -0
- package/dist/services/asr.service.js.map +1 -0
- package/dist/services/audit-cleanup.service.d.ts +9 -0
- package/dist/services/audit-cleanup.service.d.ts.map +1 -0
- package/dist/services/audit-cleanup.service.js +27 -0
- package/dist/services/audit-cleanup.service.js.map +1 -0
- package/dist/services/batch-aggregation.service.d.ts +34 -0
- package/dist/services/batch-aggregation.service.d.ts.map +1 -0
- package/dist/services/batch-aggregation.service.js +91 -0
- package/dist/services/batch-aggregation.service.js.map +1 -0
- package/dist/services/conversation-engine.d.ts +16 -0
- package/dist/services/conversation-engine.d.ts.map +1 -0
- package/dist/services/conversation-engine.js +49 -0
- package/dist/services/conversation-engine.js.map +1 -0
- package/dist/services/dead-letter.service.d.ts +12 -0
- package/dist/services/dead-letter.service.d.ts.map +1 -0
- package/dist/services/dead-letter.service.js +24 -0
- package/dist/services/dead-letter.service.js.map +1 -0
- package/dist/services/followup.service.d.ts +18 -0
- package/dist/services/followup.service.d.ts.map +1 -0
- package/dist/services/followup.service.js +146 -0
- package/dist/services/followup.service.js.map +1 -0
- package/dist/services/interview-plan-base.service.d.ts +226 -0
- package/dist/services/interview-plan-base.service.d.ts.map +1 -0
- package/dist/services/interview-plan-base.service.js +217 -0
- package/dist/services/interview-plan-base.service.js.map +1 -0
- package/dist/services/interview-plan-members.service.d.ts +45 -0
- package/dist/services/interview-plan-members.service.d.ts.map +1 -0
- package/dist/services/interview-plan-members.service.js +237 -0
- package/dist/services/interview-plan-members.service.js.map +1 -0
- package/dist/services/interview-plan-send.service.d.ts +27 -0
- package/dist/services/interview-plan-send.service.d.ts.map +1 -0
- package/dist/services/interview-plan-send.service.js +250 -0
- package/dist/services/interview-plan-send.service.js.map +1 -0
- package/dist/services/interview-plan.service.d.ts +8 -0
- package/dist/services/interview-plan.service.d.ts.map +1 -0
- package/dist/services/interview-plan.service.js +8 -0
- package/dist/services/interview-plan.service.js.map +1 -0
- package/dist/services/llm-clustering.d.ts +16 -0
- package/dist/services/llm-clustering.d.ts.map +1 -0
- package/dist/services/llm-clustering.js +91 -0
- package/dist/services/llm-clustering.js.map +1 -0
- package/dist/services/prompt.service.d.ts +13 -0
- package/dist/services/prompt.service.d.ts.map +1 -0
- package/dist/services/prompt.service.js +151 -0
- package/dist/services/prompt.service.js.map +1 -0
- package/dist/services/report.service.d.ts +28 -0
- package/dist/services/report.service.d.ts.map +1 -0
- package/dist/services/report.service.js +169 -0
- package/dist/services/report.service.js.map +1 -0
- package/dist/services/stream-message-utils.d.ts +31 -0
- package/dist/services/stream-message-utils.d.ts.map +1 -0
- package/dist/services/stream-message-utils.js +80 -0
- package/dist/services/stream-message-utils.js.map +1 -0
- package/dist/services/stream-message.service.d.ts +29 -0
- package/dist/services/stream-message.service.d.ts.map +1 -0
- package/dist/services/stream-message.service.js +184 -0
- package/dist/services/stream-message.service.js.map +1 -0
- package/dist/services/template-dimension.service.d.ts +21 -0
- package/dist/services/template-dimension.service.d.ts.map +1 -0
- package/dist/services/template-dimension.service.js +9 -0
- package/dist/services/template-dimension.service.js.map +1 -0
- package/dist/utils/db.d.ts +10 -0
- package/dist/utils/db.d.ts.map +1 -0
- package/dist/utils/db.js +21 -0
- package/dist/utils/db.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.d.ts +2 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +39 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/pii-anonymizer.d.ts +2 -0
- package/dist/utils/pii-anonymizer.d.ts.map +1 -0
- package/dist/utils/pii-anonymizer.js +13 -0
- package/dist/utils/pii-anonymizer.js.map +1 -0
- package/dist/utils/retry.d.ts +8 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +28 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/security.d.ts +26 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +99 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +80 -0
- package/prisma/schema.prisma +228 -0
- package/public/css/admin.css +73 -0
- package/public/js/alpine.min.js +2670 -0
- package/public/js/htmx.min.js +3422 -0
- package/scripts/cli.mjs +794 -0
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dialog-survey",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "AI-powered interview robot that conducts async multi-turn interviews via DingTalk",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/boyingliu01/InterviewAgent"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"dialog-survey": "scripts/cli.mjs"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20.0.0"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "tsx --env-file=.env --watch src/server.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"test": "vitest",
|
|
20
|
+
"test:coverage": "vitest --coverage",
|
|
21
|
+
"lint": "biome lint src",
|
|
22
|
+
"lint:fix": "biome lint --write src",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"check": "biome check .",
|
|
25
|
+
"check:fix": "biome check --write .",
|
|
26
|
+
"format": "biome format --write .",
|
|
27
|
+
"lint:prisma": "ast-grep scan --rule .ast-grep/rules/no-raw-prisma-in-api.yml",
|
|
28
|
+
"start": "node dist/src/server.js",
|
|
29
|
+
"deploy": "bash scripts/deploy.sh"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@fastify/cors": "^11.2.0",
|
|
33
|
+
"@fastify/formbody": "^8.0.2",
|
|
34
|
+
"@fastify/static": "^9.1.3",
|
|
35
|
+
"@fastify/view": "^11.1.1",
|
|
36
|
+
"@fastify/websocket": "^11.2.0",
|
|
37
|
+
"@langchain/core": "^1.1.37",
|
|
38
|
+
"@langchain/langgraph": "^1.2.6",
|
|
39
|
+
"@prisma/adapter-pg": "^5.22.0",
|
|
40
|
+
"@prisma/client": "5.22.0",
|
|
41
|
+
"dotenv": "^17.3.1",
|
|
42
|
+
"fastify": "^5.8.4",
|
|
43
|
+
"fastify-type-provider-zod": "^6.1.0",
|
|
44
|
+
"marked": "^14.1.4",
|
|
45
|
+
"node-cron": "^4.2.1",
|
|
46
|
+
"nunjucks": "^3.2.4",
|
|
47
|
+
"p-limit": "^5.0.0",
|
|
48
|
+
"pg": "^8.20.0",
|
|
49
|
+
"pino": "^10.3.1",
|
|
50
|
+
"pino-pretty": "^13.1.3",
|
|
51
|
+
"ws": "^8.20.0",
|
|
52
|
+
"zod": "^4.3.6"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@ast-grep/cli": "^0.43.0",
|
|
56
|
+
"@biomejs/biome": "^1.9.4",
|
|
57
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
58
|
+
"@stryker-mutator/typescript-checker": "^9.6.1",
|
|
59
|
+
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
60
|
+
"@types/node": "^20.12.0",
|
|
61
|
+
"@types/node-cron": "^3.0.11",
|
|
62
|
+
"@types/nunjucks": "^3.2.6",
|
|
63
|
+
"@types/ws": "^8.18.1",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
|
65
|
+
"@typescript-eslint/parser": "^7.4.0",
|
|
66
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
67
|
+
"eslint": "^8.57.0",
|
|
68
|
+
"eslint-config-prettier": "^9.1.0",
|
|
69
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
70
|
+
"jscpd": "^4.2.4",
|
|
71
|
+
"prettier": "^3.2.5",
|
|
72
|
+
"prisma": "5.22.0",
|
|
73
|
+
"tsx": "^4.7.1",
|
|
74
|
+
"typescript": "^5.9.3",
|
|
75
|
+
"vitest": "^4.1.2"
|
|
76
|
+
},
|
|
77
|
+
"overrides": {
|
|
78
|
+
"qs": "6.15.2"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum InterviewStatus {
|
|
11
|
+
PENDING
|
|
12
|
+
ACTIVE
|
|
13
|
+
WAITING
|
|
14
|
+
COMPLETED
|
|
15
|
+
CANCELLED
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
enum SendStatus {
|
|
19
|
+
NOT_SENT
|
|
20
|
+
SENT
|
|
21
|
+
FAILED
|
|
22
|
+
DELIVERED
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
enum TemplateStatus {
|
|
26
|
+
DRAFT
|
|
27
|
+
PUBLISHED
|
|
28
|
+
ARCHIVED
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
model Interview {
|
|
32
|
+
id String @id @default(uuid())
|
|
33
|
+
userId String
|
|
34
|
+
templateId String
|
|
35
|
+
planId String?
|
|
36
|
+
status InterviewStatus @default(PENDING)
|
|
37
|
+
currentQuestion Int @default(0)
|
|
38
|
+
followupCount Int @default(0)
|
|
39
|
+
maxFollowups Int @default(2)
|
|
40
|
+
version Int @default(1) // Optimistic locking
|
|
41
|
+
sendStatus SendStatus @default(NOT_SENT)
|
|
42
|
+
sentAt DateTime?
|
|
43
|
+
sendError String?
|
|
44
|
+
startedAt DateTime?
|
|
45
|
+
completedAt DateTime?
|
|
46
|
+
reportPath String?
|
|
47
|
+
createdAt DateTime @default(now())
|
|
48
|
+
updatedAt DateTime @updatedAt
|
|
49
|
+
|
|
50
|
+
messages Message[]
|
|
51
|
+
responses Response[]
|
|
52
|
+
plan InterviewPlan? @relation(fields: [planId], references: [id])
|
|
53
|
+
template Template @relation(fields: [templateId], references: [id])
|
|
54
|
+
reports AnalysisReport[]
|
|
55
|
+
|
|
56
|
+
@@index([userId])
|
|
57
|
+
@@index([status])
|
|
58
|
+
@@index([planId])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
model Message {
|
|
62
|
+
id String @id @default(uuid())
|
|
63
|
+
interviewId String
|
|
64
|
+
role String
|
|
65
|
+
content String
|
|
66
|
+
messageId String?
|
|
67
|
+
isVoice Boolean @default(false)
|
|
68
|
+
voiceText String?
|
|
69
|
+
createdAt DateTime @default(now())
|
|
70
|
+
|
|
71
|
+
interview Interview @relation(fields: [interviewId], references: [id], onDelete: Cascade)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
model Response {
|
|
75
|
+
id String @id @default(uuid())
|
|
76
|
+
interviewId String
|
|
77
|
+
questionId String
|
|
78
|
+
content String
|
|
79
|
+
isFollowup Boolean @default(false)
|
|
80
|
+
followupDepth Int @default(0)
|
|
81
|
+
createdAt DateTime @default(now())
|
|
82
|
+
|
|
83
|
+
interview Interview @relation(fields: [interviewId], references: [id], onDelete: Cascade)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
model Template {
|
|
87
|
+
id String @id @default(uuid())
|
|
88
|
+
name String
|
|
89
|
+
description String?
|
|
90
|
+
content String // JSON string
|
|
91
|
+
dimensions Json? // NEW: Analysis dimensions for dimension-tag analysis
|
|
92
|
+
analysisConfig Json? // NEW: Analysis configuration
|
|
93
|
+
version Int @default(1)
|
|
94
|
+
status TemplateStatus @default(DRAFT)
|
|
95
|
+
createdAt DateTime @default(now())
|
|
96
|
+
updatedAt DateTime @updatedAt
|
|
97
|
+
createdBy String @default("admin")
|
|
98
|
+
updatedBy String @default("admin")
|
|
99
|
+
|
|
100
|
+
interviews Interview[]
|
|
101
|
+
interviewPlans InterviewPlan[]
|
|
102
|
+
batchReports BatchAnalysisReport[]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
model InterviewPlan {
|
|
106
|
+
id String @id @default(uuid())
|
|
107
|
+
name String
|
|
108
|
+
description String?
|
|
109
|
+
templateId String
|
|
110
|
+
targetDate DateTime?
|
|
111
|
+
status PlanStatus @default(PENDING)
|
|
112
|
+
schedule String? // Cron expression for scheduling
|
|
113
|
+
inviteeData Json? // Imported interviewee data (JSON)
|
|
114
|
+
sentCount Int @default(0)
|
|
115
|
+
completedCount Int @default(0)
|
|
116
|
+
failedCount Int @default(0)
|
|
117
|
+
sendStatus SendStatus @default(NOT_SENT)
|
|
118
|
+
createdAt DateTime @default(now())
|
|
119
|
+
updatedAt DateTime @updatedAt
|
|
120
|
+
startedAt DateTime?
|
|
121
|
+
completedAt DateTime?
|
|
122
|
+
createdBy String @default("admin")
|
|
123
|
+
updatedBy String @default("admin")
|
|
124
|
+
|
|
125
|
+
interviews Interview[]
|
|
126
|
+
template Template @relation(fields: [templateId], references: [id])
|
|
127
|
+
batchReports BatchAnalysisReport[]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
enum PlanStatus {
|
|
131
|
+
PENDING
|
|
132
|
+
READY
|
|
133
|
+
RUNNING
|
|
134
|
+
PAUSED
|
|
135
|
+
COMPLETED
|
|
136
|
+
CANCELLED
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
model AnalysisReport {
|
|
140
|
+
id String @id @default(uuid())
|
|
141
|
+
interviewId String
|
|
142
|
+
content String
|
|
143
|
+
keyFindings String[]
|
|
144
|
+
sentiment String?
|
|
145
|
+
recommendations String[]
|
|
146
|
+
dimensionTags Json? // NEW: Structured dimension annotations
|
|
147
|
+
emergentTags String[] // NEW: Emergent issues not in preset dimensions
|
|
148
|
+
interviewerRating Int? // NEW: Overall satisfaction rating (1-5)
|
|
149
|
+
createdAt DateTime @default(now())
|
|
150
|
+
|
|
151
|
+
interview Interview? @relation(fields: [interviewId], references: [id])
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
model AnalysisFailure {
|
|
155
|
+
id String @id @default(uuid())
|
|
156
|
+
interviewId String
|
|
157
|
+
errorType String
|
|
158
|
+
errorMessage String
|
|
159
|
+
createdAt DateTime @default(now())
|
|
160
|
+
retried Boolean @default(false)
|
|
161
|
+
retryCount Int @default(0)
|
|
162
|
+
|
|
163
|
+
@@unique([interviewId, errorType])
|
|
164
|
+
@@index([retried])
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
enum BatchReportStatus {
|
|
168
|
+
PENDING
|
|
169
|
+
RUNNING
|
|
170
|
+
COMPLETED
|
|
171
|
+
FAILED
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
enum BatchReportType {
|
|
175
|
+
SUMMARY
|
|
176
|
+
COMPARISON
|
|
177
|
+
TREND
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
model BatchAnalysisReport {
|
|
181
|
+
id String @id @default(uuid())
|
|
182
|
+
planId String
|
|
183
|
+
templateId String
|
|
184
|
+
type BatchReportType @default(SUMMARY)
|
|
185
|
+
status BatchReportStatus @default(PENDING)
|
|
186
|
+
content String // Markdown aggregate report
|
|
187
|
+
metrics Json // Dimension mention rates and sentiment breakdowns
|
|
188
|
+
topics Json // Clustered sub-themes per dimension
|
|
189
|
+
emergents Json // Emergent issues
|
|
190
|
+
checkpoint Json? // Intermediate step results for failure recovery
|
|
191
|
+
error String?
|
|
192
|
+
createdAt DateTime @default(now())
|
|
193
|
+
updatedAt DateTime @updatedAt
|
|
194
|
+
completedAt DateTime?
|
|
195
|
+
createdBy String @default("admin")
|
|
196
|
+
updatedBy String @default("admin")
|
|
197
|
+
|
|
198
|
+
plan InterviewPlan @relation(fields: [planId], references: [id])
|
|
199
|
+
template Template @relation(fields: [templateId], references: [id])
|
|
200
|
+
|
|
201
|
+
@@index([planId, status, createdAt])
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
model AuditLog {
|
|
205
|
+
id String @id @default(uuid())
|
|
206
|
+
action String
|
|
207
|
+
entityType String
|
|
208
|
+
entityId String?
|
|
209
|
+
userId String?
|
|
210
|
+
details String?
|
|
211
|
+
ipAddress String?
|
|
212
|
+
expiresAt DateTime? // TTL — set to createdAt + 90 days for cleanup
|
|
213
|
+
createdAt DateTime @default(now())
|
|
214
|
+
|
|
215
|
+
@@index([expiresAt])
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
model ApiKey {
|
|
219
|
+
id String @id @default(uuid())
|
|
220
|
+
keyHash String @unique
|
|
221
|
+
userId String?
|
|
222
|
+
role String @default("user")
|
|
223
|
+
revoked Boolean @default(false)
|
|
224
|
+
createdAt DateTime @default(now())
|
|
225
|
+
lastUsedAt DateTime?
|
|
226
|
+
|
|
227
|
+
@@index([revoked])
|
|
228
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* Admin panel custom styles */
|
|
2
|
+
:root {
|
|
3
|
+
--color-primary: #3b82f6;
|
|
4
|
+
--color-primary-hover: #2563eb;
|
|
5
|
+
--color-danger: #ef4444;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* HTMX indicator */
|
|
9
|
+
.htmx-indicator {
|
|
10
|
+
opacity: 0;
|
|
11
|
+
transition: opacity 200ms ease-in;
|
|
12
|
+
}
|
|
13
|
+
.htmx-request .htmx-indicator {
|
|
14
|
+
opacity: 1;
|
|
15
|
+
}
|
|
16
|
+
.htmx-request.htmx-indicator {
|
|
17
|
+
opacity: 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Table row hover */
|
|
21
|
+
.table-row-hover:hover {
|
|
22
|
+
background-color: #f3f4f6;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Status badges */
|
|
26
|
+
.badge {
|
|
27
|
+
padding: 0.25rem 0.5rem;
|
|
28
|
+
border-radius: 0.375rem;
|
|
29
|
+
font-size: 0.75rem;
|
|
30
|
+
font-weight: 500;
|
|
31
|
+
}
|
|
32
|
+
.badge-published {
|
|
33
|
+
background-color: #dcfce7;
|
|
34
|
+
color: #166534;
|
|
35
|
+
}
|
|
36
|
+
.badge-draft {
|
|
37
|
+
background-color: #f3f4f6;
|
|
38
|
+
color: #374151;
|
|
39
|
+
}
|
|
40
|
+
.badge-archived {
|
|
41
|
+
background-color: #fef3c7;
|
|
42
|
+
color: #92400e;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Question list items */
|
|
46
|
+
.question-item {
|
|
47
|
+
transition: background-color 150ms ease-out;
|
|
48
|
+
}
|
|
49
|
+
.question-item:hover {
|
|
50
|
+
background-color: #f9fafb;
|
|
51
|
+
}
|
|
52
|
+
.question-item.dragging {
|
|
53
|
+
opacity: 0.5;
|
|
54
|
+
border: 2px dashed var(--color-primary);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Form inputs */
|
|
58
|
+
input[type="text"],
|
|
59
|
+
input[type="number"],
|
|
60
|
+
input[type="email"],
|
|
61
|
+
input[type="password"],
|
|
62
|
+
textarea {
|
|
63
|
+
border-color: #d1d5db;
|
|
64
|
+
}
|
|
65
|
+
input[type="text"]:focus,
|
|
66
|
+
input[type="number"]:focus,
|
|
67
|
+
input[type="email"]:focus,
|
|
68
|
+
input[type="password"]:focus,
|
|
69
|
+
textarea:focus {
|
|
70
|
+
border-color: var(--color-primary);
|
|
71
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
72
|
+
outline: none;
|
|
73
|
+
}
|