Skip to main content

ChecklistPortal Component

ChecklistPortal Component

Component: src/features/events/components/ChecklistPortal.tsx Route: /checklist/:token Access: Public (magic link token required)

---

Overview

The ChecklistPortal allows event registrants to complete pre-event requirements via a magic link. It supports waivers, file uploads, confirmations, and external links.

> Note (Mar 2026): Required event waivers are now signed during registration on PublicEventPage. Checklist portal waiver signing remains supported for backward compatibility and legacy registrations.

Recent Updates (Apr 2026)

  • External link hardening: Checklist link items open with window.open(url, '_blank', 'noopener,noreferrer') to prevent reverse-tabnabbing.

Features

  • Magic link access - No login required, token-based authentication
  • Progress tracking - Visual progress bar showing completion
  • Multiple item types - Waivers, uploads, confirmations, links
  • Real-time updates - Status updates via Edge Functions
  • Mobile friendly - Responsive design for on-the-go completion

UI Layout

┌─────────────────────────────────────────────────────────┐
│ Organization Logo                                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Pre-Event Checklist                                    │
│  Event Name                                             │
│  December 25, 2025                                      │
│                                                         │
│  Hello, John! Complete the items below before the event.│
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Progress: 2 of 4 complete                              │
│  ████████████░░░░░░░░░░░░  50%                         │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✓ Liability Waiver                    [Completed]      │
│    Signed on Dec 15, 2025                               │
│                                                         │
│  ✓ Medical Information Form            [Completed]      │
│    Uploaded: medical-form.pdf                           │
│                                                         │
│  ○ Packing List Confirmation           [Confirm]        │
│    Please confirm you've reviewed the packing list      │
│                                                         │
│  ○ Travel Insurance                    [View Link]      │
│    Purchase travel insurance (optional)                 │
│                                                         │
└─────────────────────────────────────────────────────────┘

URL Parameters

// URL: /checklist/abc123xyz789
const { token } = useParams<{ token: string }>();

State Management

const [registration, setRegistration] = useState<Registration | null>(null);
const [event, setEvent] = useState<Event | null>(null);
const [checklistItems, setChecklistItems] = useState<ChecklistItem[]>([]);
const [checklistStatus, setChecklistStatus] = useState<ChecklistStatus[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

Data Fetching

useEffect(() => {
  const fetchChecklist = async () => {
    const response = await fetch(
      `${SUPABASE_URL}/functions/v1/get-checklist-portal?token=${token}`
    );
    
    if (!response.ok) {
      setError('Invalid or expired link');
      return;
    }
    
    const data = await response.json();
    setRegistration(data.registration);
    setEvent(data.event);
    setChecklistItems(data.checklistItems);
    setChecklistStatus(data.checklistStatus);
    setLoading(false);
  };
  
  fetchChecklist();
}, [token]);

Checklist Item Types

Waiver

const handleSignWaiver = (itemId: string, waiverId: string) => {
  // Open public waiver signing route with registration/checklist context
  window.open(`/waivers/sign/${waiverId}?registration=${registration.id}&checklist=${itemId}&token=${token}`, '_blank');
};

Note: token is required for attendee waiver signing links; links without token are rejected.

File Upload

const handleFileUpload = async (itemId: string, file: File) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('registrationId', registration.id);
  formData.append('checklistItemId', itemId);
  formData.append('token', token);
  
  const response = await fetch(
    `${SUPABASE_URL}/functions/v1/upload-checklist-file`,
    { method: 'POST', body: formData }
  );
  
  if (response.ok) {
    // Refresh checklist status
    refetchStatus();
  }
};

Confirmation

const handleConfirm = async (itemId: string) => {
  await fetch(`${SUPABASE_URL}/functions/v1/update-checklist-status`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      registrationId: registration.id,
      checklistItemId: itemId,
      status: 'completed',
      token
    })
  });
  
  refetchStatus();
};

External Link

const handleOpenLink = (url: string) => {
  window.open(url, '_blank', 'noopener,noreferrer');
};

Progress Calculation

const progress = useMemo(() => {
  const requiredItems = checklistItems.filter(item => item.required);
  const completedRequired = requiredItems.filter(item => {
    const status = checklistStatus.find(s => s.checklist_item_id === item.id);
    return status?.status === 'completed' || status?.status === 'approved';
  });
  
  return {
    total: requiredItems.length,
    completed: completedRequired.length,
    percentage: requiredItems.length > 0 
      ? Math.round((completedRequired.length / requiredItems.length) * 100)
      : 100
  };
}, [checklistItems, checklistStatus]);

Token Validation

The Edge Function validates:

  • Token exists in event_registrations.access_token
  • Token has not expired (access_token_expires_at)
  • Registration is not canceled

Related Components

  • EventRegistrationSuccess - Links to checklist portal
  • WaiverSigning - Waiver completion flow

Related Documentation

  • Main Events Doc: ../06-EVENTS-TICKETING.md
  • Registration Success: ./06-EVENT-REGISTRATION-SUCCESS.md

Synced from IFMmvp-Frontend documentation: pages/fundraising/events/07-CHECKLIST-PORTAL.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial