89 lines
3.2 KiB
TypeScript
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 };
|
|
} |