add habit and habit completion API functions\
\ - Add comprehensive CRUD operations for habits and completions (create, update, delete, list) - Add completion management with upsert, update, and reset functionality - Include date-based queries and filtering for completions
This commit is contained in:
@@ -0,0 +1,175 @@
|
|||||||
|
import { triplit, Query } from '../triplit/client';
|
||||||
|
import type { Habit, HabitCompletion } from '../triplit/schema';
|
||||||
|
|
||||||
|
export type CreateHabitData = Omit<Habit, 'id' | 'active' | 'startDate' | 'createdAt' | 'updatedAt'> & {
|
||||||
|
endDate?: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateHabitData = Partial<Omit<Habit, 'id' | 'userId' | 'createdAt'>>;
|
||||||
|
|
||||||
|
export type CreateCompletionData = Omit<HabitCompletion, 'id' | 'completedAt'> & {
|
||||||
|
completedAt?: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createHabit(data: CreateHabitData & { active?: boolean; startDate?: Date }) {
|
||||||
|
return await triplit.insert('habits', {
|
||||||
|
userId: data.userId,
|
||||||
|
name: data.name,
|
||||||
|
duration: data.duration,
|
||||||
|
target: data.target,
|
||||||
|
increment: data.increment,
|
||||||
|
unit: data.unit || null,
|
||||||
|
active: data.active ?? true,
|
||||||
|
startDate: data.startDate || new Date(),
|
||||||
|
endDate: data.endDate || null,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateHabit(id: string, data: UpdateHabitData) {
|
||||||
|
return await triplit.update('habits', id, {
|
||||||
|
...data,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteHabit(id: string) {
|
||||||
|
// Delete all associated completions
|
||||||
|
const completions = await triplit.fetch(
|
||||||
|
Query('habit_completions').Where('habitId', '=', id)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const completion of completions) {
|
||||||
|
await triplit.delete('habit_completions', completion.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then delete the habit
|
||||||
|
return await triplit.delete('habits', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHabits(userId: string) {
|
||||||
|
return Query('habits')
|
||||||
|
.Where('userId', '=', userId)
|
||||||
|
.Order('createdAt', 'ASC');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHabit(id: string) {
|
||||||
|
return Query('habits')
|
||||||
|
.Where('id', '=', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createCompletion(data: CreateCompletionData) {
|
||||||
|
return await triplit.insert('habit_completions', {
|
||||||
|
habitId: data.habitId,
|
||||||
|
value: data.value,
|
||||||
|
failed: data.failed || false,
|
||||||
|
completedAt: data.completedAt || new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCompletion(id: string) {
|
||||||
|
return await triplit.delete('habit_completions', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCompletions(habitId: string, startDate?: Date, endDate?: Date) {
|
||||||
|
let query = Query('habit_completions')
|
||||||
|
.Where('habitId', '=', habitId);
|
||||||
|
|
||||||
|
if (startDate) {
|
||||||
|
query = query.Where('completedAt', '>=', startDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDate) {
|
||||||
|
query = query.Where('completedAt', '<=', endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Order('completedAt', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get habits with completions for a date range
|
||||||
|
export function getHabitsWithCompletions(userId: string, startDate: Date, endDate: Date) {
|
||||||
|
return Query('habits')
|
||||||
|
.Where('userId', '=', userId)
|
||||||
|
.Where([
|
||||||
|
{
|
||||||
|
exists: Query('habit_completions').Where([
|
||||||
|
['habitId', '=', '$1.id'],
|
||||||
|
['completedAt', '>=', startDate],
|
||||||
|
['completedAt', '<=', endDate]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.Include('completions')
|
||||||
|
.Order('createdAt', 'ASC');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHabitCompletions(habitId: string, startDate: Date, endDate: Date) {
|
||||||
|
return Query('habit_completions')
|
||||||
|
.Where('habitId', '=', habitId)
|
||||||
|
.Where('completedAt', '>=', startDate)
|
||||||
|
.Where('completedAt', '<=', endDate)
|
||||||
|
.Where('failed', '=', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function archiveHabit(id: string) {
|
||||||
|
return await updateHabit(id, { active: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCompletion(id: string, data: Partial<Omit<HabitCompletion, 'id' | 'habitId'>>) {
|
||||||
|
return await triplit.update('habit_completions', id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCompletionByDate(habitId: string, date: Date) {
|
||||||
|
// Create date range for the specific day (start of day to end of day)
|
||||||
|
const startOfDay = new Date(date);
|
||||||
|
startOfDay.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const endOfDay = new Date(date);
|
||||||
|
endOfDay.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
|
const completions = await triplit.fetch(
|
||||||
|
Query('habit_completions')
|
||||||
|
.Where('habitId', '=', habitId)
|
||||||
|
.Where('completedAt', '>=', startOfDay)
|
||||||
|
.Where('completedAt', '<=', endOfDay)
|
||||||
|
);
|
||||||
|
|
||||||
|
return completions[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function upsertCompletion(habitId: string, date: Date, value: number, failed: boolean = false) {
|
||||||
|
const existing = await getCompletionByDate(habitId, date);
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
return await updateCompletion(existing.id, { value, failed });
|
||||||
|
} else {
|
||||||
|
return await createCompletion({
|
||||||
|
habitId,
|
||||||
|
value,
|
||||||
|
failed,
|
||||||
|
completedAt: date
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset completions in date range to value 0
|
||||||
|
export async function resetCompletions(habitId: string, startDate: Date, endDate: Date) {
|
||||||
|
return await triplit.transact(async (tx) => {
|
||||||
|
const completions = await tx.fetch(
|
||||||
|
Query('habit_completions')
|
||||||
|
.Where('habitId', '=', habitId)
|
||||||
|
.Where('completedAt', '>=', startDate)
|
||||||
|
.Where('completedAt', '<=', endDate)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const completion of completions) {
|
||||||
|
await tx.update('habit_completions', completion.id, {
|
||||||
|
value: 0,
|
||||||
|
failed: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions.length;
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user