feat: Refactor database schema to enhance relationships and data types for Company, User, Session, and Message models

This commit is contained in:
Max Kowalski
2025-06-27 16:05:58 +02:00
parent ab2c75b736
commit d7ac0ba208
3 changed files with 154 additions and 169 deletions

108
TODO.md
View File

@ -1,108 +0,0 @@
# TODO.md
## Dashboard Integration
- [ ] **Resolve `GeographicMap.tsx` and `ResponseTimeDistribution.tsx` data simulation**
- Investigate integrating real data sources with server-side analytics
- Replace simulated data mentioned in `docs/dashboard-components.md`
## Component Specific
- [ ] **Implement robust emailing of temporary passwords**
- File: `pages/api/dashboard/users.ts`
- Set up proper email service integration
- [x] **Session page improvements**
- File: `app/dashboard/sessions/page.tsx`
- Implemented pagination, advanced filtering, and sorting
## File Cleanup
- [x] **Remove backup files**
- Reviewed and removed `.bak` and `.new` files after integration
- Cleaned up `GeographicMap.tsx.bak`, `SessionDetails.tsx.bak`, `SessionDetails.tsx.new`
## Database Schema Improvements
- [ ] **Update EndTime field**
- Make `endTime` field nullable in Prisma schema to match TypeScript interfaces
- [ ] **Add database indices**
- Add appropriate indices to improve query performance
- Focus on dashboard metrics and session listing queries
- [ ] **Implement production email service**
- Replace console logging in `lib/sendEmail.ts`
- Consider providers: Nodemailer, SendGrid, AWS SES
## General Enhancements & Features
- [ ] **Real-time updates**
- Implement for dashboard and session list
- Consider WebSockets or Server-Sent Events
- [ ] **Data export functionality**
- Allow users (especially admins) to export session data
- Support CSV format initially
- [ ] **Customizable dashboard**
- Allow users to customize dashboard view
- Let users choose which metrics/charts are most important
## Testing & Quality Assurance
- [ ] **Comprehensive testing suite**
- [ ] Unit tests for utility functions and API logic
- [ ] Integration tests for API endpoints with database
- [ ] End-to-end tests for user flows (Playwright or Cypress)
- [ ] **Error monitoring and logging**
- Integrate robust error monitoring service (Sentry)
- Enhance server-side logging
- [ ] **Accessibility improvements**
- Review application against WCAG guidelines
- Improve keyboard navigation and screen reader compatibility
- Check color contrast ratios
## Security Enhancements
- [x] **Password reset functionality**
- Implemented secure password reset mechanism
- Files: `app/forgot-password/page.tsx`, `app/reset-password/page.tsx`, `pages/api/forgot-password.ts`, `pages/api/reset-password.ts`
- [ ] **Two-Factor Authentication (2FA)**
- Consider adding 2FA, especially for admin accounts
- [ ] **Input validation and sanitization**
- Review all user inputs (API request bodies, query parameters)
- Ensure proper validation and sanitization
## Code Quality & Development
- [ ] **Code review process**
- Enforce code reviews for all changes
- [ ] **Environment configuration**
- Ensure secure management of environment-specific configurations
- [ ] **Dependency management**
- Periodically review dependencies for vulnerabilities
- Keep dependencies updated
- [ ] **Documentation updates**
- [ ] Ensure `docs/dashboard-components.md` reflects actual implementations
- [ ] Verify "Dashboard Enhancements" are consistently applied
- [ ] Update documentation for improved layout and visual hierarchies

View File

@ -15,6 +15,7 @@
"prisma:migrate": "prisma migrate dev", "prisma:migrate": "prisma migrate dev",
"prisma:seed": "node prisma/seed.mjs", "prisma:seed": "node prisma/seed.mjs",
"prisma:push": "prisma db push", "prisma:push": "prisma db push",
"prisma:push:force": "prisma db push --force-reset",
"prisma:studio": "prisma studio", "prisma:studio": "prisma studio",
"start": "node server.mjs", "start": "node server.mjs",
"lint:md": "markdownlint-cli2 \"**/*.md\" \"!.trunk/**\" \"!.venv/**\" \"!node_modules/**\"", "lint:md": "markdownlint-cli2 \"**/*.md\" \"!.trunk/**\" \"!.venv/**\" \"!node_modules/**\"",

View File

