Skip to content

Commit 8d9dacf

Browse files
committed
implemented intro demo
1 parent a283618 commit 8d9dacf

8 files changed

Lines changed: 777 additions & 51 deletions

File tree

web/components/header.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ <h1 class="logo">
8888
</button>
8989
</li>
9090

91+
<li>
92+
<button class="menu-btn" type="button" data-tour-start>
93+
Take a Tour
94+
</button>
95+
</li>
96+
9197
<li class="admin-only is-hidden">
9298
<button class="menu-btn" onclick="window.location.href='admin.html'">
9399
Admin

web/scripts/default.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and manages dashboard view preference.
99

1010
import { api } from "./api.js";
1111
import { initWalterLens } from "./walterlens.js";
12+
import { initAppTour } from "./tour.js";
1213

1314
/* ===============================================
1415
DEVELOPMENT AUTH GUARD TOGGLE
@@ -118,6 +119,7 @@ document.addEventListener("DOMContentLoaded", () => {
118119
applyCachedAppName();
119120
updateAppName();
120121
initWalterLens();
122+
initAppTour();
121123
initAchievementCelebrations();
122124
initNotifications();
123125
initInactivityTimeoutMonitor();

web/scripts/profile.js

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -582,10 +582,63 @@ const clearStatusSoon = (ms = 2000) => {
582582
}, ms);
583583
};
584584

585+
let achievementHintEl = null;
586+
587+
const ensureAchievementHintEl = () => {
588+
if (achievementHintEl) return achievementHintEl;
589+
achievementHintEl = document.createElement("div");
590+
achievementHintEl.className = "achievement-float-hint";
591+
achievementHintEl.setAttribute("role", "tooltip");
592+
document.body.appendChild(achievementHintEl);
593+
return achievementHintEl;
594+
};
595+
596+
const showAchievementHint = (card) => {
597+
const text = String(card?.dataset?.achievementHint || "").trim();
598+
if (!text) return;
599+
const hint = ensureAchievementHintEl();
600+
hint.textContent = text;
601+
hint.classList.add("is-visible");
602+
hint.style.top = "0px";
603+
hint.style.left = "0px";
604+
605+
requestAnimationFrame(() => {
606+
const rect = card.getBoundingClientRect();
607+
const hintRect = hint.getBoundingClientRect();
608+
const padding = 12;
609+
let top = rect.top - hintRect.height - 12;
610+
let left = rect.left + rect.width / 2 - hintRect.width / 2;
611+
612+
if (left < padding) left = padding;
613+
if (left + hintRect.width > window.innerWidth - padding) {
614+
left = window.innerWidth - hintRect.width - padding;
615+
}
616+
if (top < padding) {
617+
top = rect.bottom + 12;
618+
}
619+
620+
hint.style.top = `${top}px`;
621+
hint.style.left = `${left}px`;
622+
});
623+
};
624+
625+
const hideAchievementHint = () => {
626+
if (!achievementHintEl) return;
627+
achievementHintEl.classList.remove("is-visible");
628+
};
629+
585630
const renderAchievements = (payload) => {
586631
if (!achievementGrid) return;
587632
const list = Array.isArray(payload?.achievements) ? payload.achievements : [];
588633
const summary = payload?.summary || {};
634+
const fallbackDescriptions = {
635+
first_record: "Add your first record.",
636+
five_records: "Add 5 total records.",
637+
};
638+
const fallbackByTitle = {
639+
"first record": "Add your first record.",
640+
"tracking momentum": "Add 5 total records.",
641+
};
589642

590643
if (achievementStatus) {
591644
achievementStatus.textContent = list.length
@@ -633,16 +686,28 @@ const renderAchievements = (payload) => {
633686
card.appendChild(badge);
634687
card.appendChild(body);
635688

636-
const description = String(item.description || "").trim();
689+
const titleKey = String(item.title || "").trim().toLowerCase();
690+
const description = String(
691+
item.description ||
692+
fallbackDescriptions[item.key] ||
693+
fallbackByTitle[titleKey] ||
694+
""
695+
).trim();
637696
if (description) {
638-
const hint = document.createElement("span");
639-
hint.className = "achievement-hint";
640-
hint.textContent = description;
641-
card.appendChild(hint);
697+
card.dataset.achievementHint = description;
698+
card.tabIndex = 0;
699+
card.addEventListener("mouseenter", () => showAchievementHint(card));
700+
card.addEventListener("mouseleave", hideAchievementHint);
701+
card.addEventListener("focus", () => showAchievementHint(card));
702+
card.addEventListener("blur", hideAchievementHint);
642703
}
643704

644705
achievementGrid.appendChild(card);
645706
});
707+
708+
achievementGrid.addEventListener("scroll", hideAchievementHint, { passive: true });
709+
window.addEventListener("scroll", hideAchievementHint, { passive: true });
710+
window.addEventListener("resize", hideAchievementHint);
646711
};
647712

648713
async function loadAchievements() {

web/scripts/register.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,22 @@ document.addEventListener("DOMContentLoaded", () => {
3131
const legalCache = new Map();
3232
const legalSequence = ["terms", "privacy"];
3333
const APP_NAME_PLACEHOLDER = "<AppName>";
34+
const TOUR_STORAGE_KEY = "app_tour_state_v1";
35+
const TOUR_ACTIVE_KEY = "app_tour_active_v1";
3436
let legalFlowActive = false;
3537
let legalFlowStepIndex = 0;
3638
let suppressAgreeEvent = false;
3739

40+
const startAppTour = () => {
41+
localStorage.setItem(TOUR_ACTIVE_KEY, "true");
42+
localStorage.setItem(TOUR_STORAGE_KEY, JSON.stringify({ index: 0 }));
43+
};
44+
3845
if (year) year.textContent = new Date().getFullYear();
3946

4047
const googleRedirect = api.auth.consumeGoogleRedirect();
4148
if (googleRedirect?.token || googleRedirect?.success) {
49+
startAppTour();
4250
window.location.href = "home.html";
4351
return;
4452
}
@@ -440,6 +448,7 @@ document.addEventListener("DOMContentLoaded", () => {
440448
const result = await api.auth.register(email, password, fullName);
441449

442450
showMsg("✅ Account created! Redirecting…", "ok");
451+
startAppTour();
443452

444453
// Wait briefly then verify auth before redirect
445454
setTimeout(async () => {

0 commit comments

Comments
 (0)