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.
Files changed (233) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +242 -0
  3. package/VERSION +1 -0
  4. package/dist/api/admin-templates.d.ts +15 -0
  5. package/dist/api/admin-templates.d.ts.map +1 -0
  6. package/dist/api/admin-templates.js +611 -0
  7. package/dist/api/admin-templates.js.map +1 -0
  8. package/dist/api/analysis.d.ts +8 -0
  9. package/dist/api/analysis.d.ts.map +1 -0
  10. package/dist/api/analysis.js +106 -0
  11. package/dist/api/analysis.js.map +1 -0
  12. package/dist/api/health.d.ts +6 -0
  13. package/dist/api/health.d.ts.map +1 -0
  14. package/dist/api/health.js +116 -0
  15. package/dist/api/health.js.map +1 -0
  16. package/dist/api/plans.d.ts +3 -0
  17. package/dist/api/plans.d.ts.map +1 -0
  18. package/dist/api/plans.js +184 -0
  19. package/dist/api/plans.js.map +1 -0
  20. package/dist/api/templates.d.ts +9 -0
  21. package/dist/api/templates.d.ts.map +1 -0
  22. package/dist/api/templates.js +211 -0
  23. package/dist/api/templates.js.map +1 -0
  24. package/dist/core/graph.d.ts +7 -0
  25. package/dist/core/graph.d.ts.map +1 -0
  26. package/dist/core/graph.js +31 -0
  27. package/dist/core/graph.js.map +1 -0
  28. package/dist/core/nodes/analyzing.d.ts +3 -0
  29. package/dist/core/nodes/analyzing.d.ts.map +1 -0
  30. package/dist/core/nodes/analyzing.js +38 -0
  31. package/dist/core/nodes/analyzing.js.map +1 -0
  32. package/dist/core/nodes/completed.d.ts +3 -0
  33. package/dist/core/nodes/completed.d.ts.map +1 -0
  34. package/dist/core/nodes/completed.js +8 -0
  35. package/dist/core/nodes/completed.js.map +1 -0
  36. package/dist/core/nodes/interviewing.d.ts +13 -0
  37. package/dist/core/nodes/interviewing.d.ts.map +1 -0
  38. package/dist/core/nodes/interviewing.js +130 -0
  39. package/dist/core/nodes/interviewing.js.map +1 -0
  40. package/dist/core/nodes/planning.d.ts +15 -0
  41. package/dist/core/nodes/planning.d.ts.map +1 -0
  42. package/dist/core/nodes/planning.js +40 -0
  43. package/dist/core/nodes/planning.js.map +1 -0
  44. package/dist/core/types/index.d.ts +69 -0
  45. package/dist/core/types/index.d.ts.map +1 -0
  46. package/dist/core/types/index.js +49 -0
  47. package/dist/core/types/index.js.map +1 -0
  48. package/dist/domains/interview/entities/interview.entity.d.ts +21 -0
  49. package/dist/domains/interview/entities/interview.entity.d.ts.map +1 -0
  50. package/dist/domains/interview/entities/interview.entity.js +20 -0
  51. package/dist/domains/interview/entities/interview.entity.js.map +1 -0
  52. package/dist/domains/interview/entities/message.entity.d.ts +15 -0
  53. package/dist/domains/interview/entities/message.entity.d.ts.map +1 -0
  54. package/dist/domains/interview/entities/message.entity.js +11 -0
  55. package/dist/domains/interview/entities/message.entity.js.map +1 -0
  56. package/dist/domains/interview/entities/response.entity.d.ts +17 -0
  57. package/dist/domains/interview/entities/response.entity.d.ts.map +1 -0
  58. package/dist/domains/interview/entities/response.entity.js +12 -0
  59. package/dist/domains/interview/entities/response.entity.js.map +1 -0
  60. package/dist/domains/interview/services/interview.domain.d.ts +8 -0
  61. package/dist/domains/interview/services/interview.domain.d.ts.map +1 -0
  62. package/dist/domains/interview/services/interview.domain.js +30 -0
  63. package/dist/domains/interview/services/interview.domain.js.map +1 -0
  64. package/dist/integrations/dingtalk/client.d.ts +32 -0
  65. package/dist/integrations/dingtalk/client.d.ts.map +1 -0
  66. package/dist/integrations/dingtalk/client.js +89 -0
  67. package/dist/integrations/dingtalk/client.js.map +1 -0
  68. package/dist/integrations/dingtalk/message-sender.d.ts +24 -0
  69. package/dist/integrations/dingtalk/message-sender.d.ts.map +1 -0
  70. package/dist/integrations/dingtalk/message-sender.js +81 -0
  71. package/dist/integrations/dingtalk/message-sender.js.map +1 -0
  72. package/dist/integrations/dingtalk/middleware.d.ts +4 -0
  73. package/dist/integrations/dingtalk/middleware.d.ts.map +1 -0
  74. package/dist/integrations/dingtalk/middleware.js +29 -0
  75. package/dist/integrations/dingtalk/middleware.js.map +1 -0
  76. package/dist/integrations/dingtalk/stream-client.d.ts +112 -0
  77. package/dist/integrations/dingtalk/stream-client.d.ts.map +1 -0
  78. package/dist/integrations/dingtalk/stream-client.js +305 -0
  79. package/dist/integrations/dingtalk/stream-client.js.map +1 -0
  80. package/dist/integrations/dingtalk/token-manager.d.ts +22 -0
  81. package/dist/integrations/dingtalk/token-manager.d.ts.map +1 -0
  82. package/dist/integrations/dingtalk/token-manager.js +109 -0
  83. package/dist/integrations/dingtalk/token-manager.js.map +1 -0
  84. package/dist/integrations/llm/alibaba.d.ts +10 -0
  85. package/dist/integrations/llm/alibaba.d.ts.map +1 -0
  86. package/dist/integrations/llm/alibaba.js +60 -0
  87. package/dist/integrations/llm/alibaba.js.map +1 -0
  88. package/dist/integrations/llm/base.d.ts +28 -0
  89. package/dist/integrations/llm/base.d.ts.map +1 -0
  90. package/dist/integrations/llm/base.js +2 -0
  91. package/dist/integrations/llm/base.js.map +1 -0
  92. package/dist/integrations/llm/volcengine.d.ts +12 -0
  93. package/dist/integrations/llm/volcengine.d.ts.map +1 -0
  94. package/dist/integrations/llm/volcengine.js +80 -0
  95. package/dist/integrations/llm/volcengine.js.map +1 -0
  96. package/dist/middleware/admin-auth.d.ts +3 -0
  97. package/dist/middleware/admin-auth.d.ts.map +1 -0
  98. package/dist/middleware/admin-auth.js +37 -0
  99. package/dist/middleware/admin-auth.js.map +1 -0
  100. package/dist/middleware/api-auth.d.ts +10 -0
  101. package/dist/middleware/api-auth.d.ts.map +1 -0
  102. package/dist/middleware/api-auth.js +30 -0
  103. package/dist/middleware/api-auth.js.map +1 -0
  104. package/dist/repositories/interview-state-mapper.d.ts +28 -0
  105. package/dist/repositories/interview-state-mapper.d.ts.map +1 -0
  106. package/dist/repositories/interview-state-mapper.js +32 -0
  107. package/dist/repositories/interview-state-mapper.js.map +1 -0
  108. package/dist/repositories/interview-state.repository.d.ts +34 -0
  109. package/dist/repositories/interview-state.repository.d.ts.map +1 -0
  110. package/dist/repositories/interview-state.repository.js +220 -0
  111. package/dist/repositories/interview-state.repository.js.map +1 -0
  112. package/dist/repositories/interview.repository.d.ts +29 -0
  113. package/dist/repositories/interview.repository.d.ts.map +1 -0
  114. package/dist/repositories/interview.repository.js +36 -0
  115. package/dist/repositories/interview.repository.js.map +1 -0
  116. package/dist/repositories/message.repository.d.ts +17 -0
  117. package/dist/repositories/message.repository.d.ts.map +1 -0
  118. package/dist/repositories/message.repository.js +37 -0
  119. package/dist/repositories/message.repository.js.map +1 -0
  120. package/dist/repositories/template.repository.d.ts +117 -0
  121. package/dist/repositories/template.repository.d.ts.map +1 -0
  122. package/dist/repositories/template.repository.js +280 -0
  123. package/dist/repositories/template.repository.js.map +1 -0
  124. package/dist/schemas/dimensions.d.ts +61 -0
  125. package/dist/schemas/dimensions.d.ts.map +1 -0
  126. package/dist/schemas/dimensions.js +44 -0
  127. package/dist/schemas/dimensions.js.map +1 -0
  128. package/dist/server.d.ts +10 -0
  129. package/dist/server.d.ts.map +1 -0
  130. package/dist/server.js +217 -0
  131. package/dist/server.js.map +1 -0
  132. package/dist/services/analysis.service.d.ts +81 -0
  133. package/dist/services/analysis.service.d.ts.map +1 -0
  134. package/dist/services/analysis.service.js +229 -0
  135. package/dist/services/analysis.service.js.map +1 -0
  136. package/dist/services/analytics.service.d.ts +44 -0
  137. package/dist/services/analytics.service.d.ts.map +1 -0
  138. package/dist/services/analytics.service.js +222 -0
  139. package/dist/services/analytics.service.js.map +1 -0
  140. package/dist/services/asr.service.d.ts +9 -0
  141. package/dist/services/asr.service.d.ts.map +1 -0
  142. package/dist/services/asr.service.js +41 -0
  143. package/dist/services/asr.service.js.map +1 -0
  144. package/dist/services/audit-cleanup.service.d.ts +9 -0
  145. package/dist/services/audit-cleanup.service.d.ts.map +1 -0
  146. package/dist/services/audit-cleanup.service.js +27 -0
  147. package/dist/services/audit-cleanup.service.js.map +1 -0
  148. package/dist/services/batch-aggregation.service.d.ts +34 -0
  149. package/dist/services/batch-aggregation.service.d.ts.map +1 -0
  150. package/dist/services/batch-aggregation.service.js +91 -0
  151. package/dist/services/batch-aggregation.service.js.map +1 -0
  152. package/dist/services/conversation-engine.d.ts +16 -0
  153. package/dist/services/conversation-engine.d.ts.map +1 -0
  154. package/dist/services/conversation-engine.js +49 -0
  155. package/dist/services/conversation-engine.js.map +1 -0
  156. package/dist/services/dead-letter.service.d.ts +12 -0
  157. package/dist/services/dead-letter.service.d.ts.map +1 -0
  158. package/dist/services/dead-letter.service.js +24 -0
  159. package/dist/services/dead-letter.service.js.map +1 -0
  160. package/dist/services/followup.service.d.ts +18 -0
  161. package/dist/services/followup.service.d.ts.map +1 -0
  162. package/dist/services/followup.service.js +146 -0
  163. package/dist/services/followup.service.js.map +1 -0
  164. package/dist/services/interview-plan-base.service.d.ts +226 -0
  165. package/dist/services/interview-plan-base.service.d.ts.map +1 -0
  166. package/dist/services/interview-plan-base.service.js +217 -0
  167. package/dist/services/interview-plan-base.service.js.map +1 -0
  168. package/dist/services/interview-plan-members.service.d.ts +45 -0
  169. package/dist/services/interview-plan-members.service.d.ts.map +1 -0
  170. package/dist/services/interview-plan-members.service.js +237 -0
  171. package/dist/services/interview-plan-members.service.js.map +1 -0
  172. package/dist/services/interview-plan-send.service.d.ts +27 -0
  173. package/dist/services/interview-plan-send.service.d.ts.map +1 -0
  174. package/dist/services/interview-plan-send.service.js +250 -0
  175. package/dist/services/interview-plan-send.service.js.map +1 -0
  176. package/dist/services/interview-plan.service.d.ts +8 -0
  177. package/dist/services/interview-plan.service.d.ts.map +1 -0
  178. package/dist/services/interview-plan.service.js +8 -0
  179. package/dist/services/interview-plan.service.js.map +1 -0
  180. package/dist/services/llm-clustering.d.ts +16 -0
  181. package/dist/services/llm-clustering.d.ts.map +1 -0
  182. package/dist/services/llm-clustering.js +91 -0
  183. package/dist/services/llm-clustering.js.map +1 -0
  184. package/dist/services/prompt.service.d.ts +13 -0
  185. package/dist/services/prompt.service.d.ts.map +1 -0
  186. package/dist/services/prompt.service.js +151 -0
  187. package/dist/services/prompt.service.js.map +1 -0
  188. package/dist/services/report.service.d.ts +28 -0
  189. package/dist/services/report.service.d.ts.map +1 -0
  190. package/dist/services/report.service.js +169 -0
  191. package/dist/services/report.service.js.map +1 -0
  192. package/dist/services/stream-message-utils.d.ts +31 -0
  193. package/dist/services/stream-message-utils.d.ts.map +1 -0
  194. package/dist/services/stream-message-utils.js +80 -0
  195. package/dist/services/stream-message-utils.js.map +1 -0
  196. package/dist/services/stream-message.service.d.ts +29 -0
  197. package/dist/services/stream-message.service.d.ts.map +1 -0
  198. package/dist/services/stream-message.service.js +184 -0
  199. package/dist/services/stream-message.service.js.map +1 -0
  200. package/dist/services/template-dimension.service.d.ts +21 -0
  201. package/dist/services/template-dimension.service.d.ts.map +1 -0
  202. package/dist/services/template-dimension.service.js +9 -0
  203. package/dist/services/template-dimension.service.js.map +1 -0
  204. package/dist/utils/db.d.ts +10 -0
  205. package/dist/utils/db.d.ts.map +1 -0
  206. package/dist/utils/db.js +21 -0
  207. package/dist/utils/db.js.map +1 -0
  208. package/dist/utils/logger.d.ts +8 -0
  209. package/dist/utils/logger.d.ts.map +1 -0
  210. package/dist/utils/logger.js +21 -0
  211. package/dist/utils/logger.js.map +1 -0
  212. package/dist/utils/markdown.d.ts +2 -0
  213. package/dist/utils/markdown.d.ts.map +1 -0
  214. package/dist/utils/markdown.js +39 -0
  215. package/dist/utils/markdown.js.map +1 -0
  216. package/dist/utils/pii-anonymizer.d.ts +2 -0
  217. package/dist/utils/pii-anonymizer.d.ts.map +1 -0
  218. package/dist/utils/pii-anonymizer.js +13 -0
  219. package/dist/utils/pii-anonymizer.js.map +1 -0
  220. package/dist/utils/retry.d.ts +8 -0
  221. package/dist/utils/retry.d.ts.map +1 -0
  222. package/dist/utils/retry.js +28 -0
  223. package/dist/utils/retry.js.map +1 -0
  224. package/dist/utils/security.d.ts +26 -0
  225. package/dist/utils/security.d.ts.map +1 -0
  226. package/dist/utils/security.js +99 -0
  227. package/dist/utils/security.js.map +1 -0
  228. package/package.json +80 -0
  229. package/prisma/schema.prisma +228 -0
  230. package/public/css/admin.css +73 -0
  231. package/public/js/alpine.min.js +2670 -0
  232. package/public/js/htmx.min.js +3422 -0
  233. 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
+ }