Files
maternal-app/maternal-web/public/sw.js
Andrei 40dbb2287a
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
feat: Implement comprehensive onboarding improvements with role-based family invites
This commit adds a complete onboarding improvements system including progress
tracking, streamlined UI, and role-based family invitation system.

## Backend Changes

### Database Migrations
- Add onboarding tracking fields to users table (onboarding_completed, onboarding_step, onboarding_data)
- Add role-based invite codes to families table (parent/caregiver/viewer codes with expiration)
- Add indexes for fast invite code lookups

### User Preferences Module
- Add UserPreferencesController with onboarding endpoints
- Add UserPreferencesService with progress tracking methods
- Add UpdateOnboardingProgressDto for validation
- Endpoints: GET/PUT /api/v1/preferences/onboarding, POST /api/v1/preferences/onboarding/complete

### Families Module - Role-Based Invites
- Add generateRoleInviteCode() - Generate role-specific codes with expiration
- Add getRoleInviteCodes() - Retrieve all active codes for a family
- Add joinFamilyWithRoleCode() - Join family with automatic role assignment
- Add revokeRoleInviteCode() - Revoke specific role invite codes
- Add sendEmailInvite() - Generate code and send email invitation
- Endpoints: POST/GET/DELETE /api/v1/families/:id/invite-codes, POST /api/v1/families/join-with-role, POST /api/v1/families/:id/email-invite

### Email Service
- Add sendFamilyInviteEmail() - Send role-based invitation emails
- Beautiful HTML templates with role badges (👨‍👩‍👧 parent, 🤝 caregiver, 👁️ viewer)
- Role-specific permission descriptions
- Graceful fallback if email sending fails

### Auth Service
- Fix duplicate family creation bug in joinFamily()
- Ensure users only join family once during onboarding

## Frontend Changes

### Onboarding Page
- Reduce steps from 5 to 4 (combined language + measurements)
- Replace card-based selection with dropdown selectors
- Add automatic progress saving after each step
- Add progress restoration on page mount
- Extract FamilySetupStep into reusable component

### Family Page
- Add RoleInvitesSection component with accordion UI
- Generate/view/copy/regenerate/revoke controls for each role
- Send email invites directly from UI
- Display expiration dates (e.g., "Expires in 5 days")
- Info tooltips explaining role permissions
- Only visible to users with parent role

### API Client
- Add role-based invite methods to families API
- Add onboarding progress methods to users API
- TypeScript interfaces for all new data structures

## Features

 Streamlined 4-step onboarding with dropdown selectors
 Automatic progress save/restore across sessions
 Role-based family invites (parent/caregiver/viewer)
 Beautiful email invitations with role descriptions
 Automatic role assignment when joining with invite codes
 Granular permission control per role
 Email fallback if sending fails
 All changes tested and production-ready

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 15:25:16 +00:00

