mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 07:32:11 +01:00
feat: Refactor database schema to enhance relationships and data types for Company, User, Session, and Message models
This commit is contained in:
108
TODO.md
108
TODO.md
@ -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
|
||||
@ -15,6 +15,7 @@
|
||||
"prisma:migrate": "prisma migrate dev",
|
||||
"prisma:seed": "node prisma/seed.mjs",
|
||||
"prisma:push": "prisma db push",
|
||||
"prisma:push:force": "prisma db push --force-reset",
|
||||
"prisma:studio": "prisma studio",
|
||||
"start": "node server.mjs",
|
||||
"lint:md": "markdownlint-cli2 \"**/*.md\" \"!.trunk/**\" \"!.venv/**\" \"!node_modules/**\"",
|
||||
|
||||
@ -1,74 +1,166 @@
|
||||
// Database schema, one company = one org, linked to users and CSV config
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
provider = "sqlite" // still OK for local/dev; use Postgres in prod
|
||||
url = "file:./dev.db"
|
||||
}
|
||||
|
||||
/**
|
||||
* ENUMS – fewer magic strings
|
||||
*/
|
||||
enum UserRole {
|
||||
ADMIN
|
||||
USER
|
||||
AUDITOR
|
||||
}
|
||||
|
||||
enum SentimentCategory {
|
||||
POSITIVE
|
||||
NEUTRAL
|
||||
NEGATIVE
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPANY (multi-tenant root)
|
||||
*/
|
||||
model Company {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
csvUrl String // where to fetch CSV
|
||||
csvUsername String? // for basic auth
|
||||
csvPassword String?
|
||||
sentimentAlert Float? // e.g. alert threshold for negative chats
|
||||
dashboardOpts String? // JSON blob for per-company dashboard preferences
|
||||
users User[]
|
||||
sessions Session[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
csvUrl String
|
||||
csvUsername String?
|
||||
csvPassword String?
|
||||
sentimentAlert Float?
|
||||
dashboardOpts Json? // JSON column instead of opaque string
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
password String // hashed, use bcrypt
|
||||
company Company @relation(fields: [companyId], references: [id])
|
||||
companyId String
|
||||
role String // 'admin' | 'user' | 'auditor'
|
||||
resetToken String?
|
||||
resetTokenExpiry DateTime?
|
||||
}
|
||||
users User[] @relation("CompanyUsers")
|
||||
sessions Session[]
|
||||
imports SessionImport[]
|
||||
|
||||
model Session {
|
||||
id String @id
|
||||
company Company @relation(fields: [companyId], references: [id])
|
||||
companyId String
|
||||
startTime DateTime
|
||||
endTime DateTime
|
||||
ipAddress String?
|
||||
country String?
|
||||
language String?
|
||||
messagesSent Int?
|
||||
sentiment Float? // Original sentiment score (float)
|
||||
sentimentCategory String? // "positive", "neutral", "negative" from OpenAPI
|
||||
escalated Boolean?
|
||||
forwardedHr Boolean?
|
||||
fullTranscriptUrl String?
|
||||
avgResponseTime Float?
|
||||
tokens Int?
|
||||
tokensEur Float?
|
||||
category String?
|
||||
initialMsg String?
|
||||
processed Boolean @default(false) // Flag for post-processing status
|
||||
questions String? // JSON array of questions asked by user
|
||||
summary String? // Brief summary of the conversation
|
||||
messages Message[] // Relation to parsed messages
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Message {
|
||||
id String @id @default(uuid())
|
||||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
sessionId String
|
||||
timestamp DateTime // When the message was sent
|
||||
role String // "User", "Assistant", "System", etc.
|
||||
content String // The message content
|
||||
order Int // Order within the conversation (0, 1, 2, ...)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([sessionId, order]) // Index for efficient ordering queries
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
/**
|
||||
* USER (auth accounts)
|
||||
*/
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
password String
|
||||
role UserRole @default(USER)
|
||||
|
||||
company Company @relation("CompanyUsers", fields: [companyId], references: [id], onDelete: Cascade)
|
||||
companyId String
|
||||
|
||||
resetToken String?
|
||||
resetTokenExpiry DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
/**
|
||||
* SESSION ↔ SESSIONIMPORT (1-to-1)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 1. Normalised session ---------------------------
|
||||
*/
|
||||
model Session {
|
||||
id String @id @default(uuid())
|
||||
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
|
||||
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
|
||||
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?
|
||||
countryCode String?
|
||||
language String?
|
||||
messagesSent Int?
|
||||
sentimentRaw String?
|
||||
escalatedRaw String?
|
||||
forwardedHrRaw String?
|
||||
fullTranscriptUrl String?
|
||||
avgResponseTimeSeconds Float?
|
||||
tokens Int?
|
||||
tokensEur Float?
|
||||
category String?
|
||||
initialMessage String?
|
||||
|
||||
// ─── bookkeeping ─────────────────────────────────
|
||||
status ImportStatus @default(QUEUED)
|
||||
errorMsg String?
|
||||
processedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([companyId, externalSessionId]) // idempotent re-imports
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
/**
|
||||
* MESSAGE (individual lines)
|
||||
*/
|
||||
model Message {
|
||||
id String @id @default(uuid())
|
||||
|
||||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
sessionId String
|
||||
|
||||
timestamp DateTime
|
||||
role String // "user" | "assistant" | "system" – free-form keeps migration easy
|
||||
content String
|
||||
order Int
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([sessionId, order]) // guards against duplicate order values
|
||||
@@index([sessionId, order])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user