Use one back link style across the dashboards

Every dashboard page now uses the shared BackLink, the small
"← Back to dashboard" link above the page title that the Waitlist page
already had, instead of a mix of grey Back buttons in different places.
Pages that had no way back (site settings, profile, donate, manual
registration) get one too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 13:25:04 +02:00
co-authored by Claude Opus 5.5
parent d268236686
commit 9be78c3c34
27 changed files with 110 additions and 72 deletions
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { History } from "lucide-react";
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@/components/ui/table";
import { errorMessage } from "@/lib/utils";
import { BackLink } from "@/components/shared/BackLink";
const ACTIONS = [
"refund_created",
@@ -192,7 +193,7 @@ export default function AdminAuditLogPage() {
<History className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard/admin")}>← Back to dashboard</button>
<BackLink href="/dashboard/admin" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Admin Audit Log</h1>
<p className="text-sm text-gray-500">{total} action{total !== 1 ? "s" : ""} recorded</p>
</div>
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { Wallet } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
export default function CashupLandingPage() {
const { token } = useAuth();
@@ -55,15 +56,11 @@ export default function CashupLandingPage() {
<Wallet className="w-5 h-5 text-brand-600" />
</div>
<div>
<h1 className="text-xl font-semibold text-gray-900">Post-event Cashup</h1>
<BackLink href="/dashboard" />
<h1 className="text-xl font-semibold text-gray-900 mt-0.5">Post-event Cashup</h1>
<p className="text-sm text-gray-500 mt-1">Set costs, reconcile takings, and close out an event. Admin only.</p>
</div>
</div>
<button
type="button"
className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shrink-0"
onClick={() => router.push("/dashboard")}
>Back</button>
</div>
{error && <div className="text-sm text-red-600 bg-red-50 border border-red-100 rounded p-2">{error}</div>}
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { ClipboardList } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event, FormResponse, Registration } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
const STATUS_OPTIONS = ["pending", "confirmed", "partial_paid", "paid", "cancelled"] as const;
@@ -197,11 +198,11 @@ export default function AdminRegistrationsPage() {
<ClipboardList className="w-5 h-5 text-brand-600" />
</div>
<div>
<h1 className="text-2xl font-semibold text-gray-900">Manage Registrations</h1>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Manage Registrations</h1>
<p className="text-sm text-gray-500">{registrations.length} registration{registrations.length !== 1 ? "s" : ""} total</p>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200" onClick={() => router.push('/dashboard')}>Back</button>
</div>
{!isAdmin && (
@@ -2,7 +2,7 @@
import React, { Suspense, useCallback, useEffect, useRef, useState } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useRouter, useSearchParams } from "next/navigation";
import { useSearchParams } from "next/navigation";
import { apiFetch, ApiError, API_BASE, resolveToApiOrigin } from "@/lib/api";
import { useSiteSettings } from "@/contexts/SiteSettingsContext";
import { useDismissingState } from "@/hooks/useDismissingState";
@@ -14,6 +14,7 @@ import { YocoTab } from "@/components/admin/YocoTab";
import { extractDominantColors } from "@/lib/extractColors";
import { mapsSearchUrl } from "@/lib/maps";
import { errorMessage } from "@/lib/utils";
import { BackLink } from "@/components/shared/BackLink";
type TabId = "organisation" | "branding" | "notifications" | "email" | "legal" | "whatsapp" | "yoco" | "backups";
@@ -85,7 +86,6 @@ export default function SiteSettingsPage() {
function SiteSettingsPageInner() {
const { token } = useAuth();
const router = useRouter();
const searchParams = useSearchParams();
const { reload: reloadSettings } = useSiteSettings();
@@ -366,15 +366,10 @@ function SiteSettingsPageInner() {
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-semibold text-gray-900">Site Settings</h1>
<BackLink href="/dashboard/admin" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Site Settings</h1>
<p className="text-sm text-gray-500 mt-1">Configure your organisation, branding, email, and legal pages.</p>
</div>
<button
className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200"
onClick={() => router.push("/dashboard/admin")}
>
Back
</button>
</div>
{/* Tab bar */}
@@ -10,6 +10,7 @@ import { Users as UsersIcon } from "lucide-react";
import { RoleBadge, type Role as RoleBadgeRole } from "@/components/shared/RoleBadge";
import { errorMessage } from "@/lib/utils";
import type { Paginated } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
interface UserItem {
id: string;
@@ -259,12 +260,12 @@ export default function AdminUsersPage() {
<UsersIcon className="w-5 h-5 text-brand-600" />
</div>
<div>
<h1 className="text-2xl font-semibold text-gray-900">User Management</h1>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">User Management</h1>
<p className="text-sm text-gray-500">{total} user{total !== 1 ? "s" : ""} total</p>
</div>
</div>
<div className="flex gap-2">
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200" onClick={() => router.push("/dashboard")}>Back</button>
<button className="px-3 py-1.5 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700" onClick={() => setCreateOpen(v => !v)}>
{createOpen ? "Close" : "Create user"}
</button>
@@ -10,6 +10,7 @@ import { QrImage } from "@/components/shared/QrImage";
import { Ticket } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event, Ticket as TicketRecord } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
function EventTicketsContent() {
const { token, user } = useAuth();
@@ -239,14 +240,11 @@ function EventTicketsContent() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<Ticket className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Event Tickets</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Event Tickets</h1>
</div>
</div>
<button
className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm"
onClick={() => router.push("/dashboard")}
>
Back
</button>
</div>
<p className="text-sm text-gray-600 mb-4">
View and print tickets for an event.
@@ -2,10 +2,10 @@
import React, { useEffect, useState } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useRouter } from "next/navigation";
import { apiFetch } from "@/lib/api";
import { formatDate } from "@/lib/date";
import { Users } from "lucide-react";
import { BackLink } from "@/components/shared/BackLink";
type Member = {
id: string; name: string; gender: string | null; dateOfBirth: string | null; phone: string | null;
@@ -25,7 +25,6 @@ const ageOn = (dob: string | null, on: string) => {
/** Groups the signed-in staff member leads (rooms, small groups, buses) and who's in them. */
export default function MyGroupsPage() {
const { token } = useAuth();
const router = useRouter();
const [data, setData] = useState<{ medicalShown: boolean; groups: MyGroup[] } | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -42,7 +41,7 @@ export default function MyGroupsPage() {
<Users className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">My groups</h1>
<p className="text-sm text-gray-500">Rooms, small groups and buses you lead</p>
</div>
@@ -5,13 +5,13 @@ import { QRScanner, QRScannerHandle } from "@/components/qr/QRScanner";
import { useAuth } from "@/hooks/useAuth";
import { apiFetch } from "@/lib/api";
import { useDismissingState } from "@/hooks/useDismissingState";
import { useRouter } from "next/navigation";
import { QrCode } from "lucide-react";
import { useOfflineScanning } from "@/components/scanner/useOfflineScanning";
import { OfflineScanPanel } from "@/components/scanner/OfflineScanPanel";
import type { SnapshotTicket } from "@/lib/offlineScan";
import { errorMessage } from "@/lib/utils";
import type { Event, RecentScan, ScanStats, Section } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// GET /api/tickets/scan-preview/:qr — just the fields the confirm step needs. Offline
// scans build the same shape from the downloaded ticket list.
@@ -33,7 +33,6 @@ type ScanTicket = {
};
export default function TicketScanningPage() {
const router = useRouter();
const scannerRef = useRef<QRScannerHandle>(null);
const [scanInfo, setScanInfo] = useDismissingState<string | null>(null);
const [error, setError] = useDismissingState<string | null>(null);
@@ -330,9 +329,11 @@ export default function TicketScanningPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<QrCode className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Ticket Scanning</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Ticket Scanning</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push('/dashboard')}>Back</button>
</div>
<p className="text-sm text-gray-600 mb-4">
Use the button to start/stop scanning. The back camera will be used when available.
@@ -10,6 +10,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { BedDouble, Plus, Wand2, Printer, Trash2, Pencil, Eraser } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type Group = { id: string; name: string; capacity: number | null; gender: "male" | "female" | null; leaderUserId: string | null; leader?: { id: string; name: string } | null };
type AllocSet = { id: string; name: string; showOnTicket: boolean; groups: Group[]; assignments: { attendeeId: string; groupId: string }[] };
@@ -207,7 +208,7 @@ export default function AllocationsPage() {
<BedDouble className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Rooms &amp; groups</h1>
<p className="text-sm text-gray-500">Put attendees into rooms, small groups, teams or buses</p>
</div>
@@ -10,6 +10,7 @@ import { ShieldCheck, Send, RefreshCw, HeartPulse, Printer } from "lucide-react"
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@/components/ui/table";
import { errorMessage } from "@/lib/utils";
import type { Event, ConsentRow, MedicalRow } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
const STATUS_STYLE: Record<ConsentRow["status"], { label: string; cls: string }> = {
@@ -134,7 +135,7 @@ export default function ConsentTrackerPage() {
<ShieldCheck className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Parental consent</h1>
<p className="text-sm text-gray-500">Youth events: who still needs a parent&apos;s consent before check-in</p>
</div>
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { TicketPercent, Plus, X, Search } from "lucide-react";
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@/components/ui/table";
import { errorMessage } from "@/lib/utils";
import { BackLink } from "@/components/shared/BackLink";
type CouponType = "percent" | "fixed" | "comp";
type Role = "user" | "staff" | "supervisor" | "admin";
@@ -206,7 +207,7 @@ export default function CouponsPage() {
<TicketPercent className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Coupon codes</h1>
<p className="text-sm text-gray-500">Discounts for staff, leaders, volunteers and promotions</p>
</div>
@@ -9,6 +9,7 @@ import { Mail, Users, Zap, Clock } from "lucide-react";
import { AllocationGroupFilter } from "@/components/shared/AllocationGroupFilter";
import { errorMessage } from "@/lib/utils";
import type { Event, MessageSendResult, Registration, User } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type Attendee = { id: string; name: string; email: string; pref: string };
type TemplateKey = 'custom'|'payment_reminder'|'event_reminder'|'tickets';
@@ -489,10 +490,10 @@ function EmailAttendeesPageInner() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<Mail className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Email Attendees</h1>
</div>
<div className="flex items-center gap-2">
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push('/dashboard')}>Back</button>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Email Attendees</h1>
</div>
</div>
</div>
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { Ticket } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event, EventOption } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// Format a Date (or date-like input) to the value expected by <input type="datetime-local">
// This returns local time (browser timezone) as YYYY-MM-DDTHH:mm
@@ -209,9 +210,11 @@ function EventOptionsContent() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<Ticket className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Event options</h1>
<div>
<BackLink href="/dashboard/supervisor/events" label="Back to events" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Event options</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push('/dashboard/supervisor/events')}>Back</button>
</div>
{!canView && (
@@ -12,6 +12,7 @@ import { Calendar } from "lucide-react";
import { ChoicesEditor } from "@/components/shared/FormFieldExtras";
import { errorMessage } from "@/lib/utils";
import type { Event, EventAttachment, EventFormField, EventOption, Paginated, Section } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// ─── helpers ────────────────────────────────────────────────────────────────
@@ -1792,13 +1793,13 @@ export default function ManageEventsPage() {
<Calendar className="w-5 h-5 text-brand-600" />
</div>
<div>
<h1 className="text-2xl font-semibold text-gray-900">Events</h1>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Events</h1>
<p className="text-sm text-gray-500 mt-0.5">Manage and create events</p>
</div>
</div>
<div className="flex items-center gap-2">
<button type="button" className="px-4 py-2 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700 shadow-sm font-medium" onClick={openCreate}>+ Add Event</button>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800" onClick={() => router.push("/dashboard")}>Back</button>
</div>
</div>
@@ -9,6 +9,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { FileText } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event, EventForm, FormResponse, Registration, User } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type FormFieldType = 'yes_no' | 'text' | 'date' | 'numeric' | 'statement' | 'paragraph' | 'rating' | 'multiple_choice';
@@ -452,10 +453,12 @@ export default function FormsBrowserPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<FileText className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Forms</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Forms</h1>
</div>
</div>
<div className="flex gap-2">
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200" onClick={() => router.push("/dashboard")}>Back</button>
{mode === 'view' && (
<button className="px-3 py-1.5 text-sm rounded-lg bg-brand-600 text-white hover:bg-brand-700" onClick={onPrint} disabled={items.length === 0}>
Print forms
@@ -9,6 +9,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { UserPlus } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Registration } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type FormField = { id: string; type: 'yes_no'|'text'|'date'|'numeric'|'statement'|'paragraph'|'rating'|'multiple_choice'; label: string; isRequired?: boolean; helpText?: string|null; options?: unknown };
@@ -67,7 +68,10 @@ export default function ManualRegistrationPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<UserPlus className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-xl font-semibold text-gray-900">Manual Registration</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-xl font-semibold text-gray-900 mt-0.5">Manual Registration</h1>
</div>
</div>
<form onSubmit={submit} className="space-y-3">
<div>
@@ -12,6 +12,7 @@ import { UserPlus, CreditCard } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import { optionUnitNow, variantUnitNow } from "@/lib/pricing";
import type { Event, EventOption, Registration, RegistrationOption, User } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// ─── Pricing helpers ─────────────────────────────────────────────────────────
@@ -348,9 +349,11 @@ export default function ManualRegistrationPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<UserPlus className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Manual registration</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Manual registration</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push('/dashboard')}>Back</button>
</div>
{!canView && (
@@ -9,6 +9,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { WifiOff, Unlock, CheckCircle2 } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type Lock = { id: string; sectionName: string | null; deviceName: string | null; user: { name: string }; lockedAt: string; snapshotAt: string | null; lastSyncAt: string | null };
type Report = {
@@ -86,7 +87,7 @@ export default function OfflineScansPage() {
<WifiOff className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Offline scanning</h1>
<p className="text-sm text-gray-500">Devices scanning without a connection, and their synced scans</p>
</div>
@@ -10,6 +10,7 @@ import { Wallet, RotateCcw, HandHeart, ScanLine, Link2 } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import { registrationOptionUnitPrice } from "@/lib/pricing";
import type { Event, Payment, Registration, User } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// A donation is never mutated once created — assigning it to a registration creates a separate
// "leg" Payment row (isDonation:false, originalPaymentId -> the donation, amount > 0) instead.
@@ -499,9 +500,11 @@ function PaymentsContent() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<Wallet className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Payments</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Payments</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push('/dashboard')}>Back</button>
</div>
{!canView && (
@@ -10,6 +10,7 @@ import { ListChecks, ArrowUp, ArrowDown } from "lucide-react";
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@/components/ui/table";
import { errorMessage } from "@/lib/utils";
import type { Event } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
type Entry = {
id: string;
@@ -100,7 +101,7 @@ export default function WaitlistPage() {
<ListChecks className="w-5 h-5 text-brand-600" />
</div>
<div>
<button className="text-xs text-brand-600 hover:underline" onClick={() => router.push("/dashboard")}>← Back to dashboard</button>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Waitlist</h1>
<p className="text-sm text-gray-500">People waiting for sold-out tickets</p>
</div>
@@ -9,6 +9,7 @@ import { MessageCircle, Users, Zap, Send, Clock, type LucideIcon } from "lucide-
import { AllocationGroupFilter } from "@/components/shared/AllocationGroupFilter";
import { errorMessage } from "@/lib/utils";
import type { Event, MessageSendResult, Registration, User } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// Attendee with preference info
type Attendee = { id: string; name: string; phone: string; pref: string };
@@ -608,11 +609,11 @@ function WhatsAppAttendeesPageInner() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<MessageCircle className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">WhatsApp Attendees</h1>
<div>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">WhatsApp Attendees</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm" onClick={() => router.push("/dashboard")}>
Back
</button>
</div>
{!canView && (
@@ -6,6 +6,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { HandHeart } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Event } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
export default function DonatePage() {
const { token } = useAuth();
@@ -65,7 +66,8 @@ export default function DonatePage() {
<HandHeart className="w-5 h-5 text-rose-600" />
</div>
<div>
<h1 className="text-2xl font-semibold text-gray-900">Make a donation</h1>
<BackLink href="/dashboard/user" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Make a donation</h1>
<p className="text-sm text-gray-500">Support an event or the ministry directly.</p>
</div>
</div>
@@ -8,6 +8,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { FileText } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Registration } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
// Types for form fields
type FormField = { id: string; type: 'yes_no'|'text'|'date'|'numeric'|'statement'|'paragraph'|'rating'|'multiple_choice'; label: string; isRequired?: boolean; helpText?: string|null; options?: unknown };
@@ -139,9 +140,11 @@ function FormsContent() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<FileText className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Attendee forms</h1>
<div>
<BackLink href="/dashboard/user" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Attendee forms</h1>
</div>
</div>
<button className="px-3 py-1.5 text-sm rounded-lg bg-gray-100 hover:bg-gray-200" onClick={() => router.push('/dashboard/user')}>Back</button>
</div>
{loading && <div className="text-sm text-gray-600 mb-2">Loading…</div>}
@@ -9,6 +9,7 @@ import { formatPaymentMethod } from "@/lib/paymentMethod";
import { Receipt } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import type { Paginated } from "@/types";
import { BackLink } from "@/components/shared/BackLink";
interface PaymentItem {
id: string;
@@ -108,12 +109,11 @@ export default function UserPaymentsPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<Receipt className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">My Payments</h1>
<div>
<BackLink href="/dashboard/user" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">My Payments</h1>
</div>
</div>
<button
className="px-2.5 py-1 text-xs rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-1"
onClick={() => router.push("/dashboard/user")}
>Back to dashboard</button>
</div>
{error && <p className="text-red-600 text-sm mb-3">{error}</p>}
@@ -9,6 +9,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { TwoFactorChallenge, TwoFactorMethod } from "@/lib/auth";
import { User, Lock, ShieldAlert, ShieldCheck, ShieldOff, Trash2, Clock, LogIn, KeyRound, RotateCcw } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import { BackLink } from "@/components/shared/BackLink";
type ActivityEvent = { id: string; type: "login" | "password_changed" | "password_reset" | "two_factor_enabled" | "two_factor_disabled"; device: string | null; createdAt: string };
@@ -309,7 +310,8 @@ export default function UserProfilePage() {
return (
<div className="max-w-4xl mx-auto w-full">
<div className="mb-6">
<h1 className="text-2xl font-semibold text-gray-900">Profile &amp; Security</h1>
<BackLink href="/dashboard" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Profile &amp; Security</h1>
<p className="text-sm text-gray-500 mt-1">Manage your personal information and keep your account secure.</p>
</div>
@@ -6,6 +6,7 @@ import { useDismissingState } from "@/hooks/useDismissingState";
import { useRouter } from "next/navigation";
import { KeyRound } from "lucide-react";
import { errorMessage } from "@/lib/utils";
import { BackLink } from "@/components/shared/BackLink";
export default function ResetPasswordPage() {
const { token } = useAuth();
@@ -50,12 +51,11 @@ export default function ResetPasswordPage() {
<div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
<KeyRound className="w-5 h-5 text-brand-600" />
</div>
<h1 className="text-2xl font-semibold text-gray-900">Reset password</h1>
<div>
<BackLink href="/dashboard/user" />
<h1 className="text-2xl font-semibold text-gray-900 mt-0.5">Reset password</h1>
</div>
</div>
<button
className="px-2.5 py-1 text-xs rounded-lg bg-gray-100 hover:bg-gray-200 text-gray-800 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-1"
onClick={() => router.push("/dashboard/user")}
>Back to dashboard</button>
</div>
{error && <p className="text-red-600 text-sm mb-3">{error}</p>}
@@ -0,0 +1,14 @@
import React from "react";
import Link from "next/link";
/**
* The site-wide "← Back to …" link, shown above a page's title (next to its icon tile).
* Every dashboard page uses this one so back navigation looks and sits the same everywhere.
*/
export function BackLink({ href, label = "Back to dashboard" }: { href: string; label?: string }) {
return (
<Link href={href} className="inline-block text-xs text-brand-600 hover:underline">
← {label}
</Link>
);
}