Help guide opens on demand only; add At the door to admin quick actions
- The site-wide help guide no longer auto-opens on first visit to a page — it only opens when the help button is clicked. Removed the now-pointless "Don't show this again" checkbox and the per-page dismissal tracking it drove. - Admin dashboard quick actions was missing "At the door" (walk-in registration, payments, ticket printing), which Supervisor already had. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,8 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
||||
|
||||
- Admin/Supervisor dashboard "Revenue" figures (today/week/month) were overcounting: they didn't exclude donation-application "legs" (the money was already counted once via the original donation) the way Reports and the admin payments-stats endpoint already did. Both endpoints now use the same exclusion.
|
||||
- Admin/Supervisor dashboard "Revenue" KPI, trend chart, and top-performing-events table were undercounting the other way — donations themselves weren't being counted as revenue at all, only tracked in the separate "Donations" figure. Donations now count toward revenue (donation-application legs are still excluded either way, since that money was already counted once via the original donation).
|
||||
- Admin/Supervisor dashboard KPIs (revenue, donations, registrations, tickets sold) used a calendar-month-to-date window, which under-counted for most of the month (e.g. only 3 days' worth of data on the 3rd). They now use a rolling trailing-month window instead — today back one calendar month, e.g. 9 May through 8 June if today is 8 June — compared against the equal-length month before that. The "View report" links from those tiles now deep-link into the same trailing-month range.
|
||||
- Admin dashboard was missing the "At the door" quick action that Supervisor already had (walk-in registration, payments, and ticket printing at the door) — added.
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -41,6 +43,7 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
||||
- Admin/Supervisor dashboard quick actions no longer list "Manage sections" or "Event tickets & printing" (redundant with the Events and At-the-door pages) or "Manage WhatsApp API" (moved into Site Settings). Staff keeps "Event tickets & printing" — their only other tool is ticket scanning, and they can't reach At-the-door.
|
||||
- Admin dashboard no longer shows ticket-scanning stats ("Recent scans") — kept on Supervisor/Staff, where it's actually actionable.
|
||||
- The old hand-rolled `Button` component (`components/shared/Button.tsx`) is gone — its one remaining caller now uses the standard `components/ui/button.tsx`.
|
||||
- The site-wide help guide no longer opens itself automatically on first visit to a page — it only opens when the help button is clicked. The now-pointless "Don't show this again" checkbox was removed along with the per-page dismissal tracking it drove.
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useStableState } from "@/hooks/useStableState";
|
||||
import { useVisiblePolling } from "@/hooks/useVisiblePolling";
|
||||
import {
|
||||
Calendar, Banknote, Gift, Users, Ticket, QrCode, ClipboardList,
|
||||
UserPlus, FileText, MessageCircle, BarChart2, Mail, Wallet,
|
||||
UserPlus, FileText, MessageCircle, BarChart2, Mail, Wallet, DoorOpen,
|
||||
} from "lucide-react";
|
||||
import { StatCard, StatCardRow } from "@/components/shared/StatCard";
|
||||
import { QuickActionTile, QuickActionGrid } from "@/components/shared/QuickActionTile";
|
||||
@@ -25,6 +25,7 @@ const QUICK_ACTIONS = [
|
||||
{ href: "/dashboard/supervisor/events", label: "Manage events", description: "Create, edit, and update ticket types", icon: Calendar },
|
||||
{ href: "/dashboard/admin/registrations", label: "Manage registrations", description: "Cancel, update status, and search registrations", icon: ClipboardList },
|
||||
{ href: "/dashboard/supervisor/manual", label: "Manual registration", description: "Register a guest and issue tickets", icon: UserPlus },
|
||||
{ href: "/dashboard/supervisor/at-the-door", label: "At the door", description: "Walk-ins, payments, ticket printing", icon: DoorOpen },
|
||||
{ href: "/dashboard/supervisor/payments", label: "Payments & donations", description: "Manual payments and assignment", icon: Wallet },
|
||||
{ href: "/dashboard/staff/ticket-scanning", label: "Open scanner", description: "Use your device camera to validate tickets", icon: QrCode },
|
||||
{ href: "/dashboard/supervisor/reports", label: "Reports", description: "View, export, and email reports", icon: BarChart2 },
|
||||
|
||||
@@ -21,8 +21,8 @@ const EXCLUDED_PREFIXES = ["/self-service", "/setup"];
|
||||
|
||||
export default function HelpFab() {
|
||||
const pathname = usePathname() || "/";
|
||||
const { slug, content } = resolveHelpContent(pathname);
|
||||
const { open, openGuide, closeGuide } = useHelpGuide(slug);
|
||||
const { content } = resolveHelpContent(pathname);
|
||||
const { open, openGuide, closeGuide } = useHelpGuide();
|
||||
|
||||
if (EXCLUDED_PREFIXES.some(prefix => pathname.startsWith(prefix))) {
|
||||
return null;
|
||||
|
||||
@@ -5,9 +5,8 @@ import Link from "next/link";
|
||||
import { X, Mail, MessageCircleQuestion } from "lucide-react";
|
||||
import type { HelpContent } from "@/content/help/types";
|
||||
|
||||
export default function HelpGuideModal({ content, onClose }: { content: HelpContent; onClose: (dontShowAgain: boolean) => void }) {
|
||||
export default function HelpGuideModal({ content, onClose }: { content: HelpContent; onClose: () => void }) {
|
||||
const [tab, setTab] = useState(content.tabs[0]?.key);
|
||||
const [dontShowAgain, setDontShowAgain] = useState(false);
|
||||
const activeTab = content.tabs.find(t => t.key === tab) || content.tabs[0];
|
||||
|
||||
return (
|
||||
@@ -15,7 +14,7 @@ export default function HelpGuideModal({ content, onClose }: { content: HelpCont
|
||||
// page-level popup (e.g. the report viewer at z-[60]), since the guide
|
||||
// can be opened from the FAB while another overlay is showing.
|
||||
<div className="fixed inset-0 z-[70]">
|
||||
<div className="absolute inset-0 bg-black/40 animate-in fade-in duration-200" onClick={() => onClose(dontShowAgain)} />
|
||||
<div className="absolute inset-0 bg-black/40 animate-in fade-in duration-200" onClick={onClose} />
|
||||
<div className="absolute inset-0 flex items-center justify-center p-4">
|
||||
<div
|
||||
className="w-full max-w-3xl bg-white rounded-2xl shadow-2xl animate-in fade-in zoom-in-95 slide-in-from-bottom-2 duration-200 overflow-hidden"
|
||||
@@ -31,7 +30,7 @@ export default function HelpGuideModal({ content, onClose }: { content: HelpCont
|
||||
<p className="text-xs text-gray-500">{content.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button className="p-1.5 rounded-full hover:bg-gray-100 transition-colors" onClick={() => onClose(dontShowAgain)} aria-label="Close">
|
||||
<button className="p-1.5 rounded-full hover:bg-gray-100 transition-colors" onClick={onClose} aria-label="Close">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -42,7 +41,7 @@ export default function HelpGuideModal({ content, onClose }: { content: HelpCont
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={() => onClose(dontShowAgain)}
|
||||
onClick={onClose}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-full border border-brand-200 bg-white text-brand-700 hover:bg-brand-100 hover:border-brand-300 transition-colors"
|
||||
>
|
||||
{link.icon && <link.icon className="w-3.5 h-3.5" />}
|
||||
@@ -90,12 +89,8 @@ export default function HelpGuideModal({ content, onClose }: { content: HelpCont
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="flex items-center gap-2 text-xs text-gray-600 cursor-pointer">
|
||||
<input type="checkbox" checked={dontShowAgain} onChange={e => setDontShowAgain(e.target.checked)} className="rounded accent-brand-600" />
|
||||
Don't show this again
|
||||
</label>
|
||||
<button className="px-4 py-2 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700 transition-colors shadow-sm" onClick={() => onClose(dontShowAgain)}>
|
||||
<div className="flex items-center justify-end">
|
||||
<button className="px-4 py-2 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700 transition-colors shadow-sm" onClick={onClose}>
|
||||
Got it
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@ import { staffTicketScanningHelpContent } from "./staff-ticket-scanning";
|
||||
import { userFormsHelpContent } from "./user-forms";
|
||||
|
||||
type HelpRegistryEntry = {
|
||||
/** Unique, stable slug — used as the per-page "don't show again" storage key. */
|
||||
/** Unique, stable identifier for this guide. */
|
||||
slug: string;
|
||||
matches: (pathname: string) => boolean;
|
||||
content: HelpContent;
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
/**
|
||||
* Open/close state for a help guide, with per-page "don't show again"
|
||||
* persistence. `storageKey` should be a stable slug identifying the guide's
|
||||
* content (e.g. "reports", "dashboard-user", "general") — each slug gets an
|
||||
* independent dismissal flag, so dismissing one page's guide doesn't
|
||||
* suppress another's.
|
||||
* Open/close state for a help guide — opens only when the user explicitly
|
||||
* asks for it (e.g. clicking the help FAB), never automatically.
|
||||
*/
|
||||
export function useHelpGuide(storageKey: string) {
|
||||
export function useHelpGuide() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const dismissedKey = `hope_events_help_dismissed_${storageKey}`;
|
||||
|
||||
// Show automatically on first visit to this guide, unless dismissed for good.
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (typeof window !== "undefined" && window.localStorage.getItem(dismissedKey) !== "1") {
|
||||
setOpen(true);
|
||||
}
|
||||
} catch {
|
||||
// localStorage unavailable (private browsing, etc.) — just don't auto-show.
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dismissedKey]);
|
||||
|
||||
const openGuide = () => setOpen(true);
|
||||
|
||||
const closeGuide = (dontShowAgain: boolean) => {
|
||||
setOpen(false);
|
||||
if (dontShowAgain) {
|
||||
try {
|
||||
window.localStorage.setItem(dismissedKey, "1");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const closeGuide = () => setOpen(false);
|
||||
return { open, openGuide, closeGuide };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user