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
@@ -95,6 +95,54 @@
<button class="btn btn-primary" @click="closeDeleteError">Close</button>
</div>
</ModalDialog>
<!-- Help / Tutorial section -->
<section class="help-section" aria-labelledby="help-heading">
<h3 id="help-heading" class="help-heading">Help</h3>
<label class="help-row">
<span class="help-row-text">
<span class="help-row-label">Show tutorial tips</span>
<span class="help-row-desc">Show helpful tips as I use the app.</span>
</span>
<input
type="checkbox"
class="help-toggle"
:checked="tutorialEnabled"
@change="onToggleTutorial"
aria-label="Show tutorial tips"
/>
</label>
<button type="button" class="btn-link btn-link-space" @click="openRestartConfirm">
Restart tutorial
</button>
</section>
<!-- Restart confirmation -->
<ModalDialog
v-if="showRestartConfirm"
title="Restart tutorial?"
@close="showRestartConfirm = false"
>
<div class="modal-message">
Start the tour again from the beginning? You'll see the tips again as you use the app.
</div>
<div class="modal-actions">
<button class="btn btn-secondary" @click="showRestartConfirm = false">Cancel</button>
<button class="btn btn-primary" @click="confirmRestartTutorial">Restart</button>
</div>
</ModalDialog>
<!-- Restart success -->
<ModalDialog
v-if="showRestartSuccess"
title="Tour reset"
@close="showRestartSuccess = false"
>
<div class="modal-message">Tour reset — here we go!</div>
<div class="modal-actions">
<button class="btn btn-primary" @click="showRestartSuccess = false">OK</button>
</div>
</ModalDialog>
</div>
</template>
@@ -113,6 +161,11 @@ import {
import { parseErrorResponse, isEmailValid } from '@/common/api'
import { ALREADY_MARKED } from '@/common/errorCodes'
import { logoutUser, suppressForceLogout } from '@/stores/auth'
import {
tutorialEnabled,
setTutorialEnabled,
resetAllProgress,
} from '@/tutorial/controller'
import '@/assets/styles.css'
const router = useRouter()
@@ -136,6 +189,25 @@ const deleteErrorMessage = ref('')
const pushError = ref('')
const pushPermissionDenied = ref(false)
// Tutorial controls
const showRestartConfirm = ref(false)
const showRestartSuccess = ref(false)
async function onToggleTutorial(e: Event) {
const target = e.target as HTMLInputElement
await setTutorialEnabled(target.checked)
}
function openRestartConfirm() {
showRestartConfirm.value = true
}
async function confirmRestartTutorial() {
showRestartConfirm.value = false
await resetAllProgress()
showRestartSuccess.value = true
}
const initialData = ref<{
image_id: string | null
first_name: string
@@ -500,4 +572,45 @@ function closeDeleteError() {
outline: none;
border-color: var(--btn-primary, #4a90e2);
}
.help-section {
margin-top: 1.4rem;
padding-top: 1.2rem;
border-top: 1px solid var(--form-input-border, #cbd5e1);
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.help-heading {
margin: 0 0 0.2rem;
font-size: 1rem;
color: var(--form-heading, #667eea);
}
.help-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
cursor: pointer;
}
.help-row-text {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.help-row-label {
font-weight: 600;
color: var(--text-primary, #222);
}
.help-row-desc {
font-size: 0.88rem;
color: var(--text-secondary, #888);
}
.help-toggle {
width: 22px;
height: 22px;
margin-top: 2px;
accent-color: var(--btn-primary, #667eea);
cursor: pointer;
}
</style>