update migration of user fields
This commit is contained in:
@@ -407,6 +407,34 @@ async function ensureSchema(): Promise<void> {
|
|||||||
console.log('[migrate] Schema bootstrapped.');
|
console.log('[migrate] Schema bootstrapped.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add theme + newer auth fields to `users` on installs where the collection
|
||||||
|
// already exists (fresh installs get them via ensureUsers). Idempotent —
|
||||||
|
// only adds missing fields. Mirrors ensureFamFields.
|
||||||
|
async function ensureUserFields(): Promise<void> {
|
||||||
|
const usersCol = await getCollection('users');
|
||||||
|
if (!usersCol) return;
|
||||||
|
const has = (n: string) => usersCol.fields.some((f: any) => f.name === n);
|
||||||
|
const needed: any[] = [];
|
||||||
|
if (!has('pattern')) {
|
||||||
|
needed.push({ name: 'pattern', type: 'text', required: false });
|
||||||
|
}
|
||||||
|
if (!has('themeSize')) {
|
||||||
|
needed.push({ name: 'themeSize', type: 'text', required: false });
|
||||||
|
}
|
||||||
|
if (!has('themeOpacity')) {
|
||||||
|
needed.push({ name: 'themeOpacity', type: 'text', required: false });
|
||||||
|
}
|
||||||
|
if (!has('passwordResetToken')) {
|
||||||
|
needed.push({ name: 'passwordResetToken', type: 'text', required: false });
|
||||||
|
}
|
||||||
|
if (!has('passwordResetExpiry')) {
|
||||||
|
needed.push({ name: 'passwordResetExpiry', type: 'text', required: false });
|
||||||
|
}
|
||||||
|
if (needed.length) {
|
||||||
|
await updateCollection(usersCol.id, { ...usersCol, fields: [...usersCol.fields, ...needed] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the access-gating fields to `fams` on installs where it already exists
|
// Add the access-gating fields to `fams` on installs where it already exists
|
||||||
// (fresh installs get them via SCHEMA_PLAN). Idempotent — only adds missing
|
// (fresh installs get them via SCHEMA_PLAN). Idempotent — only adds missing
|
||||||
// fields.
|
// fields.
|
||||||
@@ -443,6 +471,7 @@ export async function migrate(): Promise<void> {
|
|||||||
await ensureSchema();
|
await ensureSchema();
|
||||||
// Runs even when the schema already exists (unlike ensureSchema's early
|
// Runs even when the schema already exists (unlike ensureSchema's early
|
||||||
// return) so new platform collections/fields/seed land on existing installs.
|
// return) so new platform collections/fields/seed land on existing installs.
|
||||||
|
await ensureUserFields();
|
||||||
await ensureFamFields();
|
await ensureFamFields();
|
||||||
await ensureBonusFields();
|
await ensureBonusFields();
|
||||||
await ensureTemplateFields();
|
await ensureTemplateFields();
|
||||||
@@ -960,7 +989,7 @@ function addDays(d: Date, n: number): Date {
|
|||||||
// Each kid's "completion personality" — Juno is reliable, Jackson solid, Jalipo spotty.
|
// Each kid's "completion personality" — Juno is reliable, Jackson solid, Jalipo spotty.
|
||||||
// Maps assigned chore customName → weekday (0=Sun) → probability of completion.
|
// Maps assigned chore customName → weekday (0=Sun) → probability of completion.
|
||||||
const COMPLETION_PROFILES: Record<string, Record<number, Record<string, number>>> = {
|
const COMPLETION_PROFILES: Record<string, Record<number, Record<string, number>>> = {
|
||||||
'Juno': {
|
Juno: {
|
||||||
0: { default: 0.85 }, // Sun
|
0: { default: 0.85 }, // Sun
|
||||||
1: { default: 0.95 }, // Mon
|
1: { default: 0.95 }, // Mon
|
||||||
2: { default: 0.9 },
|
2: { default: 0.9 },
|
||||||
@@ -969,7 +998,7 @@ const COMPLETION_PROFILES: Record<string, Record<number, Record<string, number>>
|
|||||||
5: { default: 0.85 }, // Fri
|
5: { default: 0.85 }, // Fri
|
||||||
6: { default: 0.8 } // Sat
|
6: { default: 0.8 } // Sat
|
||||||
},
|
},
|
||||||
'Jackson': {
|
Jackson: {
|
||||||
0: { default: 0.7 },
|
0: { default: 0.7 },
|
||||||
1: { default: 0.9 },
|
1: { default: 0.9 },
|
||||||
2: { default: 0.85 },
|
2: { default: 0.85 },
|
||||||
@@ -978,7 +1007,7 @@ const COMPLETION_PROFILES: Record<string, Record<number, Record<string, number>>
|
|||||||
5: { default: 0.7 },
|
5: { default: 0.7 },
|
||||||
6: { default: 0.6 }
|
6: { default: 0.6 }
|
||||||
},
|
},
|
||||||
'Jalipo': {
|
Jalipo: {
|
||||||
0: { default: 0.4 },
|
0: { default: 0.4 },
|
||||||
1: { default: 0.75 },
|
1: { default: 0.75 },
|
||||||
2: { default: 0.65 },
|
2: { default: 0.65 },
|
||||||
@@ -1018,12 +1047,18 @@ async function seedDemoCompletions(): Promise<void> {
|
|||||||
|
|
||||||
// Fetch members, assigned chores
|
// Fetch members, assigned chores
|
||||||
const [membersRes, assignedRes] = await Promise.all([
|
const [membersRes, assignedRes] = await Promise.all([
|
||||||
fetch(`${PB_ENDPOINT}/api/collections/users/records?filter=${encodeURIComponent(`famId='${famId}' && role='child'`)}&perPage=10`, {
|
fetch(
|
||||||
headers: { Authorization: `Bearer ${t}` }
|
`${PB_ENDPOINT}/api/collections/users/records?filter=${encodeURIComponent(`famId='${famId}' && role='child'`)}&perPage=10`,
|
||||||
}),
|
{
|
||||||
fetch(`${PB_ENDPOINT}/api/collections/assigned_chores/records?filter=famId='${famId}'&perPage=100`, {
|
headers: { Authorization: `Bearer ${t}` }
|
||||||
headers: { Authorization: `Bearer ${t}` }
|
}
|
||||||
})
|
),
|
||||||
|
fetch(
|
||||||
|
`${PB_ENDPOINT}/api/collections/assigned_chores/records?filter=famId='${famId}'&perPage=100`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${t}` }
|
||||||
|
}
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
const members: any[] = (await membersRes.json())?.items || [];
|
const members: any[] = (await membersRes.json())?.items || [];
|
||||||
const assigned: any[] = (await assignedRes.json())?.items || [];
|
const assigned: any[] = (await assignedRes.json())?.items || [];
|
||||||
@@ -1032,12 +1067,18 @@ async function seedDemoCompletions(): Promise<void> {
|
|||||||
|
|
||||||
// Delete existing completions and weekly_history for the demo family
|
// Delete existing completions and weekly_history for the demo family
|
||||||
const [oldCompsRes, oldHistoryRes] = await Promise.all([
|
const [oldCompsRes, oldHistoryRes] = await Promise.all([
|
||||||
fetch(`${PB_ENDPOINT}/api/collections/completions/records?filter=famId='${famId}'&perPage=500`, {
|
fetch(
|
||||||
headers: { Authorization: `Bearer ${t}` }
|
`${PB_ENDPOINT}/api/collections/completions/records?filter=famId='${famId}'&perPage=500`,
|
||||||
}),
|
{
|
||||||
fetch(`${PB_ENDPOINT}/api/collections/weekly_history/records?filter=famId='${famId}'&perPage=500`, {
|
headers: { Authorization: `Bearer ${t}` }
|
||||||
headers: { Authorization: `Bearer ${t}` }
|
}
|
||||||
})
|
),
|
||||||
|
fetch(
|
||||||
|
`${PB_ENDPOINT}/api/collections/weekly_history/records?filter=famId='${famId}'&perPage=500`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${t}` }
|
||||||
|
}
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
const oldComps: any[] = (await oldCompsRes.json())?.items || [];
|
const oldComps: any[] = (await oldCompsRes.json())?.items || [];
|
||||||
const oldHistory: any[] = (await oldHistoryRes.json())?.items || [];
|
const oldHistory: any[] = (await oldHistoryRes.json())?.items || [];
|
||||||
@@ -1061,7 +1102,10 @@ async function seedDemoCompletions(): Promise<void> {
|
|||||||
|
|
||||||
// Track per-member-per-week totals for weekly_history
|
// Track per-member-per-week totals for weekly_history
|
||||||
type WeekKey = string; // "YYYY-MM-DD" (Monday)
|
type WeekKey = string; // "YYYY-MM-DD" (Monday)
|
||||||
const weekTotals: Record<string, Record<string, { points: number; money: number; count: number }>> = {};
|
const weekTotals: Record<
|
||||||
|
string,
|
||||||
|
Record<string, { points: number; money: number; count: number }>
|
||||||
|
> = {};
|
||||||
|
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
const profile = COMPLETION_PROFILES[member.name] || COMPLETION_PROFILES['Jalipo'];
|
const profile = COMPLETION_PROFILES[member.name] || COMPLETION_PROFILES['Jalipo'];
|
||||||
@@ -1092,10 +1136,7 @@ async function seedDemoCompletions(): Promise<void> {
|
|||||||
|
|
||||||
const prob = dayProfile[chore.customName] || dayProfile.default;
|
const prob = dayProfile[chore.customName] || dayProfile.default;
|
||||||
const rng = demoRng(
|
const rng = demoRng(
|
||||||
famId.length * 1000 +
|
famId.length * 1000 + member.id.length * 100 + d * 10 + (chore.id?.charCodeAt(0) || 0)
|
||||||
member.id.length * 100 +
|
|
||||||
d * 10 +
|
|
||||||
(chore.id?.charCodeAt(0) || 0)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rng() > prob) continue; // missed this day
|
if (rng() > prob) continue; // missed this day
|
||||||
|
|||||||
@@ -1069,7 +1069,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card cols={1} title="Claims" accent={claimableRewards().length > 0 ? '#f97316' : undefined} class={claimableRewards().length > 0 ? 'claims-card' : ''}>
|
<Card cols={1} title="Claims" accent={hasClaimableRewards ? '#f97316' : undefined} class={hasClaimableRewards ? 'claims-card' : ''}>
|
||||||
{#if claimableRewards().length === 0}
|
{#if claimableRewards().length === 0}
|
||||||
<div class="claims-empty">
|
<div class="claims-empty">
|
||||||
{@html checkCircleIcon}<span>No outstanding claims</span>
|
{@html checkCircleIcon}<span>No outstanding claims</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user