236 lines
6.2 KiB
TypeScript
236 lines
6.2 KiB
TypeScript
import { fail, redirect } from '@sveltejs/kit';
|
|
import { hono } from '$lib/server/hono';
|
|
import { memberApi } from '$lib/client/api';
|
|
import { SERVER_IP, PROXY_PORT } from '../../../../../config.ts';
|
|
|
|
const HONO_URL = `http://${SERVER_IP}:${PROXY_PORT}`;
|
|
|
|
export async function load(event) {
|
|
const session = event.locals.session;
|
|
|
|
// Parent (session auth) → admin overview
|
|
if (session) {
|
|
const famId = session.famId;
|
|
const famSlug = event.params.fam;
|
|
const username = event.params.username;
|
|
if (session.memberName && session.memberName !== username) {
|
|
throw redirect(303, `/${famSlug}/${session.memberName}`);
|
|
}
|
|
const [
|
|
members,
|
|
templates,
|
|
assigned,
|
|
summary,
|
|
fam,
|
|
rewards,
|
|
bonusConfigs,
|
|
completions,
|
|
settings
|
|
] = await Promise.all([
|
|
hono.admin.list(event, 'members', famId),
|
|
hono.admin.list(event, 'chore-templates', famId),
|
|
hono.admin.list(event, 'assigned-chores', famId),
|
|
hono.admin.weeklySummary(event, famId),
|
|
hono.admin.fam(event, famId),
|
|
hono.admin.rewards(event, famId),
|
|
hono.admin.bonusConfigs(event, famId),
|
|
hono.admin.completions(event, famId),
|
|
hono.admin.settings(event, famId).catch(() => ({}))
|
|
]);
|
|
return {
|
|
role: 'parent',
|
|
members,
|
|
templates,
|
|
assigned,
|
|
summary,
|
|
fam,
|
|
famSlug,
|
|
rewards,
|
|
bonusConfigs,
|
|
completions,
|
|
settings
|
|
};
|
|
}
|
|
|
|
// Child (device token) → kanban
|
|
const deviceToken =
|
|
event.cookies.get('device_token') || event.url.searchParams.get('token') || '';
|
|
const famSlug = event.params.fam;
|
|
const username = event.params.username;
|
|
|
|
if (!deviceToken) {
|
|
return {
|
|
role: 'child',
|
|
token: '',
|
|
memberId: '',
|
|
verified: false,
|
|
famId: '',
|
|
templates: [],
|
|
assigned: [],
|
|
completions: [],
|
|
rewards: [],
|
|
bonusConfigs: [],
|
|
tallies: {}
|
|
};
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${HONO_URL}/api/members/verify-token`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ deviceToken, famSlug })
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok || data.name !== username) {
|
|
return {
|
|
role: 'child',
|
|
token: deviceToken,
|
|
memberId: '',
|
|
verified: false,
|
|
famId: '',
|
|
memberName: '',
|
|
memberColor: '',
|
|
templates: [],
|
|
assigned: [],
|
|
completions: [],
|
|
rewards: [],
|
|
bonusConfigs: [],
|
|
tallies: {}
|
|
};
|
|
}
|
|
|
|
let chores: any = {};
|
|
try {
|
|
chores = await memberApi.myChores(deviceToken, data.famId);
|
|
} catch {}
|
|
|
|
return {
|
|
role: 'child',
|
|
token: deviceToken,
|
|
memberId: data.memberId,
|
|
famId: data.famId,
|
|
verified: true,
|
|
memberName: data.name,
|
|
memberColor: data.color,
|
|
templates: chores.templates || [],
|
|
assigned: chores.assigned || [],
|
|
completions: chores.completions || [],
|
|
rewards: chores.rewards || [],
|
|
bonusConfigs: chores.bonusConfigs || [],
|
|
tallies: chores.tallies || {},
|
|
payday: chores.payday,
|
|
paydayTime: chores.paydayTime || '18:00',
|
|
timezone: chores.timezone || 'auto',
|
|
simulateEow: !!chores.simulateEow
|
|
};
|
|
} catch {
|
|
return {
|
|
role: 'child',
|
|
token: deviceToken,
|
|
memberId: '',
|
|
verified: false,
|
|
famId: '',
|
|
templates: [],
|
|
assigned: [],
|
|
completions: [],
|
|
rewards: [],
|
|
bonusConfigs: [],
|
|
tallies: {},
|
|
payday: 1,
|
|
paydayTime: '18:00',
|
|
timezone: 'auto'
|
|
};
|
|
}
|
|
}
|
|
|
|
export const actions = {
|
|
setEow: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
const fd = await event.request.formData();
|
|
const on = fd.get('on') === 'true';
|
|
try {
|
|
const result = await hono.admin.updateSettings(event, famId, { simulateEow: on });
|
|
return { simulateEow: result.simulateEow };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to update settings' };
|
|
}
|
|
},
|
|
|
|
previewEow: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
try {
|
|
const preview = await hono.admin.eowPreview(event, famId);
|
|
await hono.admin.updateSettings(event, famId, { simulateEow: true });
|
|
return { preview, simulateEow: true };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to preview payday' };
|
|
}
|
|
},
|
|
|
|
claim: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
const fd = await event.request.formData();
|
|
const rewardId = fd.get('id') as string;
|
|
const message = fd.get('message') as string;
|
|
try {
|
|
const record = await hono.admin.claimReward(event, famId, rewardId);
|
|
if (message) {
|
|
await hono.admin.sendMessage(event, famId, record.memberId, message);
|
|
}
|
|
return { record };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to claim reward' };
|
|
}
|
|
},
|
|
|
|
issueAll: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
const fd = await event.request.formData();
|
|
const memberId = fd.get('memberId') as string;
|
|
const message = fd.get('message') as string;
|
|
try {
|
|
const result = await hono.admin.issueAllRewards(event, famId, memberId, message || undefined);
|
|
return { count: result.count };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to issue rewards' };
|
|
}
|
|
},
|
|
|
|
revoke: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
const fd = await event.request.formData();
|
|
const completionId = fd.get('id') as string;
|
|
try {
|
|
await hono.admin.revokeCompletion(event, famId, completionId);
|
|
return { revoked: true, id: completionId };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to revoke' };
|
|
}
|
|
},
|
|
|
|
trigger: async (event) => {
|
|
if (!event.locals.session) throw redirect(303, '/login');
|
|
const famId = event.locals.session.famId;
|
|
const fd = await event.request.formData();
|
|
const configId = fd.get('configId') as string;
|
|
const memberId = fd.get('memberId') as string;
|
|
if (!configId) return { error: 'Config ID required' };
|
|
try {
|
|
const result = await hono.admin.triggerBonusConfig(
|
|
event,
|
|
famId,
|
|
configId,
|
|
memberId || undefined
|
|
);
|
|
return { records: result.records || [] };
|
|
} catch (e) {
|
|
return { error: e instanceof Error ? e.message : 'Failed to trigger' };
|
|
}
|
|
}
|
|
};
|