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