@ -1,74 +1,166 @@
// Database schema, one company = one org, linked to users and CSV config
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
datasource db { datasource db {
provider = "sqlite" provider = "sqlite" // still OK for local/dev; use Postgres in prod
url = "file:./dev.db" url = "file:./dev.db"
} }
/**
* ENUMS fewer magic strings
*/
enum UserRole {
ADMIN
USER
AUDITOR
}
enum SentimentCategory {
POSITIVE
NEUTRAL
NEGATIVE
}
/**
* COMPANY (multi-tenant root)
*/
model Company { model Company {
id String @id @default(uuid()) id String @id @default(uuid())
name String name String
csvUrl String // where to fetch CSV csvUrl String
csvUsername String? // for basic auth csvUsername String?
csvPassword String? csvPassword String?
sentimentAlert Float? // e.g. alert threshold for negative chats sentimentAlert Float?
dashboardOpts String? // JSON blob for per-company dashboard preferences dashboardOpts Json? // JSON column instead of opaque string
users User[]
users User[] @relation("CompanyUsers")
sessions Session[] sessions Session[]
imports SessionImport[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
} }
/**
* USER (auth accounts)
*/
model User { model User {
id String @id @default(uuid()) id String @id @default(uuid())
email String @unique email String @unique
password String // hashed, use bcrypt password String
company Company @relation(fields: [companyId], references: [id]) role UserRole @default(USER)
company Company @relation("CompanyUsers", fields: [companyId], references: [id], onDelete: Cascade)
companyId String companyId String
role String // 'admin' | 'user' | 'auditor'
resetToken String? resetToken String?
resetTokenExpiry DateTime? resetTokenExpiry DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
} }
/**
* SESSION ↔ SESSIONIMPORT (1-to-1)
*/
/**
* 1. Normalised session ---------------------------
*/
model Session { model Session {
id String @id id String @id @default(uuid())
company Company @relation(fields: [companyId], references: [id]) company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
companyId String companyId String
/**
* 1-to-1 link back to the import row
*/
import SessionImport? @relation("ImportToSession", fields: [importId], references: [id])
importId String? @unique
/**
* session-level data …
*/
startTime DateTime startTime DateTime
endTime DateTime endTime DateTime
// … whatever other scalar fields you have here …
/**
* ---------- the missing opposite side ----------
*/
messages Message[] // <-- satisfies Message.session
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([companyId, startTime])
}
/**
* 2. Raw CSV row waiting to be processed ----------
*/
enum ImportStatus {
QUEUED
PROCESSING
DONE
ERROR
}
model SessionImport {
id String @id @default(uuid())
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
companyId String
/**
* 1-to-1 back-relation; NO fields/references here
*/
session Session? @relation("ImportToSession")
// ─── 16 CSV columns 1-to-1 ────────────────────────
externalSessionId String @unique // value from CSV column 1
startTimeRaw String
endTimeRaw String
ipAddress String? ipAddress String?
country String? countryCode String?
language String? language String?
messagesSent Int? messagesSent Int?
sentiment Float? // Original sentiment score (float) sentimentRaw String?
sentimentCategory String? // "positive", "neutral", "negative" from OpenAPI escalatedRaw String?
escalated Boolean? forwardedHrRaw String?
forwardedHr Boolean?
fullTranscriptUrl String? fullTranscriptUrl String?
avgResponseTime Float? avgResponseTimeSeconds Float?
tokens Int? tokens Int?
tokensEur Float? tokensEur Float?
category String? category String?
initialMsg String? initialMessage String?
processed Boolean @default(false) // Flag for post-processing status
questions String? // JSON array of questions asked by user // ─── bookkeeping ─────────────────────────────────
summary String? // Brief summary of the conversation status ImportStatus @default(QUEUED)
messages Message[] // Relation to parsed messages errorMsg String?
processedAt DateTime?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
@@unique([companyId, externalSessionId]) // idempotent re-imports
@@index([status])
} }
/**
* MESSAGE (individual lines)
*/
model Message { model Message {
id String @id @default(uuid()) id String @id @default(uuid())
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade) session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
sessionId String sessionId String
timestamp DateTime // When the message was sent
role String // "User", "Assistant", "System", etc. timestamp DateTime
content String // The message content role String // "user" | "assistant" | "system" free-form keeps migration easy
order Int // Order within the conversation (0, 1, 2, ...) content String
order Int
createdAt DateTime @default(now()) createdAt DateTime @default(now())
@@index([sessionId, order]) // Index for efficient ordering queries @@unique([sessionId, order]) // guards against duplicate order values
@@index([sessionId, order])
} }