// screens-me.jsx — member account: credits, packs, history, stats, settings
const { fmtIDR, findClass, findCoach, findFriend, minutesUntil, DAYS_SHORT } = window.NADI_HELPERS;
function MeScreen({ user, setUser, setTab, openCheckout, onSignOut, openSettings, showToast, enterAdmin, upcomingBookings }) {
const getAppToday = window.NADI_HELPERS.getAppToday;
const parseLocalDate = window.NADI_HELPERS.parseLocalDate;
const total = (user.creditPacks || []).reduce((s, p) => s + p.credits, 0);
// Parse local date time for sorting
const parseLocalDateTime = (dateStr, timeStr) => {
if (!dateStr || !timeStr) return null;
const [year, month, day] = dateStr.split('-').map(Number);
const [hours, minutes] = timeStr.split(':').map(Number);
return new Date(year, month - 1, day, hours, minutes);
};
const upcoming = (upcomingBookings || [])
.map(b => {
const s = window.NADI.SCHEDULE.find(x => x.id === b.session_id);
if (!s) return null;
return {
...s,
bookingId: b.id,
date: b.date,
};
})
.filter(Boolean)
.filter(s => {
const d = parseLocalDateTime(s.date, s.time);
return d && d >= getAppToday();
})
.sort((a, b) => parseLocalDateTime(a.date, a.time) - parseLocalDateTime(b.date, b.time));
return (
{/* Hero */}
{user.visibleByDefault ? user.name.split(" ")[0] : "Anonymous"}
member since {user.joined.toLowerCase()}
{/* Default privacy toggle — applies to every new booking */}
default for new bookings
{user.visibleByDefault ? "Visible to your circle" : "Going anonymously"}
{user.visibleByDefault ? "friends see you in class lists" : "you appear as +1 anon"} · override per class
{
if (!setUser) return;
setUser({ ...user, visibleByDefault: !v });
showToast && showToast(v ? "Going incognito by default." : "Visible to your friends.");
}}
/>
{/* Credits big card */}
credits remaining
{total}
credit{total === 1 ? "" : "s"}
{/* Stats row */}
h.date && (h.date.includes(new Date().toLocaleDateString("en-US", { month: "short" })) || h.date.includes("May") || h.date.includes("may"))).length} suffix="visits" />
{/* Activity sparkline */}
{/* Active packs */}
active packs
{(user.creditPacks || [])
.slice().sort((a, b) => new Date(a.expires) - new Date(b.expires))
.map((p, i) => {
const getStartOfDay = (dateObj) => new Date(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate());
const expiryDate = parseLocalDate(p.expires);
const todayDate = getStartOfDay(getAppToday());
const daysLeft = Math.max(0, Math.round((expiryDate - todayDate) / 86400000));
const pct = Math.round((p.credits / p.total) * 100);
const urgent = daysLeft <= 10;
return (
{i === 0 ? "spending next" : "queued"}
{daysLeft} days left
{p.packageName}
{p.credits} of {p.total} credits
{pct}%
);
})}
{/* Upcoming + History tabs */}
your classes
{upcoming.length > 0 && (
<>
upcoming
tap to change privacy
{upcoming.map((s) =>
)}
>
)}
history
{(user.history || []).map((h) =>
)}
{enterAdmin && (
)}
);
}
function StatCell({ label, value, suffix, accent }) {
return (
);
}
function ActivityBars({ monthly }) {
const max = Math.max(1, ...monthly.map((x) => x.n));
return (
{monthly.map((row, i) => {
const h = Math.round((row.n / max) * 100);
const isLast = i === monthly.length - 1;
return (
);
})}
{monthly.map((row) => {row.m.toLowerCase()})}
);
}
function ClassRow({ s, upcoming, user, setUser, showToast }) {
const c = findClass(s.classId), co = findCoach(s.coachId);
const privacy = (user && user.bookingPrivacy && user.bookingPrivacy[s.id])
|| (user && user.visibleByDefault ? "self" : "anon");
const isAnon = privacy === "anon";
const flipPrivacy = (e) => {
e && e.stopPropagation();
if (!user || !setUser) return;
const next = isAnon ? "self" : "anon";
setUser({
...user,
bookingPrivacy: { ...(user.bookingPrivacy || {}), [s.id]: next },
});
showToast && showToast(next === "anon" ? "Going anonymously." : "Visible to your circle.");
};
const dateStr = s.date ? window.NADI_HELPERS.parseLocalDate(s.date).toLocaleDateString("en-GB", { weekday: "short", day: "2-digit", month: "short" }).toLowerCase() : DAYS_SHORT[s.day].toLowerCase();
return (
{c.name}
{co.firstName.toLowerCase()} · {s.roomShort.toLowerCase()}
{upcoming ? (
) : null}
);
}
function HistoryRow({ h }) {
const c = findClass(h.classId), co = findCoach(h.coachId);
return (
{c.name}
{co.firstName.toLowerCase()} · {h.roomShort.toLowerCase()}
{h.date.toLowerCase()}
);
}
Object.assign(window, { MeScreen });