feat: add onboarding tutorial for new users
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 3m4s

- Introduced a modular tutorial layer to guide new parents through the app setup process.
- Implemented a 3-step forced intro after first sign-in (PIN setup → child creation → chore creation).
- Added just-in-time contextual hints for various features as users encounter them.
- Persisted user progress on the backend with new fields in the User model.
- Created a new tutorial controller and step registry in the frontend for managing tutorial states.
- Added Help button for easy access to tutorial tips and a restart option in the user profile.
- Ensured accessibility and mobile responsiveness for the tutorial overlay.
- Included tests for backend and frontend functionalities related to the tutorial.
This commit is contained in:
2026-06-19 17:27:35 -04:00
parent d147bd6f27
commit e2bb9cd6b9
24 changed files with 1764 additions and 627 deletions
@@ -29,7 +29,7 @@
:data-idx="idx"
:class="{ 'drag-over': dragOverIdx === idx, dragging: draggingIdx === idx }"
>
<span class="drag-handle" title="Drag to reorder" @pointerdown.prevent="(e) => onPointerDown(e, idx)">⠿</span>
<span class="drag-handle" data-tutorial="routine-task-reorder" title="Drag to reorder" @pointerdown.prevent="(e) => onPointerDown(e, idx)">⠿</span>
<div class="item-left">
<img
v-if="item.image_url"
@@ -43,11 +43,17 @@
<button
type="button"
class="btn btn-secondary small-btn"
data-tutorial="routine-task-edit"
@click="startEditItem(idx)"
>
Edit
</button>
<button type="button" class="btn btn-secondary small-btn" @click="removeItem(idx)">
<button
type="button"
class="btn btn-secondary small-btn"
data-tutorial="routine-task-delete"
@click="removeItem(idx)"
>
Delete
</button>
</div>
@@ -162,13 +168,7 @@ const draggingIdx = ref<number | null>(null)
const dragOverIdx = ref<number | null>(null)
onMounted(async () => {
if (!isEdit.value) {
tutorialMaybeShow('create-routine')
tutorialMaybeShow(
'create-routine-add-task',
() => document.querySelector('.add-task-trigger') as HTMLElement | null,
)
}
if (!isEdit.value) tutorialMaybeShow('edit-routine-name')
if (isEdit.value && props.id) {
loading.value = true
try {