add toggle fix and possible log out issue

This commit is contained in:
JCEEE
2026-09-14 12:09:44 +01:00
parent 70a38ee95b
commit 158a60f30c
8 changed files with 120 additions and 49 deletions
+18 -1
View File
@@ -10,6 +10,23 @@ import {
} from '@shared/timezone';
import { computeBonusProgress } from '@shared/bonus-progress';
import { famMeta } from './fam';
import { pbAdmin } from '$lib/server/pocketbase';
// `fams` reads via superuser: the acting token may predate RULE_OWN_FAM
// (migrate only bootstraps fresh stores), which otherwise logs a 404 and
// aborts evaluation. Fam-scoped by record id.
async function famMetaSU(famId: string) {
try {
const fam: any = await pbAdmin.getOne('fams', famId);
return {
payday: fam.payday !== undefined && fam.payday !== null ? Number(fam.payday) : 1,
paydayTime: fam.paydayTime || '18:00',
tz: resolveTz(fam.timezone || 'auto')
};
} catch {
return { payday: 1, paydayTime: '18:00', tz: resolveTz('auto') };
}
}
function resolveServerTz(tz?: string): string {
return resolveTz(tz || 'auto');
@@ -64,7 +81,7 @@ export async function evaluateFam(pb: any, famId: string) {
try {
allRewards = await pb.collection('rewards').getFullList({ filter: `famId = '${famId}'` });
} catch {}
const { payday: paydayEval, tz: tzEval } = await famMeta(pb, famId);
const { payday: paydayEval, tz: tzEval } = await famMetaSU(famId);
// Chores "due" this week per member (daily = 7, otherwise 1). Used by the
// `percent` threshold type to compute % of chores completed.
@@ -1,7 +1,27 @@
import { periodWindow } from '@shared/timezone';
import { famMeta } from './fam';
import { pbAdmin } from '$lib/server/pocketbase';
import { evaluateFam } from './bonuses';
// Reads/writes the acting user may not be permitted by PB rules:
// - `fams` view (prod may predate RULE_OWN_FAM; migrate only bootstraps fresh)
// - `assigned_chores` update (parent-only rule, but children claim shared chores)
// Both stay fam-scoped: the fam record id and the chore's own famId are checked.
async function famMetaSU(famId: string) {
const fam: any = await pbAdmin.getOne('fams', famId);
const { resolveTz } = await import('@shared/timezone');
return {
payday: fam.payday !== undefined && fam.payday !== null ? Number(fam.payday) : 1,
paydayTime: fam.paydayTime || '18:00',
tz: resolveTz(fam.timezone || 'auto')
};
}
async function claimChore(assignedChoreId: string, famId: string, memberId: string | '') {
const chore: any = await pbAdmin.getOne('assigned_chores', assignedChoreId);
if (!chore || chore.famId !== famId) throw new Error('Chore not found');
await pbAdmin.update('assigned_chores', assignedChoreId, { memberId });
}
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}'` }),
@@ -12,7 +32,7 @@ export async function myChores(pb: any, famId: string, memberId: string) {
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);
const { payday, paydayTime, tz } = await famMetaSU(famId);
return {
templates,
assigned,
@@ -39,13 +59,13 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
let filter: string;
if (isShared && !isTodo) {
// Shared non-todo: any completion in the period = already claimed
const { payday, tz } = await famMeta(pb, famId);
const { payday, tz } = await famMetaSU(famId);
const { from, to } = periodWindow(chore?.frequency, payday, tz);
filter = `assignedChoreId = '${assignedChoreId}' && date >= '${from}' && date < '${to}'`;
} else if (isTodo) {
filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}'`;
} else {
const { payday, tz } = await famMeta(pb, famId);
const { payday, tz } = await famMetaSU(famId);
const { from, to } = periodWindow(chore?.frequency, payday, tz);
filter = `assignedChoreId = '${assignedChoreId}' && memberId = '${memberId}' && date >= '${from}' && date < '${to}'`;
}
@@ -60,7 +80,7 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
}
// If this was a shared chore claimed by this member, un-share it
if (isShared && existing[0].memberId === memberId) {
await pb.collection('assigned_chores').update(assignedChoreId, { memberId: '' });
await claimChore(assignedChoreId, famId, '');
}
evaluateFam(pb, famId).catch(() => {});
return { completed: false };
@@ -89,7 +109,7 @@ export async function toggle(pb: any, famId: string, memberId: string, body: { a
});
// Claim the shared chore by setting memberId
if (isShared) {
await pb.collection('assigned_chores').update(assignedChoreId, { memberId });
await claimChore(assignedChoreId, famId, memberId);
}
evaluateFam(pb, famId).catch(() => {});
return { completed: true, record };
@@ -107,7 +127,7 @@ export async function revoke(pb: any, famId: string, completionId: string) {
.getFullList({ filter: `famId = '${famId}' && id = '${completion.assignedChoreId}'` });
const chore = choreList?.[0];
if (chore?.shared === true) {
await pb.collection('assigned_chores').update(chore.id, { memberId: '' });
await claimChore(chore.id, famId, '');
}
evaluateFam(pb, famId).catch(() => {});
return { revoked: true };