aboutsummaryrefslogtreecommitdiff
path: root/src/lib/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/server')
-rw-r--r--src/lib/server/db/schema.ts1
-rw-r--r--src/lib/server/services/routines.test.ts36
-rw-r--r--src/lib/server/services/routines.ts42
3 files changed, 71 insertions, 8 deletions
diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts
index 23e10f5..bac1c46 100644
--- a/src/lib/server/db/schema.ts
+++ b/src/lib/server/db/schema.ts
@@ -52,6 +52,7 @@ export const routines = sqliteTable(
.references(() => projects.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
frequencyDays: integer('frequency_days').notNull(),
+ description: text('description').notNull().default(''),
archived: integer('archived', { mode: 'boolean' }).notNull().default(false),
createdAt: text('created_at').notNull(),
},
diff --git a/src/lib/server/services/routines.test.ts b/src/lib/server/services/routines.test.ts
index 9e1f429..05abcaf 100644
--- a/src/lib/server/services/routines.test.ts
+++ b/src/lib/server/services/routines.test.ts
@@ -33,7 +33,11 @@ describe('routines service', () => {
it('edit inserts a new snapshot version and keeps history', async () => {
const r = await add(projectId, 'clean kitchen', 7, db);
- const updated = await edit(r.id, 'clean kitchen deeply', 3, db);
+ const updated = await edit(
+ r.id,
+ { name: 'clean kitchen deeply', frequencyDays: 3 },
+ db,
+ );
expect(updated.version).toBe(2);
expect(updated.frequencyDays).toBe(3);
@@ -43,6 +47,34 @@ describe('routines service', () => {
expect(versions[1].frequencyDays).toBe(3);
});
+ it('new routines start with an empty description', async () => {
+ const r = await add(projectId, 'clean kitchen', 7, db);
+ expect(r.description).toBe('');
+ });
+
+ it('edit is partial: name/frequency edits keep the description and vice-versa', async () => {
+ const r = await add(projectId, 'clean kitchen', 7, db);
+ const described = await edit(r.id, { description: 'wax on, wax off' }, db);
+ expect(described.name).toBe('clean kitchen');
+ expect(described.frequencyDays).toBe(7);
+ expect(described.description).toBe('wax on, wax off');
+
+ const renamed = await edit(r.id, { name: 'clean the whole kitchen' }, db);
+ expect(renamed.version).toBe(3);
+ expect(renamed.description).toBe('wax on, wax off');
+ expect(renamed.frequencyDays).toBe(7);
+
+ const refreq = await edit(r.id, { frequencyDays: 14 }, db);
+ expect(refreq.version).toBe(4);
+ expect(refreq.name).toBe('clean the whole kitchen');
+ expect(refreq.description).toBe('wax on, wax off');
+
+ const cleared = await edit(r.id, { description: '' }, db);
+ expect(cleared.version).toBe(5);
+ expect(cleared.description).toBe('');
+ expect(cleared.name).toBe('clean the whole kitchen');
+ });
+
it('archive inserts an archived snapshot without removing history', async () => {
const r = await add(projectId, 'clean kitchen', 7, db);
await archive(r.id, db);
@@ -58,7 +90,7 @@ describe('routines service', () => {
it('complete records the snapshot version it was made against', async () => {
const r = await add(projectId, 'clean kitchen', 7, db);
- await edit(r.id, 'clean kitchen', 5, db);
+ await edit(r.id, { frequencyDays: 5 }, db);
const doneAt = new Date().toISOString();
const completion = await complete(r.id, 'with vinegar', doneAt, db);
expect(completion.routineVersion).toBe(2);
diff --git a/src/lib/server/services/routines.ts b/src/lib/server/services/routines.ts
index e9e0c45..c3a5eff 100644
--- a/src/lib/server/services/routines.ts
+++ b/src/lib/server/services/routines.ts
@@ -1,6 +1,6 @@
import { db as defaultDb } from '$lib/server/db';
import { routines, routineCompletions, projects } from '$lib/server/db/schema';
-import { and, eq, inArray, sql } from 'drizzle-orm';
+import { eq, inArray, desc, sql } from 'drizzle-orm';
import type { AppDatabase } from './groceries';
import type { Routine, RoutineCompletion } from './types';
import {
@@ -40,11 +40,15 @@ export async function add(
return inserted;
}
-/** Edit = insert a new snapshot row with version+1; history stays intact. */
+/**
+ * Edit = insert a new snapshot row with version+1; history stays intact.
+ * Only the given fields change; the others are copied from the current
+ * snapshot so a name/frequency edit never touches the description and
+ * vice-versa.
+ */
export async function edit(
id: number,
- name: string,
- frequencyDays: number,
+ fields: Partial<Pick<Routine, 'name' | 'frequencyDays' | 'description'>>,
db = defaultDb,
): Promise<Routine> {
const [current] = await db
@@ -61,8 +65,9 @@ export async function edit(
id: current.id,
version: current.version + 1,
projectId: current.projectId,
- name,
- frequencyDays,
+ name: fields.name ?? current.name,
+ frequencyDays: fields.frequencyDays ?? current.frequencyDays,
+ description: fields.description ?? current.description,
archived: current.archived,
createdAt: current.createdAt,
})
@@ -97,6 +102,31 @@ export async function loadAllVersions(
.orderBy(routines.version);
}
+/** Current (max-version) snapshot of a routine, or undefined when unknown. */
+export async function loadCurrent(
+ routineId: number,
+ db = defaultDb,
+): Promise<Routine | undefined> {
+ const [row] = await db
+ .select()
+ .from(routines)
+ .where(eq(routines.id, routineId))
+ .orderBy(sql`${routines.version} DESC`)
+ .limit(1);
+ return row;
+}
+
+export async function loadCompletions(
+ routineId: number,
+ db = defaultDb,
+): Promise<RoutineCompletion[]> {
+ return db
+ .select()
+ .from(routineCompletions)
+ .where(eq(routineCompletions.routineId, routineId))
+ .orderBy(desc(routineCompletions.doneAt));
+}
+
export async function complete(
routineId: number,
note: string | null,