Children pick a weekly reward goal, with a Monday reminder
A child chooses the reward they are aiming for (an admin can choose for them); the goal belongs to one week and starts empty each Monday. A child with none gets one daytime reminder that week, settled when they pick. Shown on Chores and the family board. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c97b62004e
commit
714680a94e
7 files changed
+73
-11
No files matched your search
+24
-1
@@ -1,7 +1,8 @@
|
||||
// Chores: when each kind of chore is due, who it is given to, ticking off and undoing, who may tick what, and the points for the week.
|
||||
const http = require('http'), assert = require('assert'), fs = require('fs'), os = require('os'), path = require('path');
|
||||
process.env.TZ = 'UTC'; // (the reminder goes out in the daytime by the server's clock)
|
||||
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'nehub-chores-'));
|
||||
const users = require('../users'), chores = require('../chores');
|
||||
const users = require('../users'), chores = require('../chores'), push = require('../push'), inbox = require('../inbox');
|
||||
const P = { a: users.create({ name: 'Ann', role: 'admin', handle: 'YQ' }), b: users.create({ name: 'Bob', role: 'user', tabs: ['chores'], handle: 'Yg' }), c: users.create({ name: 'Cy', role: 'user', tabs: ['chores'], handle: 'Yw' }), d: users.create({ name: 'Di', role: 'user', tabs: ['music'], handle: 'ZA' }) };
|
||||
users.update(P.b.id, { kid: { on: true } }); users.update(P.c.id, { kid: { on: true } }); // (points and rewards are for child accounts)
|
||||
const hub = http.createServer((req, res) => users.runAs(P[req.headers['x-user'] || 'a'], () => chores.handle(req, res))).listen(0, async () => {
|
||||
@@ -47,6 +48,28 @@ const hub = http.createServer((req, res) => users.runAs(P[req.headers['x-user']
|
||||
assert.equal((await j(await call('/week?date=' + chores.addDays(today, -7), {}, 'a'))).people.every(x => x.points === 0), true, 'last week had no points: they start again each week');
|
||||
const rc = await j(await post('/reward', { title: 'Bake together', points: 3, for: [P.c.id] })); wk = await j(await call('/week?date=' + today, {}, 'a')); assert.ok(wk.people.find(x => x.name === 'Cy').earned.includes(rc.id), 'a reward just for Cy: Cy earns it'); assert.ok(!wk.people.find(x => x.name === 'Bob').tiers.includes(rc.id), 'and it is not on Bob\'s list'); assert.equal((await post('/reward', { title: 'x', points: 3, for: [] })).status, 400, 'someone has to be chosen'); await post('/reward', { id: rc.id, remove: true });
|
||||
assert.equal((await post('/give', { user: P.b.id, reward: r5.id, undo: true })).status, 200); assert.equal((await j(await call('/week?date=' + today, {}, 'a'))).people.find(x => x.name === 'Bob').given.length, 0);
|
||||
// a child chooses the reward they are aiming for
|
||||
const rcy = await j(await post('/reward', { title: 'Cy only', points: 4, for: [P.c.id] }));
|
||||
const goalOf = async who => (await j(await call('/week?date=' + today, {}, 'a'))).people.find(x => x.name === who).goal;
|
||||
assert.equal((await post('/goal', { reward: r50.id }, 'b')).status, 200); assert.equal(await goalOf('Bob'), r50.id, 'Bob is aiming for the trip');
|
||||
assert.equal((await post('/goal', { reward: rcy.id }, 'b')).status, 400, 'not one that is only for Cy'); assert.equal((await post('/goal', { reward: 'nope' }, 'b')).status, 400);
|
||||
assert.equal((await post('/goal', { reward: r5.id, user: P.c.id }, 'b')).status, 200); assert.equal(await goalOf('Bob'), r5.id, 'a child only ever sets their own'); assert.equal(await goalOf('Cy'), null);
|
||||
assert.equal((await post('/goal', { reward: rcy.id, user: P.c.id }, 'a')).status, 200); assert.equal(await goalOf('Cy'), rcy.id, 'an admin can choose for a child');
|
||||
assert.equal((await post('/goal', { reward: r5.id }, 'a')).status, 400, 'goals are for children');
|
||||
const tmp = await j(await post('/reward', { title: 'Temp', points: 9 })); await post('/goal', { reward: tmp.id }, 'b'); assert.equal(await goalOf('Bob'), tmp.id); await post('/reward', { id: tmp.id, remove: true }); assert.equal(await goalOf('Bob'), null, 'a removed reward is no longer anyone\'s goal');
|
||||
await post('/goal', { reward: r5.id }, 'b'); assert.equal((await post('/goal', { reward: '' }, 'b')).status, 200); assert.equal(await goalOf('Bob'), null, 'no reward clears it');
|
||||
assert.equal((await j(await call('/', {}, 'b'))).me.kid, true, 'the page knows who is a child');
|
||||
// a goal is for one week and starts empty each Monday; a child with none is reminded once
|
||||
const wkGoal = async (who, date) => (await j(await call('/week?date=' + date, {}, 'a'))).people.find(x => x.name === who).goal, thisMon = (await j(await call('/week?date=' + today, {}, 'a'))).from;
|
||||
await post('/goal', { reward: r5.id }, 'b'); assert.equal(await wkGoal('Bob', today), r5.id); assert.equal(await wkGoal('Bob', chores.addDays(today, 7)), null, 'next week starts empty'); assert.equal(await wkGoal('Bob', chores.addDays(today, -7)), null, 'and last week had none');
|
||||
assert.equal(chores.goalNeeded(P.b.id, today), null, 'a goal is chosen: no reminder'); await post('/goal', { reward: '' }, 'b');
|
||||
assert.equal(chores.goalNeeded(P.b.id, today), thisMon, 'no goal yet: a reminder is due'); assert.equal(chores.goalNeeded(P.a.id, today), null, 'not for adults');
|
||||
const later = chores.addDays(thisMon, 14), at = new Date(Date.parse(later + 'T10:00:00Z')), mine = () => inbox.load(P.b.id).filter(i => i.tag === 'goal:' + later);
|
||||
await push.tick(at, true); assert.equal(mine().length, 1, 'a child with no goal is reminded in the daytime'); assert.equal(inbox.load(P.a.id).some(i => /^goal:/.test(i.tag)), false, 'not an adult');
|
||||
await push.tick(new Date(at.getTime() + 60e3), true); assert.equal(mine()[0].count, 1, 'only once a week');
|
||||
await post('/goal', { reward: '', user: P.c.id }); await push.tick(new Date(Date.parse(thisMon + 'T10:00:00Z') + 2 * 864e5), true); const cyRem = () => inbox.load(P.c.id).filter(i => i.tag === 'goal:' + thisMon); assert.equal(cyRem().length, 1); assert.equal(cyRem()[0].read, false);
|
||||
await post('/goal', { reward: rcy.id }, 'c'); assert.equal(cyRem()[0].read, true, 'choosing a goal settles the reminder');
|
||||
const early = new Date(Date.parse(chores.addDays(later, 7) + 'T03:00:00Z')); await push.tick(early, true); assert.equal(inbox.load(P.b.id).some(i => i.tag === 'goal:' + chores.addDays(later, 7)), false, 'not in the middle of the night');
|
||||
// delete removes its history
|
||||
assert.equal((await call('/' + once.id, { method: 'DELETE' }, 'b')).status, 403); assert.equal((await call('/' + once.id, { method: 'DELETE' })).status, 200); assert.equal((await j(await call('/summary', {}, 'a'))).people.find(x => x.name === 'Bob').points, 3);
|
||||
assert.equal((await call('/?date=nope')).status, 400);
|
||||
|
||||
Reference in new issue
Block a user