Files
famdone/frontend/src/lib/server/services/completions.ts
T

89 lines
3.2 KiB
TypeScript

import { periodWindow } from '@shared/timezone';
import { famMeta } from './fam';
import { evaluateFam } from './bonuses';
export async function myChores(pb: any, famId: string, memberId: string) {
const [templates, assigned, completions, rewards, bonusConfigs] = await Promise.all([
pb.collection('chore_templates').getFullList({ filter: `famId = '${famId}'` }),
pb.collection('assigned_chores').getFullList({ filter: `famId = '${famId}' && memberId = '${memberId}'` }),
pb.collection('completions').getFullList({ filter: `famId = '${famId}' && memberId = '${memberId}'` }),
pb.collection('rewards').getFullList({ filter: `famId = '${famId}' && memberId = '${memberId}'` }),
pb.collection('bonus_configs').getFullList({ filter: `famId = '${famId}' && status = 'active'` })
]);
const { payday, paydayTime, tz } = await famMeta(pb, famId);
return {
templates,
assigned,
completions,
rewards,
bonusConfigs,
payday,
paydayTime,
timezone: tz
};
}
export async function toggle(pb: any, famId: string, memberId: string, body: { assignedChoreId: string; date: string }) {
const { assignedChoreId, date } = body;
if (!assignedChoreId || !date) throw new Error('assignedChoreId and date required');
const choreList = await pb
.collection('assigned_chores')
.getFullList({ filter: `famId = '${famId}' && id = '${assignedChoreId}'` });
const chore = choreList?.[0];
const isTodo = chore?.isTodo;
let filter: string;
if (isTodo) {
filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}'`;
} else {
const { payday, tz } = await famMeta(pb, famId);
const { from, to } = periodWindow(chore?.frequency, payday, tz);
filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}' && date >= '${from}' && date < '${to}'`;
}
const existing = await pb
.collection('completions')
.getFullList({ filter });
if (existing?.length > 0) {
await pb.collection('completions').delete(existing[0].id);
// Un-completing a cash todo removes its one-time reward.
if (isTodo && existing[0].rewardId) {
await pb.collection('rewards').delete(existing[0].rewardId).catch(() => {});
}
evaluateFam(pb, famId).catch(() => {});
return { completed: false };
}
let rewardId: string | undefined;
if (isTodo && chore?.type === 'money' && Number(chore.value) > 0) {
const reward = await pb.collection('rewards').create({
famId,
memberId,
label: chore.customName || 'Cash todo',
value: Number(chore.value),
rewardType: 'cash',
status: 'unclaimed',
claimable: 'payday',
date
});
rewardId = reward.id;
}
const record = await pb.collection('completions').create({
famId,
memberId,
assignedChoreId,
date,
completedAt: new Date().toISOString(),
...(rewardId ? { rewardId } : {})
});
evaluateFam(pb, famId).catch(() => {});
return { completed: true, record };
}
export async function revoke(pb: any, famId: string, completionId: string) {
const completions = await pb
.collection('completions')
.getFullList({ filter: `famId = '${famId}' && id = '${completionId}'` });
if (!completions?.length) throw new Error('Completion not found');
await pb.collection('completions').delete(completionId);
evaluateFam(pb, famId).catch(() => {});
return { revoked: true };
}