lib/db/schema.ts with 47 tables organized into functional groups. This page provides a comprehensive reference.
Schema Overview
Entity Relationships
Core Tables
Authentication & Users
user - Team Members
Internal team members who use CharleOS.
| Column | Type | Description |
|---|---|---|
id | text | Primary key (CUID) |
name | text | Full name |
email | text | Email (unique, @charle.co.uk) |
status | enum | pending, sent, active, deactivated |
workType | enum | development, design, pm, csm, qa, sdr |
accessLevel | enum | admin, manager, staff |
hasBillableCapacity | boolean | Track for utilization metrics? |
hasExecutiveAccess | boolean | Access to commercials dashboard? |
engagementType | enum | retainer, project (for ICs) |
departmentId | text | Department assignment |
reportsToId | text | Manager (references user.id) |
slackUserId | text | Cached Slack user ID |
birthday | text | Format: MM-DD |
joinDate | timestamp | Employment start date |
- Admin: Full system access (Luke)
- Manager: Management access (Simon, Andre, Ben, Nic)
- Staff: Standard access (developers, designers, PMs, CSMs)
- development, design, qa: Individual Contributors (ICs)
- pm: Project Managers
- csm: Client Success Managers
- sdr: Sales Development Representatives
session & account
Better Auth tables for session management and OAuth accounts.
session: Active login sessions (expires after 7 days)account: OAuth provider connections (Google)
user_preference
User UI preferences (key-value store).
| Column | Type | Description |
|---|---|---|
userId | text | References user.id |
key | text | Preference name (e.g., “sidebarCollapsed”) |
value | text | JSON string value |
Client Management
client - Client Companies
Companies that hire Charle for work.
| Column | Type | Description |
|---|---|---|
id | text | Primary key |
name | text | Company name |
slug | text | URL-friendly identifier (unique) |
csmId | text | Assigned CSM (references user.id) |
planId | text | Pricing plan (references plan.id) |
monthlyHoursOverride | integer | Override plan hours |
monthlyCostOverride | integer | Override plan cost (pence) |
startDate | timestamp | Client start date |
status | enum | active, archived |
currentRagStatus | enum | green, amber, red |
slackChannelId | text | Slack integration channel |
client_period - Billing Periods
Tracks budget usage per billing period (monthly).
| Column | Type | Description |
|---|---|---|
clientId | text | References client.id |
periodStart | timestamp | Start of period |
periodEnd | timestamp | End of period |
allocatedMinutes | integer | Budget for period |
usedMinutes | integer | Actual time logged |
soldDayRate | integer | Calculated sold rate (pence) |
actualDayRate | integer | Calculated actual rate (pence) |
client_user - Client Portal Users
Client employees who access the client portal.
| Column | Type | Description |
|---|---|---|
clientId | text | Parent client company |
email | text | Email (unique) |
name | text | Full name |
role | enum | admin, member |
status | enum | pending, sent, active, inactive |
inviteToken | text | Invitation token |
- Client portal uses Better Auth with separate tables
client_sessionfor sessionsclient_accountfor credentials- Completely isolated from team auth
plan - Pricing Tiers
Retainer plan configurations.
| Column | Type | Description |
|---|---|---|
name | text | Plan name (e.g., “Retainer 100”) |
monthlyHours | integer | Included hours per month |
monthlyCost | integer | Cost in pence |
Quote System
quote - Work Requests
Requests for work that go through approval workflow.
| Column | Type | Description |
|---|---|---|
displayId | integer | Sequential ID (QUOTE-123) |
clientId | text | Client requesting work |
title | text | Quote title |
description | text | Brief description (rich text) |
status | enum | awaiting_quote, in_progress, awaiting_csm_review, awaiting_client_approval, approved, cancelled |
origin | enum | internal, client_request |
estimatedTime | integer | Total estimated minutes |
createdById | text | Who created it |
awaiting_quote- Created, needs scopingin_progress- Being scoped by teamawaiting_csm_review- Ready for CSM reviewawaiting_client_approval- Sent to clientapproved- Client approved (converts to task)cancelled- Client cancelled
quote_requirement - Requirement Blocks
Individual work items within a quote.
| Column | Type | Description |
|---|---|---|
quoteId | text | Parent quote |
type | enum | development, design, seo, retain |
title | text | Requirement title |
briefContent | text | Brief description (rich text JSON) |
scopeContent | text | Detailed scope (rich text JSON) |
tshirtSize | enum | xs, s, m, l, xl, xxl, not_required |
estimatedMinutes | integer | Total time (from t-shirt size) |
workSplitCore | integer | Core work minutes (80%) |
workSplitFeedback | integer | QA/feedback minutes (20%) |
isComplete | boolean | Scoping complete? |
scopedById | text | Who scoped it |
- XS: 15-30 min
- S: 30-60 min
- M: 1-4 hours
- L: 4-8 hours
- XL: 8-16 hours
- XXL: 16-32 hours
- Core work (80%) rounded down
- QA/feedback budget (20%) rounded up to nearest 15 min
Task System
task - Approved Work
Work items created from approved quotes or directly.
| Column | Type | Description |
|---|---|---|
displayId | integer | Sequential ID (TASK-123) |
quoteId | text | Source quote (if converted) |
clientId | text | Client (optional for internal) |
projectId | text | Parent project (optional) |
title | text | Task title |
description | text | Task description (rich text) |
pmId | text | Assigned project manager |
priority | integer | Priority (1-5) |
status | enum | Task status |
estimatedMinutes | integer | Total estimate |
subtask - Task Phases
Individual work phases within a task.
| Column | Type | Description |
|---|---|---|
taskId | text | Parent task |
type | enum | Subtask type (see below) |
phase | enum | design, development, qa, deployment |
status | enum | awaiting_scheduling, scheduled, in_progress, awaiting_review, complete |
assigneeId | text | Who’s doing the work |
estimatedMinutes | integer | Time estimate |
actualMinutes | integer | Actual time logged |
iteration | integer | Feedback cycle number |
reviewOutcome | enum | pending, approved, rejected |
design- Initial design workclient_design_feedback- Feedback round
development- Build workinternal_qa_fixes- Internal QA fixesexternal_qa_fixes- Client QA fixes
internal_qa_review- Internal reviewexternal_qa_review- Client review
deployment- Deploy to production
Time Tracking
time_entry - Logged Time
Time logged against clients, tasks, and subtasks.
| Column | Type | Description |
|---|---|---|
userId | text | Who logged the time |
clientId | text | Client being billed |
taskId | text | Related task (optional) |
subtaskId | text | Related subtask (optional) |
duration | integer | Minutes worked |
date | date | Date of work |
description | text | Work description |
category | text | Type of work |
Projects
project - Multi-Phase Projects
Long-running projects with budgeted phases.
| Column | Type | Description |
|---|---|---|
clientId | text | Client |
name | text | Project name |
description | text | Description |
status | enum | planning, in_progress, on_hold, completed |
totalBudgetMinutes | integer | Total allocated time |
usedMinutes | integer | Time used across tasks |
project_phase - Budgeted Phases
Budget allocations within a project.
| Column | Type | Description |
|---|---|---|
projectId | text | Parent project |
name | text | Phase name |
budgetMinutes | integer | Allocated time |
usedMinutes | integer | Time used |
status | enum | Phase status |
Scheduling
client_block - Scheduled Work
Blocks of scheduled time on the calendar.
| Column | Type | Description |
|---|---|---|
clientId | text | Client |
userId | text | Assigned user |
subtaskId | text | Related subtask (optional) |
startDate | date | Start date |
endDate | date | End date |
hours | real | Hours per day |
type | enum | subtask, retainer, placeholder |
status | enum | scheduled, in_progress, complete, cancelled |
Metrics & Reporting
metric_snapshot - Daily Metrics
Daily snapshots of utilization, day rates, and efficiency.
| Column | Type | Description |
|---|---|---|
date | date | Snapshot date |
entityType | enum | user, client, department, agency |
entityId | text | Entity identifier |
utilizationPercent | integer | Utilization % |
soldDayRate | integer | Sold day rate (pence) |
actualDayRate | integer | Actual day rate (pence) |
bankedMinutes | integer | Efficiency gain |
overageMinutes | integer | Non-billable overage |
Help Desk
help_desk_ticket - Support Tickets
Client support requests (converted to subtasks).
| Column | Type | Description |
|---|---|---|
displayId | integer | Ticket number |
clientId | text | Client |
title | text | Ticket title |
description | text | Description |
type | enum | bug, feature_request, question, other |
priority | enum | low, medium, high, critical |
status | enum | Ticket status |
subtaskId | text | Created subtask |
Annual Leave
annual_leave - Leave Requests
Team member leave requests and approvals.
| Column | Type | Description |
|---|---|---|
userId | text | Team member |
startDate | timestamp | Leave start |
endDate | timestamp | Leave end |
leaveType | enum | annual, sick, unpaid, compassionate, birthday, other |
dayType | enum | full_day, half_day_am, half_day_pm |
status | enum | pending, approved, rejected, cancelled |
reviewerId | text | Who approved/rejected |
RAG Reports
rag_report - Client Status Reports
Weekly Red/Amber/Green client health reports.
| Column | Type | Description |
|---|---|---|
clientId | text | Client |
weekStarting | date | Week start date |
status | enum | draft, completed |
ragStatus | enum | green, amber, red |
keyPoints | text | Summary points |
risks | text | Risk assessment |
Enums Reference
User & Access
Task & Subtask
Quote
Type Inference
Drizzle automatically generates TypeScript types from the schema:Relationships
Foreign Keys
All relationships use foreign keys with appropriate cascade rules:onDelete: "cascade"- Delete children when parent is deletedonDelete: "set null"- Set to null when parent is deleted- Default: Restrict deletion if children exist
Common Joins
Modifying the Schema
1
Edit Schema File
Modify
lib/db/schema.ts to add/change tables or columns2
Test Locally
3
Verify in Studio
4
Generate Migration
5
Commit & Deploy
Commit schema + migration files. Migrations run automatically on deployment.