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

@@ -7,6 +7,12 @@ import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const TEST_IMAGE = path.join(__dirname, '../../.resources/crown.png')
/** Navigate to the parent children list and wait for the Add Child button to be ready. */
async function gotoParentList(page: import('@playwright/test').Page): Promise<void> {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Add Child' })).toBeVisible({ timeout: 10000 })
}
async function deleteNamedChildren(request: any, names: string[]) {
const res = await request.get('/api/child/list')
const data = await res.json()
@@ -38,7 +44,7 @@ test.describe('Create Child', () => {
await deleteNamedChildren(request, ['Alice'])
// 1. Navigate to app root - router redirects to /parent (children list) when parent-authenticated
await page.goto('/')
await gotoParentList(page)
await expect(page).toHaveURL('/parent')
// 2. Click the 'Add Child' FAB
@@ -123,7 +129,7 @@ test.describe('Create Child', () => {
await deleteNamedChildren(request, ['Grace'])
// 1. Navigate to app root - router redirects to /parent (children list)
await page.goto('/')
await gotoParentList(page)
await page.getByRole('button', { name: 'Add Child' }).click()
await expect(page.getByRole('heading', { name: 'Create Child' })).toBeVisible()

View File

@@ -2,10 +2,16 @@
import { test, expect } from '@playwright/test'
/** Navigate to the parent children list and wait for the Add Child button to be ready. */
async function gotoParentList(page: import('@playwright/test').Page): Promise<void> {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Add Child' })).toBeVisible({ timeout: 10000 })
}
test.describe('Create Child', () => {
test('Cancel navigates back without saving', async ({ page }) => {
// 1. Navigate to app root - router redirects to /parent (children list)
await page.goto('/')
// 1. Navigate to parent list and wait for it to fully load
await gotoParentList(page)
await page.getByRole('button', { name: 'Add Child' }).click()
await expect(page.getByRole('heading', { name: 'Create Child' })).toBeVisible()

View File

@@ -3,6 +3,12 @@
import { test, expect } from '@playwright/test'
import { STORAGE_STATE_CC } from '../../e2e-constants'
/** Navigate to the parent children list and wait for the Add Child button to be ready. */
async function gotoParentList(page: import('@playwright/test').Page): Promise<void> {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Add Child' })).toBeVisible({ timeout: 10000 })
}
test.describe('Create Child', () => {
test.describe.configure({ mode: 'serial' })
@@ -17,7 +23,7 @@ test.describe('Create Child', () => {
}
// 1. Open two browser tabs both on /parent (children list)
await page.goto('/')
await gotoParentList(page)
await expect(page).toHaveURL('/parent')
const tab2 = await context.newPage()
@@ -64,7 +70,7 @@ test.describe('Create Child', () => {
}
// 1. Tab 1: parent mode
await page.goto('/')
await gotoParentList(page)
await expect(page).toHaveURL('/parent')
// 2. Tab 2: isolated browser context so removing parentAuth doesn't affect Tab 1

View File

@@ -2,12 +2,18 @@
import { test, expect } from '@playwright/test'
/** Navigate to the parent children list and wait for the Add Child button to be ready. */
async function gotoParentList(page: import('@playwright/test').Page): Promise<void> {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Add Child' })).toBeVisible({ timeout: 10000 })
}
test.describe('Create Child', () => {
test.beforeEach(async ({ page }, testInfo) => {
test.skip(testInfo.project.name === 'chromium-no-pin', 'Requires parent-authenticated mode')
// Navigate to app root - router redirects to /parent (children list) when parent-authenticated
await page.goto('/')
// Navigate to parent list and wait for it to fully load before clicking Add Child
await gotoParentList(page)
await page.getByRole('button', { name: 'Add Child' }).click()
await expect(page.getByRole('heading', { name: 'Create Child' })).toBeVisible()
})