remove old messaging system
This commit is contained in:
@@ -81,7 +81,11 @@ export const hono = {
|
|||||||
return request(
|
return request(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
`/api/admin/${famId}/fam`,
|
`/api/admin/${famId}/fam`,
|
||||||
{ payday, ...(paydayTime !== undefined ? { paydayTime } : {}), ...(timezone !== undefined ? { timezone } : {}) },
|
{
|
||||||
|
payday,
|
||||||
|
...(paydayTime !== undefined ? { paydayTime } : {}),
|
||||||
|
...(timezone !== undefined ? { timezone } : {})
|
||||||
|
},
|
||||||
sessionHeaders(event)
|
sessionHeaders(event)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -111,11 +115,11 @@ export const hono = {
|
|||||||
sessionHeaders(event)
|
sessionHeaders(event)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async issueAllRewards(event: RequestEvent, famId: string, memberId: string, message?: string) {
|
async issueAllRewards(event: RequestEvent, famId: string, memberId: string) {
|
||||||
return request(
|
return request(
|
||||||
'POST',
|
'POST',
|
||||||
`/api/admin/${famId}/rewards/issue-all`,
|
`/api/admin/${famId}/rewards/issue-all`,
|
||||||
{ memberId, message },
|
{ memberId },
|
||||||
sessionHeaders(event)
|
sessionHeaders(event)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -196,14 +200,6 @@ export const hono = {
|
|||||||
sessionHeaders(event)
|
sessionHeaders(event)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async sendMessage(event: RequestEvent, famId: string, memberId: string, message: string) {
|
|
||||||
return request(
|
|
||||||
'POST',
|
|
||||||
`/api/admin/${famId}/send-message`,
|
|
||||||
{ memberId, message },
|
|
||||||
sessionHeaders(event)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async memberChores(event: RequestEvent, famId: string, memberId: string) {
|
async memberChores(event: RequestEvent, famId: string, memberId: string) {
|
||||||
return request(
|
return request(
|
||||||
'GET',
|
'GET',
|
||||||
|
|||||||
@@ -174,12 +174,8 @@ export const actions = {
|
|||||||
const famId = event.locals.session.famId;
|
const famId = event.locals.session.famId;
|
||||||
const fd = await event.request.formData();
|
const fd = await event.request.formData();
|
||||||
const rewardId = fd.get('id') as string;
|
const rewardId = fd.get('id') as string;
|
||||||
const message = fd.get('message') as string;
|
|
||||||
try {
|
try {
|
||||||
const record = await hono.admin.claimReward(event, famId, rewardId);
|
const record = await hono.admin.claimReward(event, famId, rewardId);
|
||||||
if (message) {
|
|
||||||
await hono.admin.sendMessage(event, famId, record.memberId, message);
|
|
||||||
}
|
|
||||||
return { record };
|
return { record };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: e instanceof Error ? e.message : 'Failed to claim reward' };
|
return { error: e instanceof Error ? e.message : 'Failed to claim reward' };
|
||||||
@@ -191,9 +187,8 @@ export const actions = {
|
|||||||
const famId = event.locals.session.famId;
|
const famId = event.locals.session.famId;
|
||||||
const fd = await event.request.formData();
|
const fd = await event.request.formData();
|
||||||
const memberId = fd.get('memberId') as string;
|
const memberId = fd.get('memberId') as string;
|
||||||
const message = fd.get('message') as string;
|
|
||||||
try {
|
try {
|
||||||
const result = await hono.admin.issueAllRewards(event, famId, memberId, message || undefined);
|
const result = await hono.admin.issueAllRewards(event, famId, memberId);
|
||||||
return { count: result.count };
|
return { count: result.count };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: e instanceof Error ? e.message : 'Failed to issue rewards' };
|
return { error: e instanceof Error ? e.message : 'Failed to issue rewards' };
|
||||||
|
|||||||
@@ -19,15 +19,6 @@
|
|||||||
isCompleteForPeriod
|
isCompleteForPeriod
|
||||||
} from '../../../../../timezone.ts';
|
} from '../../../../../timezone.ts';
|
||||||
|
|
||||||
interface Notification {
|
|
||||||
id: string;
|
|
||||||
famId: string;
|
|
||||||
memberId: string;
|
|
||||||
message: string;
|
|
||||||
read: boolean;
|
|
||||||
created: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
let role = $state(data.role || 'child');
|
let role = $state(data.role || 'child');
|
||||||
@@ -44,15 +35,6 @@
|
|||||||
|
|
||||||
let simulateEow = $state(!!(data.settings?.simulateEow ?? data.simulateEow));
|
let simulateEow = $state(!!(data.settings?.simulateEow ?? data.simulateEow));
|
||||||
let eowPreview = $state<any>(null);
|
let eowPreview = $state<any>(null);
|
||||||
const responseTags = [
|
|
||||||
'👏 well done',
|
|
||||||
'😊 really pleased',
|
|
||||||
'🎯 you deserved that',
|
|
||||||
'💪 great effort',
|
|
||||||
'🙏 thanks'
|
|
||||||
];
|
|
||||||
let selectedMessage = $state<Record<string, string>>({});
|
|
||||||
let customMessage = $state<Record<string, string>>({});
|
|
||||||
let toast = $state('');
|
let toast = $state('');
|
||||||
|
|
||||||
let parentMembers = $derived(famStore.initialized ? famStore.members : data.members || []);
|
let parentMembers = $derived(famStore.initialized ? famStore.members : data.members || []);
|
||||||
@@ -515,33 +497,8 @@
|
|||||||
loading = false;
|
loading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadAndDismiss();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadAndDismiss() {
|
|
||||||
if (!deviceToken || !famId) return;
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/members/notifications', {
|
|
||||||
headers: {
|
|
||||||
'x-device-token': deviceToken,
|
|
||||||
'x-device-famid': famId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!res.ok) return;
|
|
||||||
const all: Notification[] = await res.json();
|
|
||||||
for (const n of all.filter((n) => !n.read)) {
|
|
||||||
await fetch(`/api/members/notifications/${n.id}/dismiss`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'x-device-token': deviceToken,
|
|
||||||
'x-device-famid': famId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCompleted(assignedChoreId: string, date: string): boolean {
|
function isCompleted(assignedChoreId: string, date: string): boolean {
|
||||||
const a = assigned.find((x) => x.id === assignedChoreId);
|
const a = assigned.find((x) => x.id === assignedChoreId);
|
||||||
// Weekly chores are "done for the week" — complete if completed any day
|
// Weekly chores are "done for the week" — complete if completed any day
|
||||||
@@ -822,7 +779,6 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="memberId" value={m.id} />
|
<input type="hidden" name="memberId" value={m.id} />
|
||||||
<input type="hidden" name="message" value={selectedMessage[m.id] || ''} />
|
|
||||||
<Button type="submit" size="sm" variant="secondary"
|
<Button type="submit" size="sm" variant="secondary"
|
||||||
>Issue All ({cashRequested.length}) · £{requestedTotal.toFixed(2)}</Button
|
>Issue All ({cashRequested.length}) · £{requestedTotal.toFixed(2)}</Button
|
||||||
>
|
>
|
||||||
@@ -840,7 +796,6 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="id" value={r.id} />
|
<input type="hidden" name="id" value={r.id} />
|
||||||
<input type="hidden" name="message" value={selectedMessage[m.id] || ''} />
|
|
||||||
<div class="payment-row">
|
<div class="payment-row">
|
||||||
<span>{r.label}</span>
|
<span>{r.label}</span>
|
||||||
<div class="payment-right">
|
<div class="payment-right">
|
||||||
@@ -866,31 +821,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="response-area">
|
|
||||||
<p class="respond-label">Response:</p>
|
|
||||||
<div class="tags">
|
|
||||||
{#each responseTags as tag}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="tag"
|
|
||||||
class:selected={selectedMessage[m.id] === tag}
|
|
||||||
onclick={() =>
|
|
||||||
(selectedMessage[m.id] = selectedMessage[m.id] === tag ? '' : tag)}
|
|
||||||
>{tag}</button
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="custom-msg"
|
|
||||||
placeholder="Or type your own..."
|
|
||||||
bind:value={customMessage[m.id]}
|
|
||||||
oninput={() => {
|
|
||||||
if (customMessage[m.id]) selectedMessage[m.id] = customMessage[m.id];
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -1569,41 +1499,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
}
|
}
|
||||||
.respond-label {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #6b7280;
|
|
||||||
margin: 0.4rem 0 0.3rem;
|
|
||||||
}
|
|
||||||
.tags {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.3rem;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.tag {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
padding: 0.2rem 0.5rem;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: white;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.tag:hover {
|
|
||||||
background: #f3f4f6;
|
|
||||||
}
|
|
||||||
.tag.selected {
|
|
||||||
background: #6366f1;
|
|
||||||
color: white;
|
|
||||||
border-color: #6366f1;
|
|
||||||
}
|
|
||||||
.custom-msg {
|
|
||||||
margin-top: 0.3rem;
|
|
||||||
padding: 0.3rem 0.5rem;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: #dc2626;
|
color: #dc2626;
|
||||||
|
|||||||
+1
-97
@@ -1877,7 +1877,7 @@ app.post("/api/admin/:famId/rewards/issue-all", requireAdmin, async (c) => {
|
|||||||
try {
|
try {
|
||||||
const { famId } = c.req.param();
|
const { famId } = c.req.param();
|
||||||
const body = await c.req.json();
|
const body = await c.req.json();
|
||||||
const { memberId, message } = body;
|
const { memberId } = body;
|
||||||
if (!memberId) return c.json({ error: "memberId required" }, 400);
|
if (!memberId) return c.json({ error: "memberId required" }, 400);
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
// Find all claimable rewards for this member (unclaimed or requested)
|
// Find all claimable rewards for this member (unclaimed or requested)
|
||||||
@@ -1890,15 +1890,6 @@ app.post("/api/admin/:famId/rewards/issue-all", requireAdmin, async (c) => {
|
|||||||
await pb.update("rewards", r.id, { status: "claimed", claimedAt: now });
|
await pb.update("rewards", r.id, { status: "claimed", claimedAt: now });
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
// Send notification to member if message provided
|
|
||||||
if (message && count > 0) {
|
|
||||||
await pb.create("notifications", {
|
|
||||||
famId,
|
|
||||||
memberId,
|
|
||||||
message,
|
|
||||||
read: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return c.json({ count });
|
return c.json({ count });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return handleError(c, err);
|
return handleError(c, err);
|
||||||
@@ -1928,8 +1919,6 @@ app.post(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── Admin: Send notification message ────────────────────
|
|
||||||
|
|
||||||
app.get("/api/admin/:famId/profile", requireAdmin, async (c) => {
|
app.get("/api/admin/:famId/profile", requireAdmin, async (c) => {
|
||||||
try {
|
try {
|
||||||
const { famId } = c.req.param();
|
const { famId } = c.req.param();
|
||||||
@@ -1978,25 +1967,6 @@ app.patch("/api/admin/:famId/profile", requireAdmin, async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/admin/:famId/send-message", requireAdmin, async (c) => {
|
|
||||||
try {
|
|
||||||
const { famId } = c.req.param();
|
|
||||||
const body = await c.req.json();
|
|
||||||
if (!body.memberId || !body.message) {
|
|
||||||
return c.json({ error: "memberId and message required" }, 400);
|
|
||||||
}
|
|
||||||
const record = await pb.create("notifications", {
|
|
||||||
famId,
|
|
||||||
memberId: body.memberId,
|
|
||||||
message: body.message,
|
|
||||||
read: false,
|
|
||||||
});
|
|
||||||
return c.json(record);
|
|
||||||
} catch (err) {
|
|
||||||
return handleError(c, err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── Member: Claim a reward ─────────────────────────────
|
// ── Member: Claim a reward ─────────────────────────────
|
||||||
|
|
||||||
app.post("/api/members/rewards/:id/claim", requireDeviceToken, async (c) => {
|
app.post("/api/members/rewards/:id/claim", requireDeviceToken, async (c) => {
|
||||||
@@ -2024,19 +1994,6 @@ app.post("/api/members/rewards/:id/claim", requireDeviceToken, async (c) => {
|
|||||||
status: "requested",
|
status: "requested",
|
||||||
requestedAt: now,
|
requestedAt: now,
|
||||||
});
|
});
|
||||||
// Create notification for admin
|
|
||||||
const members = await pb.getList(
|
|
||||||
"members",
|
|
||||||
`famId = '${famId}' && id = '${memberId}'`,
|
|
||||||
);
|
|
||||||
const memberName = members.items?.[0]?.name || "Unknown";
|
|
||||||
const msg = `Claim requested by ${memberName}`;
|
|
||||||
await pb.create("notifications", {
|
|
||||||
famId,
|
|
||||||
memberId,
|
|
||||||
message: msg,
|
|
||||||
read: false,
|
|
||||||
});
|
|
||||||
return c.json(record);
|
return c.json(record);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return handleError(c, err);
|
return handleError(c, err);
|
||||||
@@ -2069,56 +2026,12 @@ app.post("/api/members/rewards/request-all", requireDeviceToken, async (c) => {
|
|||||||
});
|
});
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
|
||||||
const members = await pb.getList(
|
|
||||||
"members",
|
|
||||||
`famId = '${famId}' && id = '${memberId}'`,
|
|
||||||
);
|
|
||||||
const memberName = members.items?.[0]?.name || "Unknown";
|
|
||||||
const msg = `${count} claim(s) requested by ${memberName}`;
|
|
||||||
await pb.create("notifications", {
|
|
||||||
famId,
|
|
||||||
memberId,
|
|
||||||
message: msg,
|
|
||||||
read: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return c.json({ count });
|
return c.json({ count });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return handleError(c, err);
|
return handleError(c, err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Member: Notifications ──────────────────────────────
|
|
||||||
|
|
||||||
app.get("/api/members/notifications", requireDeviceToken, async (c) => {
|
|
||||||
try {
|
|
||||||
const famId = c.get("famId");
|
|
||||||
const memberId = c.get("memberId");
|
|
||||||
const data = await pb.getList(
|
|
||||||
"notifications",
|
|
||||||
`famId = '${famId}' && memberId = '${memberId}'`,
|
|
||||||
);
|
|
||||||
return c.json(data.items);
|
|
||||||
} catch (err) {
|
|
||||||
return handleError(c, err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post(
|
|
||||||
"/api/members/notifications/:id/dismiss",
|
|
||||||
requireDeviceToken,
|
|
||||||
async (c) => {
|
|
||||||
try {
|
|
||||||
const { id } = c.req.param();
|
|
||||||
const record = await pb.update("notifications", id, { read: true });
|
|
||||||
return c.json(record);
|
|
||||||
} catch (err) {
|
|
||||||
return handleError(c, err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ── Payday: release this period's unpaid cash as an auto-claimed ask ──
|
// ── Payday: release this period's unpaid cash as an auto-claimed ask ──
|
||||||
|
|
||||||
async function releaseWeek(famId: string) {
|
async function releaseWeek(famId: string) {
|
||||||
@@ -2178,15 +2091,6 @@ async function releaseWeek(famId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Kid notice: the fun moment.
|
|
||||||
try {
|
|
||||||
await pb.create("notifications", {
|
|
||||||
famId,
|
|
||||||
memberId: m.id,
|
|
||||||
message: `You've earned £${total.toFixed(2)}! Go get it from your parent ⭐`,
|
|
||||||
read: false,
|
|
||||||
});
|
|
||||||
} catch {}
|
|
||||||
breakdown.push({
|
breakdown.push({
|
||||||
memberId: m.id,
|
memberId: m.id,
|
||||||
name: m.name,
|
name: m.name,
|
||||||
|
|||||||
+89
-113
@@ -302,7 +302,9 @@ export async function migrate(): Promise<void> {
|
|||||||
missing.push({ name: "settleDate", type: "text", required: false });
|
missing.push({ name: "settleDate", type: "text", required: false });
|
||||||
}
|
}
|
||||||
if (missing.length) {
|
if (missing.length) {
|
||||||
console.log("[migrate] Adding rewards.claimable/settleDate (payday gating)...");
|
console.log(
|
||||||
|
"[migrate] Adding rewards.claimable/settleDate (payday gating)...",
|
||||||
|
);
|
||||||
rewardsCol2.fields.push(...missing);
|
rewardsCol2.fields.push(...missing);
|
||||||
await updateCollection(rewardsCol2.id, {
|
await updateCollection(rewardsCol2.id, {
|
||||||
name: "rewards",
|
name: "rewards",
|
||||||
@@ -370,47 +372,6 @@ export async function migrate(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 4. Create notifications collection if missing ──
|
|
||||||
const notifCol = await getCollection("notifications");
|
|
||||||
if (!notifCol) {
|
|
||||||
const famsCol = await getCollection("fams");
|
|
||||||
if (!famsCol) throw new Error("fams collection not found");
|
|
||||||
const membersCol = await getCollection("members");
|
|
||||||
if (!membersCol) throw new Error("members collection not found");
|
|
||||||
console.log("[migrate] Creating notifications collection...");
|
|
||||||
await createCollection({
|
|
||||||
name: "notifications",
|
|
||||||
type: "base",
|
|
||||||
listRule: "",
|
|
||||||
viewRule: "",
|
|
||||||
createRule: null,
|
|
||||||
updateRule: null,
|
|
||||||
deleteRule: null,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "famId",
|
|
||||||
type: "relation",
|
|
||||||
required: true,
|
|
||||||
collectionId: famsCol.id,
|
|
||||||
maxSelect: 1,
|
|
||||||
cascadeDelete: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "memberId",
|
|
||||||
type: "relation",
|
|
||||||
required: true,
|
|
||||||
collectionId: membersCol.id,
|
|
||||||
maxSelect: 1,
|
|
||||||
cascadeDelete: false,
|
|
||||||
},
|
|
||||||
{ name: "message", type: "text", required: true },
|
|
||||||
{ name: "read", type: "bool", required: false },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(` ↳ notifications already exists`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 5. Add payday field to fams if missing ──
|
// ── 5. Add payday field to fams if missing ──
|
||||||
const famsCol = await getCollection("fams");
|
const famsCol = await getCollection("fams");
|
||||||
if (famsCol) {
|
if (famsCol) {
|
||||||
@@ -547,10 +508,15 @@ export async function migrate(): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (fams.length) {
|
if (fams.length) {
|
||||||
console.log(` ✓ Backfilled timezone="auto" for ${fams.length} fam${fams.length > 1 ? "s" : ""}`);
|
console.log(
|
||||||
|
` ✓ Backfilled timezone="auto" for ${fams.length} fam${fams.length > 1 ? "s" : ""}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(" ↳ timezone backfill skipped:", err instanceof Error ? err.message : err);
|
console.log(
|
||||||
|
" ↳ timezone backfill skipped:",
|
||||||
|
err instanceof Error ? err.message : err,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 6. Backfill completions.date — strip timestamps to YYYY-MM-DD ──
|
// ── 6. Backfill completions.date — strip timestamps to YYYY-MM-DD ──
|
||||||
@@ -1334,78 +1300,88 @@ export async function migrate(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 7. Add todo fields to assigned_chores if missing ──
|
// ── 7. Add todo fields to assigned_chores if missing ──
|
||||||
const assignedCol = await getCollection("assigned_chores");
|
const assignedCol = await getCollection("assigned_chores");
|
||||||
if (assignedCol) {
|
if (assignedCol) {
|
||||||
let needsUpdate = false;
|
let needsUpdate = false;
|
||||||
|
|
||||||
// 7a. Make templateId non-required (todos don't use templates)
|
// 7a. Make templateId non-required (todos don't use templates)
|
||||||
const tplField = assignedCol.fields.find((f: any) => f.name === "templateId");
|
const tplField = assignedCol.fields.find(
|
||||||
if (tplField && tplField.required) {
|
(f: any) => f.name === "templateId",
|
||||||
console.log("[migrate] Making assigned_chores.templateId non-required...");
|
);
|
||||||
tplField.required = false;
|
if (tplField && tplField.required) {
|
||||||
needsUpdate = true;
|
console.log(
|
||||||
}
|
"[migrate] Making assigned_chores.templateId non-required...",
|
||||||
|
);
|
||||||
|
tplField.required = false;
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
// 7b. Add isTodo, startDate, completeBy fields
|
// 7b. Add isTodo, startDate, completeBy fields
|
||||||
const hasIsTodo = assignedCol.fields.some((f: any) => f.name === "isTodo");
|
const hasIsTodo = assignedCol.fields.some((f: any) => f.name === "isTodo");
|
||||||
if (!hasIsTodo) {
|
if (!hasIsTodo) {
|
||||||
console.log("[migrate] Adding todo fields to assigned_chores...");
|
console.log("[migrate] Adding todo fields to assigned_chores...");
|
||||||
assignedCol.fields.push(
|
assignedCol.fields.push(
|
||||||
{ name: "isTodo", type: "bool" },
|
{ name: "isTodo", type: "bool" },
|
||||||
{ name: "startDate", type: "text" },
|
{ name: "startDate", type: "text" },
|
||||||
{ name: "completeBy", type: "text" }
|
{ name: "completeBy", type: "text" },
|
||||||
);
|
);
|
||||||
needsUpdate = true;
|
needsUpdate = true;
|
||||||
} else {
|
} else {
|
||||||
console.log(` ↳ assigned_chores.isTodo already exists`);
|
console.log(` ↳ assigned_chores.isTodo already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsUpdate) {
|
if (needsUpdate) {
|
||||||
await updateCollection(assignedCol.id, {
|
await updateCollection(assignedCol.id, {
|
||||||
name: "assigned_chores",
|
name: "assigned_chores",
|
||||||
type: "base",
|
type: "base",
|
||||||
listRule: assignedCol.listRule,
|
listRule: assignedCol.listRule,
|
||||||
viewRule: assignedCol.viewRule,
|
viewRule: assignedCol.viewRule,
|
||||||
createRule: assignedCol.createRule,
|
createRule: assignedCol.createRule,
|
||||||
updateRule: assignedCol.updateRule,
|
updateRule: assignedCol.updateRule,
|
||||||
deleteRule: assignedCol.deleteRule,
|
deleteRule: assignedCol.deleteRule,
|
||||||
fields: assignedCol.fields,
|
fields: assignedCol.fields,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(` ↳ assigned_chores collection not found (will be created by seed)`);
|
console.log(
|
||||||
}
|
` ↳ assigned_chores collection not found (will be created by seed)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ── 8. Add completedAt timestamp to completions ──
|
// ── 8. Add completedAt timestamp to completions ──
|
||||||
const complCol = await getCollection("completions");
|
const complCol = await getCollection("completions");
|
||||||
if (complCol) {
|
if (complCol) {
|
||||||
const hasCompletedAt = complCol.fields.some((f: any) => f.name === "completedAt");
|
const hasCompletedAt = complCol.fields.some(
|
||||||
if (!hasCompletedAt) {
|
(f: any) => f.name === "completedAt",
|
||||||
console.log("[migrate] Adding completions.completedAt...");
|
);
|
||||||
complCol.fields.push({
|
if (!hasCompletedAt) {
|
||||||
name: "completedAt",
|
console.log("[migrate] Adding completions.completedAt...");
|
||||||
type: "date",
|
complCol.fields.push({
|
||||||
required: false,
|
name: "completedAt",
|
||||||
hidden: false,
|
type: "date",
|
||||||
});
|
required: false,
|
||||||
await updateCollection(complCol.id, {
|
hidden: false,
|
||||||
name: "completions",
|
});
|
||||||
type: "base",
|
await updateCollection(complCol.id, {
|
||||||
listRule: complCol.listRule,
|
name: "completions",
|
||||||
viewRule: complCol.viewRule,
|
type: "base",
|
||||||
createRule: complCol.createRule,
|
listRule: complCol.listRule,
|
||||||
updateRule: complCol.updateRule,
|
viewRule: complCol.viewRule,
|
||||||
deleteRule: complCol.deleteRule,
|
createRule: complCol.createRule,
|
||||||
fields: complCol.fields,
|
updateRule: complCol.updateRule,
|
||||||
});
|
deleteRule: complCol.deleteRule,
|
||||||
console.log(" ✓ completions.completedAt added");
|
fields: complCol.fields,
|
||||||
} else {
|
});
|
||||||
console.log(` ↳ completions.completedAt already exists`);
|
console.log(" ✓ completions.completedAt added");
|
||||||
}
|
} else {
|
||||||
} else {
|
console.log(` ↳ completions.completedAt already exists`);
|
||||||
console.log(` ↳ completions collection not found (will be created by seed)`);
|
}
|
||||||
}
|
} else {
|
||||||
|
console.log(
|
||||||
|
` ↳ completions collection not found (will be created by seed)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
console.log("[migrate] Done");
|
console.log("[migrate] Done");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user