feat(tutorial): implement comprehensive tutorial system with step guidance
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m9s

- Added tutorial controller to manage tutorial state and progress.
- Introduced HelpButton component for contextual help throughout the application.
- Created various tutorial steps for onboarding and feature guidance.
- Integrated tutorial prompts in multiple components (ChildrenListView, LoginButton, ScheduleModal, etc.) to enhance user experience.
- Implemented logic to show tutorials based on user actions and state.
- Added functionality to dismiss and skip tutorial sessions.
- Established a mechanism to hydrate tutorial state from user profile.
This commit is contained in:
2026-05-26 16:54:45 -04:00
parent ec4912aa4a
commit d147bd6f27
23 changed files with 1338 additions and 5 deletions
@@ -1,9 +1,14 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import { ref, onMounted, onBeforeUnmount, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { getCachedImageUrl, revokeAllImageUrls } from '../../common/imageCache'
import { isParentAuthenticated } from '../../stores/auth'
import { eventBus } from '@/common/eventBus'
import {
maybeShow as tutorialMaybeShow,
markStepSeen as tutorialMark,
tutorialReady,
} from '@/tutorial/controller'
import type {
Child,
ChildModifiedEventPayload,
@@ -150,6 +155,22 @@ const createChild = () => {
router.push({ name: 'CreateChild' })
}
function maybeTriggerCreateChildTutorial() {
if (!isParentAuthenticated.value) return
if (loading.value) return
if (!tutorialReady.value) return
if (children.value.length === 0) {
tutorialMaybeShow('create-child', () => document.querySelector('.fab') as HTMLElement | null)
} else {
void tutorialMark('has-created-child')
}
}
watch([tutorialReady, loading, children, isParentAuthenticated], maybeTriggerCreateChildTutorial, {
immediate: false,
deep: true,
})
onMounted(async () => {
eventBus.on('child_modified', handleChildModified)
eventBus.on('child_task_triggered', handleChildTaskTriggered)
@@ -158,6 +179,7 @@ onMounted(async () => {
const listPromise = fetchChildren()
listPromise.then((list) => {
children.value = list
maybeTriggerCreateChildTutorial()
})
// listen for outside clicks to auto-close any open kebab menu
document.addEventListener('click', onDocClick, true)
@@ -214,6 +236,10 @@ const selectChild = (childId: string | number) => {
const openMenu = (childId: string | number, evt?: Event) => {
evt?.stopPropagation()
activeMenuFor.value = childId
tutorialMaybeShow(
'child-kebab',
() => document.querySelector('.kebab-menu') as HTMLElement | null,
)
}
const closeMenu = () => {
activeMenuFor.value = null