2 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
if(!self.define){let e,a={};const s=(s,c)=>(s=new URL(s+".js",c).href,a[s]||new Promise(a=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=a,document.head.appendChild(e)}else e=s,importScripts(s),a()}).then(()=>{let e=a[s];if(!e)throw new Error(`Module ${s} didnt register its module`);return e}));self.define=(c,i)=>{const n=e||("document"in self?document.currentScript.src:"")||location.href;if(a[n])return;let t={};const d=e=>s(e,n),r={module:{uri:n},exports:t,require:d};a[n]=Promise.all(c.map(e=>r[e]||d(e))).then(e=>(i(...e),t))}}define(["./workbox-4d767a27"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"4471892493745c9bb059364e414ea5ac"},{url:"/_next/static/Hyo8VeuH7updWxR-hbgDI/_buildManifest.js",revision:"673df67655213af81147283455f8956d"},{url:"/_next/static/Hyo8VeuH7updWxR-hbgDI/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/1091.c762d795c6885f94.js",revision:"c762d795c6885f94"},{url:"/_next/static/chunks/1188.88c14dc0b9d46cf9.js",revision:"88c14dc0b9d46cf9"},{url:"/_next/static/chunks/1255-b2f7fd83e387a9e1.js",revision:"b2f7fd83e387a9e1"},{url:"/_next/static/chunks/1280-077bbec6d00a7de6.js",revision:"077bbec6d00a7de6"},{url:"/_next/static/chunks/1514-a6ed8a01b9885870.js",revision:"a6ed8a01b9885870"},{url:"/_next/static/chunks/1543-530e0f57f7af68aa.js",revision:"530e0f57f7af68aa"},{url:"/_next/static/chunks/164f4fb6.cb2a48d4da4418c4.js",revision:"cb2a48d4da4418c4"},{url:"/_next/static/chunks/189-453061dd646fdba4.js",revision:"453061dd646fdba4"},{url:"/_next/static/chunks/1930-cd8328eb1cfa4178.js",revision:"cd8328eb1cfa4178"},{url:"/_next/static/chunks/2239-b3f7ecc33c6fd306.js",revision:"b3f7ecc33c6fd306"},{url:"/_next/static/chunks/2262-26293d6453fcc927.js",revision:"26293d6453fcc927"},{url:"/_next/static/chunks/2619-04bc32f026a0d946.js",revision:"04bc32f026a0d946"},{url:"/_next/static/chunks/2931.14c1e0fb7788f4ba.js",revision:"14c1e0fb7788f4ba"},{url:"/_next/static/chunks/2f0b94e8.3186a98eb4c9012b.js",revision:"3186a98eb4c9012b"},{url:"/_next/static/chunks/3127-49a95e7cb556ace3.js",revision:"49a95e7cb556ace3"},{url:"/_next/static/chunks/3452-86647d15ff7842a5.js",revision:"86647d15ff7842a5"},{url:"/_next/static/chunks/3460-a2b6a712ec21acfb.js",revision:"a2b6a712ec21acfb"},{url:"/_next/static/chunks/3664-56dedfcaec4aaceb.js",revision:"56dedfcaec4aaceb"},{url:"/_next/static/chunks/3762-96365c71edc342bf.js",revision:"96365c71edc342bf"},{url:"/_next/static/chunks/4199.bc1715114dd19eda.js",revision:"bc1715114dd19eda"},{url:"/_next/static/chunks/4337-6c756374da7aa8e3.js",revision:"6c756374da7aa8e3"},{url:"/_next/static/chunks/4648-bc0ced32f57b916b.js",revision:"bc0ced32f57b916b"},{url:"/_next/static/chunks/4710-9f9aefe46e6a48d5.js",revision:"9f9aefe46e6a48d5"},{url:"/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js",revision:"100b9d70ed4e49c1"},{url:"/_next/static/chunks/5125-c990fc036d2a6ce4.js",revision:"c990fc036d2a6ce4"},{url:"/_next/static/chunks/5380-9004e1ac3565daca.js",revision:"9004e1ac3565daca"},{url:"/_next/static/chunks/5385-7ecda8e4ba984edc.js",revision:"7ecda8e4ba984edc"},{url:"/_next/static/chunks/5482-7535aa0aab02d518.js",revision:"7535aa0aab02d518"},{url:"/_next/static/chunks/551.26e2933365d2f96d.js",revision:"26e2933365d2f96d"},{url:"/_next/static/chunks/6088-c165c565edce02be.js",revision:"c165c565edce02be"},{url:"/_next/static/chunks/6181-66be9b76f10d48f6.js",revision:"66be9b76f10d48f6"},{url:"/_next/static/chunks/6357-0263657691f8e0c3.js",revision:"0263657691f8e0c3"},{url:"/_next/static/chunks/658-1d9d4c0c8b5fb129.js",revision:"1d9d4c0c8b5fb129"},{url:"/_next/static/chunks/670-a4ca0f366ee779f5.js",revision:"a4ca0f366ee779f5"},{url:"/_next/static/chunks/6873-ff265086321345c8.js",revision:"ff265086321345c8"},{url:"/_next/static/chunks/6886-40f1779ffff00d58.js",revision:"40f1779ffff00d58"},{url:"/_next/static/chunks/710-7e96cbf5d461482a.js",revision:"7e96cbf5d461482a"},{url:"/_next/static/chunks/7359-1abfb9f346309354.js",revision:"1abfb9f346309354"},{url:"/_next/static/chunks/7741-0af8b5a61d8e63d3.js",revision:"0af8b5a61d8e63d3"},{url:"/_next/static/chunks/7855-72c79224370eff7b.js",revision:"72c79224370eff7b"},{url:"/_next/static/chunks/787-032067ae978e62a8.js",revision:"032067ae978e62a8"},{url:"/_next/static/chunks/7902-e1f71c3b4c62bff9.js",revision:"e1f71c3b4c62bff9"},{url:"/_next/static/chunks/7981-1205285ee8c556da.js",revision:"1205285ee8c556da"},{url:"/_next/static/chunks/8221-d51102291d5ddaf9.js",revision:"d51102291d5ddaf9"},{url:"/_next/static/chunks/8241-eaf1b9c6054e9ad8.js",revision:"eaf1b9c6054e9ad8"},{url:"/_next/static/chunks/8412-8ce7440f3599e2d9.js",revision:"8ce7440f3599e2d9"},{url:"/_next/static/chunks/8423-ac92fec5ac4dabe7.js",revision:"ac92fec5ac4dabe7"},{url:"/_next/static/chunks/8466-ffa71cea7998f777.js",revision:"ffa71cea7998f777"},{url:"/_next/static/chunks/8544.74f59dd908783038.js",revision:"74f59dd908783038"},{url:"/_next/static/chunks/8746-92ff3ad56eb06d6e.js",revision:"92ff3ad56eb06d6e"},{url:"/_next/static/chunks/8900-ff82add2eebd43fa.js",revision:"ff82add2eebd43fa"},{url:"/_next/static/chunks/9205-f540995b767df00b.js",revision:"f540995b767df00b"},{url:"/_next/static/chunks/9333-edf14831f0a39549.js",revision:"edf14831f0a39549"},{url:"/_next/static/chunks/9392-2887c5e5703ed90a.js",revision:"2887c5e5703ed90a"},{url:"/_next/static/chunks/9397-40b8ac68e22a4d87.js",revision:"40b8ac68e22a4d87"},{url:"/_next/static/chunks/9515-e88b59e87a9d336f.js",revision:"e88b59e87a9d336f"},{url:"/_next/static/chunks/9517-17518b5fffe76114.js",revision:"17518b5fffe76114"},{url:"/_next/static/chunks/9580-031d243edbbe82e5.js",revision:"031d243edbbe82e5"},{url:"/_next/static/chunks/9738-d4ae78df35beeba7.js",revision:"d4ae78df35beeba7"},{url:"/_next/static/chunks/ad2866b8.e13a3cf75ccf0eb8.js",revision:"e13a3cf75ccf0eb8"},{url:"/_next/static/chunks/app/(auth)/forgot-password/page-ca00943cf66c3e17.js",revision:"ca00943cf66c3e17"},{url:"/_next/static/chunks/app/(auth)/login/page-6337793313f43fb1.js",revision:"6337793313f43fb1"},{url:"/_next/static/chunks/app/(auth)/onboarding/page-f759aa2ff8e242a7.js",revision:"f759aa2ff8e242a7"},{url:"/_next/static/chunks/app/(auth)/register/page-26c9ab5378556580.js",revision:"26c9ab5378556580"},{url:"/_next/static/chunks/app/(auth)/reset-password/page-2eec6b4142e79702.js",revision:"2eec6b4142e79702"},{url:"/_next/static/chunks/app/_not-found/page-95f11f5fe94340f1.js",revision:"95f11f5fe94340f1"},{url:"/_next/static/chunks/app/ai-assistant/page-3edb2cda7412d8b4.js",revision:"3edb2cda7412d8b4"},{url:"/_next/static/chunks/app/analytics/advanced/page-8dce8adb1ed3736a.js",revision:"8dce8adb1ed3736a"},{url:"/_next/static/chunks/app/analytics/page-938a3b366d2969b4.js",revision:"938a3b366d2969b4"},{url:"/_next/static/chunks/app/api/ai/chat/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/login/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/password-reset/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/register/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/health/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/tracking/feeding/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/voice/transcribe/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/children/page-40f1bfa952ee593c.js",revision:"40f1bfa952ee593c"},{url:"/_next/static/chunks/app/family/page-d7ded6a4620cb8d8.js",revision:"d7ded6a4620cb8d8"},{url:"/_next/static/chunks/app/history/page-36e2f94462dd67ae.js",revision:"36e2f94462dd67ae"},{url:"/_next/static/chunks/app/insights/page-296df3d508143098.js",revision:"296df3d508143098"},{url:"/_next/static/chunks/app/layout-974781e155547ac5.js",revision:"974781e155547ac5"},{url:"/_next/static/chunks/app/legal/cookies/page-c39a3fa6e27a8806.js",revision:"c39a3fa6e27a8806"},{url:"/_next/static/chunks/app/legal/eula/page-8015f749ab4dd660.js",revision:"8015f749ab4dd660"},{url:"/_next/static/chunks/app/legal/page-3de074f0b9741bc6.js",revision:"3de074f0b9741bc6"},{url:"/_next/static/chunks/app/legal/privacy/page-3cb58024b6fd8e21.js",revision:"3cb58024b6fd8e21"},{url:"/_next/static/chunks/app/legal/terms/page-b5a1c96cae251767.js",revision:"b5a1c96cae251767"},{url:"/_next/static/chunks/app/logout/page-83925cf53bb9c692.js",revision:"83925cf53bb9c692"},{url:"/_next/static/chunks/app/offline/page-28c005360c2b2736.js",revision:"28c005360c2b2736"},{url:"/_next/static/chunks/app/page-c5729e7d614eb749.js",revision:"c5729e7d614eb749"},{url:"/_next/static/chunks/app/settings/page-c89cad68dc101709.js",revision:"c89cad68dc101709"},{url:"/_next/static/chunks/app/track/activity/page-3767427eaacf5fff.js",revision:"3767427eaacf5fff"},{url:"/_next/static/chunks/app/track/diaper/page-c62c6bfb393c13a1.js",revision:"c62c6bfb393c13a1"},{url:"/_next/static/chunks/app/track/feeding/page-5acbeddc0db06597.js",revision:"5acbeddc0db06597"},{url:"/_next/static/chunks/app/track/growth/page-aac0bf91cb288a19.js",revision:"aac0bf91cb288a19"},{url:"/_next/static/chunks/app/track/medicine/page-dcad90e6224b0800.js",revision:"dcad90e6224b0800"},{url:"/_next/static/chunks/app/track/page-dd5ade1eb19ad389.js",revision:"dd5ade1eb19ad389"},{url:"/_next/static/chunks/app/track/sleep/page-b586a2d14249bb9a.js",revision:"b586a2d14249bb9a"},{url:"/_next/static/chunks/bc98253f.2a96f718cf128d0e.js",revision:"2a96f718cf128d0e"},{url:"/_next/static/chunks/framework-bd61ec64032c2de7.js",revision:"bd61ec64032c2de7"},{url:"/_next/static/chunks/main-520e5ec2d671abe7.js",revision:"520e5ec2d671abe7"},{url:"/_next/static/chunks/main-app-02fc3649960ba6c7.js",revision:"02fc3649960ba6c7"},{url:"/_next/static/chunks/pages/_app-4b3fb5e477a0267f.js",revision:"4b3fb5e477a0267f"},{url:"/_next/static/chunks/pages/_error-c970d8b55ace1b48.js",revision:"c970d8b55ace1b48"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-898276ad52dbad4b.js",revision:"898276ad52dbad4b"},{url:"/_next/static/css/dd1dff55aa5f7521.css",revision:"dd1dff55aa5f7521"},{url:"/_next/static/media/19cfc7226ec3afaa-s.woff2",revision:"9dda5cfc9a46f256d0e131bb535e46f8"},{url:"/_next/static/media/21350d82a1f187e9-s.woff2",revision:"4e2553027f1d60eff32898367dd4d541"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/ba9851c3c22cd980-s.woff2",revision:"9e494903d6b0ffec1a1e14d34427d44d"},{url:"/_next/static/media/c5fe6dc8356a8c31-s.woff2",revision:"027a89e9ab733a145db70f09b8a18b42"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/apple-touch-icon.png",revision:"fa2d4d791b90148a18d49bc3bfd7a43a"},{url:"/check-updates.js",revision:"bc016a0ceb6c72a5fe9ba02ad05d78be"},{url:"/favicon-16x16.png",revision:"db2da3355c89a6149f6d9ee35ebe6bf3"},{url:"/favicon-32x32.png",revision:"0fd88d56aa584bd0546d05ffc63ef777"},{url:"/icon-192x192.png",revision:"b8ef7f117472c4399cceffea644eb8bd"},{url:"/icons/icon-128x128.png",revision:"96cff3b189d9c1daa1edf470290a90cd"},{url:"/icons/icon-144x144.png",revision:"b627c346c431d7e306005aec5f51baff"},{url:"/icons/icon-152x152.png",revision:"012071830c13d310e51f833baed531af"},{url:"/icons/icon-192x192.png",revision:"dfb20132ddb628237eccd4b0e2ee4aaa"},{url:"/icons/icon-384x384.png",revision:"d032b25376232878a2a29b5688992a8d"},{url:"/icons/icon-512x512.png",revision:"ffda0043571d60956f4e321cba706670"},{url:"/icons/icon-72x72.png",revision:"cc89e74126e7e1109f0186774b3c0d77"},{url:"/icons/icon-96x96.png",revision:"32813cdad5b636fc09eec01c7d705936"},{url:"/manifest.json",revision:"5cbf1ecd33b05c4772688ce7d00c2c23"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/push-sw.js",revision:"45385b2ab1bb9d8db17e3707d4364890"},{url:"/vercel.svg",revision:"61c6b19abff40ea7acd577be818f3976"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:a,event:s,state:c})=>a&&"opaqueredirect"===a.type?new Response(a.body,{status:200,statusText:"OK",headers:a.headers}):a}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/api\/.*$/i,new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/.*/i,new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET")});