feat: implement routines feature for child and parent modes
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m23s

- Added backend models for routines, routine items, and schedules.
- Created API endpoints for managing routines and their items.
- Implemented frontend components for routine creation, detail view, and assignment.
- Integrated routines into child view with a scrolling list and approval workflow.
- Added push notifications for routine confirmations and pending approvals.
- Refactored existing components to accommodate new routines functionality.
- Updated tests to cover new routines feature and ensure proper functionality.
This commit is contained in:
2026-04-28 23:30:52 -04:00
parent c840825549
commit 6bf10fda2f
19 changed files with 422 additions and 126 deletions

View File

@@ -622,10 +622,10 @@ onUnmounted(() => {
:sort-fn="childChoreSort"
>
<template #item="{ item }: { item: ChildTask }">
<span v-if="isChoreExpired(item)" class="chore-stamp">TOO LATE</span>
<span v-else-if="isChoreCompletedToday(item)" class="chore-stamp completed-stamp"
<span v-if="isChoreCompletedToday(item)" class="chore-stamp completed-stamp"
>COMPLETED</span
>
<span v-else-if="isChoreExpired(item)" class="chore-stamp">TOO LATE</span>
<span v-else-if="item.pending_status === 'pending'" class="chore-stamp pending-stamp"
>PENDING</span
>

View File

@@ -987,14 +987,14 @@ function goToAssignRewards() {
</Teleport>
</div>
<!-- TOO LATE badge -->
<span v-if="isChoreExpired(item)" class="chore-stamp">TOO LATE</span>
<!-- PENDING badge -->
<span v-else-if="isChorePending(item)" class="chore-stamp pending-stamp">PENDING</span>
<!-- COMPLETED badge -->
<span v-else-if="isChoreCompletedToday(item)" class="chore-stamp completed-stamp"
<span v-if="isChoreCompletedToday(item)" class="chore-stamp completed-stamp"
>COMPLETED</span
>
<!-- TOO LATE badge -->
<span v-else-if="isChoreExpired(item)" class="chore-stamp">TOO LATE</span>
<!-- PENDING badge -->
<span v-else-if="isChorePending(item)" class="chore-stamp pending-stamp">PENDING</span>
<div class="item-name">{{ item.name }}</div>
<img v-if="item.image_url" :src="item.image_url" alt="Task Image" class="item-image" />

View File

@@ -19,6 +19,7 @@ import {
subscribeToPushWithResult,
unsubscribeFromPush,
isPushOptedOut,
ensurePushSubscriptionSynced,
} from '@/services/pushSubscription'
const router = useRouter()
@@ -66,6 +67,16 @@ async function fetchUserProfile() {
if (userImageId.value) {
await loadAvatarImages(userImageId.value)
}
// Silent startup self-heal: if user has push enabled, ensure browser + backend
// subscription state are synchronized (no permission prompt).
if (
isParentAuthenticated.value &&
data.push_notifications_enabled !== false &&
!isPushOptedOut()
) {
void ensurePushSubscriptionSynced()
}
} catch (e) {
console.error('Error fetching user profile:', e)
profileLoading.value = false