feat: add initial database schema and task service
parent
7646e85c30
commit
644883109f
@ -0,0 +1,39 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
|
||||
export const db = new Database('tasks.db');
|
||||
|
||||
export function initializeDatabase() {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
due_date TIMESTAMP NULL,
|
||||
completed BOOLEAN DEFAULT FALSE,
|
||||
completed_at TIMESTAMP NULL,
|
||||
group_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_assignments (
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
assigned_by TEXT NOT NULL,
|
||||
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (task_id, user_id),
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
phone_number TEXT PRIMARY KEY,
|
||||
wa_id TEXT NOT NULL, // WhatsApp's @s.whatsapp.net ID
|
||||
name TEXT,
|
||||
last_seen TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS groups (
|
||||
id TEXT PRIMARY KEY,
|
||||
community_id TEXT NOT NULL,
|
||||
name TEXT
|
||||
);
|
||||
`);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
export interface Task {
|
||||
id: number;
|
||||
description: string;
|
||||
created_at: Date;
|
||||
due_date: Date | null;
|
||||
completed: boolean;
|
||||
completed_at: Date | null;
|
||||
group_id: string; // WhatsApp group ID where task was created
|
||||
}
|
||||
|
||||
export interface TaskAssignment {
|
||||
task_id: number;
|
||||
user_id: string; // Normalized phone number
|
||||
assigned_by: string; // Who assigned this
|
||||
assigned_at: Date;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { db } from '../db';
|
||||
import type { Task, TaskAssignment } from './model';
|
||||
|
||||
export class TaskService {
|
||||
static createTask(task: Omit<Task, 'id'>, assignments: Omit<TaskAssignment, 'task_id'>[]) {
|
||||
return db.transaction(() => {
|
||||
const insertTask = db.prepare(`
|
||||
INSERT INTO tasks (description, due_date, group_id)
|
||||
VALUES (?, ?, ?)
|
||||
RETURNING id
|
||||
`);
|
||||
|
||||
const taskResult = insertTask.get(
|
||||
task.description,
|
||||
task.due_date?.toISOString(),
|
||||
task.group_id
|
||||
) as { id: number };
|
||||
|
||||
const insertAssignment = db.prepare(`
|
||||
INSERT INTO task_assignments (task_id, user_id, assigned_by)
|
||||
VALUES (?, ?, ?)
|
||||
`);
|
||||
|
||||
assignments.forEach(assignment => {
|
||||
insertAssignment.run(
|
||||
taskResult.id,
|
||||
assignment.user_id,
|
||||
assignment.assigned_by
|
||||
);
|
||||
});
|
||||
|
||||
return taskResult.id;
|
||||
});
|
||||
}
|
||||
|
||||
// We'll add more methods here as we progress
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { db, initializeDatabase } from '../../src/db';
|
||||
import { describe, test, expect, beforeEach } from 'bun:test';
|
||||
|
||||
describe('Database', () => {
|
||||
beforeEach(() => {
|
||||
// Reset database between tests
|
||||
db.exec('DROP TABLE IF EXISTS tasks');
|
||||
db.exec('DROP TABLE IF EXISTS task_assignments');
|
||||
db.exec('DROP TABLE IF EXISTS users');
|
||||
db.exec('DROP TABLE IF EXISTS groups');
|
||||
initializeDatabase();
|
||||
});
|
||||
|
||||
test('should create all required tables', () => {
|
||||
const tables = db
|
||||
.query("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
.all()
|
||||
.map((t: any) => t.name);
|
||||
|
||||
expect(tables).toContain('tasks');
|
||||
expect(tables).toContain('task_assignments');
|
||||
expect(tables).toContain('users');
|
||||
expect(tables).toContain('groups');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue