Skip to main content

Overview

PassTru provides robust reporting and export capabilities for audit trails, event analytics, and attendee data across your organization.

Report Types

Audit Logs

Complete activity history for your organization

Event Reports

Aggregated event data with attendance metrics

Attendee Data

Individual event attendee lists with check-in status

Check-In Analytics

Check-in methods and timing analysis

Accessing Reports

1

Navigate to Reports

From your Client Dashboard, click Reports in the sidebar.
2

View Audit Logs

See recent activity across all events and users in your organization.
3

Export Data

Use export buttons to download data in CSV format.

Audit Log System

Automated activity tracking for compliance and monitoring:

Implementation

src/pages/client/Reports.tsx
const { data } = await supabase
  .from("audit_logs")
  .select("*")
  .eq("organization_id", organizationId)
  .order("created_at", { ascending: false })
  .limit(100);

Logged Actions

Audit logs track key operations:
  • Event Management: Create, edit, suspend, delete events
  • Attendee Operations: Add, import, remove attendees
  • Check-In Activities: Manual and automated check-ins
  • User Management: Event manager creation and updates
  • Settings Changes: Organization and event configuration

Audit Log Fields

action
string
Description of the action performed (e.g., “Event Created”, “Attendee Checked In”)
entity_type
string
Type of entity affected: Event, Attendee, User, Organization
user_id
string
ID of the user who performed the action
created_at
timestamp
When the action occurred
details
json
Additional context and metadata about the action

Audit Log Display

src/pages/client/Reports.tsx
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Action</TableHead>
      <TableHead>Entity</TableHead>
      <TableHead>Timestamp</TableHead>
      <TableHead>Details</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {logs.map((log) => (
      <TableRow key={log.id}>
        <TableCell className="font-medium">{log.action}</TableCell>
        <TableCell>
          <Badge variant="outline" className="capitalize">
            {log.entity_type}
          </Badge>
        </TableCell>
        <TableCell>{format(new Date(log.created_at), "MMM d, yyyy HH:mm")}</TableCell>
        <TableCell className="truncate">
          {log.details ? JSON.stringify(log.details) : "—"}
        </TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>

Event Data Export

Export comprehensive event summaries:

Export Implementation

src/pages/client/Reports.tsx
const downloadEventData = async () => {
  // Fetch all events
  const { data: events } = await supabase
    .from("events")
    .select("id, name, slug, date, venue, category, status")
    .eq("organization_id", organizationId);
    
  // Get attendee counts for each event
  const rows = [];
  for (const evt of events) {
    const { count: totalCount } = await supabase
      .from("attendees")
      .select("id", { count: "exact", head: true })
      .eq("event_id", evt.id);
      
    const { count: checkedInCount } = await supabase
      .from("attendees")
      .select("id", { count: "exact", head: true })
      .eq("event_id", evt.id)
      .eq("checked_in", true);
      
    rows.push({
      "Event": evt.name,
      "Slug": evt.slug,
      "Date": evt.date,
      "Venue": evt.venue ?? "",
      "Category": evt.category ?? "",
      "Status": evt.status,
      "Attendees": totalCount ?? 0,
      "Checked In": checkedInCount ?? 0,
    });
  }
  
  const csv = toCSV(rows, columns);
  downloadFile(csv, "event-data.csv");
};

Event Export Columns

  • Event name
  • Event slug
  • Date
  • Venue
  • Category
  • Status (active/suspended)
  • Total attendees
  • Checked-in count

Attendee Data Export

Export attendee lists with full details:
From Attendee Management page:
src/pages/event/AttendeeManagement.tsx
const handleExport = () => {
  const columns = [
    "Name", "Email", "Unique ID",
    "Checked In", "Check-In Time", "Check-In Method",
    "Portal Active"
  ];
  
  const data = attendees.map((a) => ({
    "Name": a.name,
    "Email": a.email,
    "Unique ID": a.unique_id,
    "Checked In": a.checked_in ? "Yes" : "No",
    "Check-In Time": a.checked_in_at ?? "",
    "Check-In Method": a.checkin_method ?? "",
    "Portal Active": a.portal_active ? "Yes" : "No",
  }));
  
  const csv = toCSV(data, columns);
  downloadFile(csv, `${event.slug}-attendees.csv`);
};
Columns:
  • Name
  • Email
  • Unique ID
  • Checked In (Yes/No)
  • Check-In Time
  • Check-In Method
  • Portal Active (Yes/No)

Audit Log Export

Export audit trail for compliance:
src/pages/client/Reports.tsx
const exportAuditLogs = () => {
  const columns = ["Action", "Entity", "Timestamp", "Details"];
  
  const data = filtered.map((log) => ({
    "Action": log.action,
    "Entity": log.entity_type ?? "",
    "Timestamp": format(new Date(log.created_at), "yyyy-MM-dd HH:mm:ss"),
    "Details": log.details ? JSON.stringify(log.details) : "",
  }));
  
  const csv = toCSV(data, columns);
  downloadFile(csv, "reports.csv");
};

CSV Export Utility

All exports use a centralized CSV generation system:
src/lib/csv.ts
export function toCSV(
  data: Record<string, string>[],
  columns: string[]
): string {
  const header = columns.join(",");
  
  const rows = data.map((row) =>
    columns.map((col) => {
      const raw = row[col] ?? "";
      const val = sanitizeCellValue(raw);
      
      // Quote if contains comma, newline, or quote
      if (val.includes(",") || val.includes("\n") || val.includes('"')) {
        return `"${val.replace(/"/g, '""')}"`;
      }
      return val;
    }).join(",")
  );
  
  return [header, ...rows].join("\n");
}

CSV Security

Prevents CSV injection attacks:
src/lib/csv.ts
function sanitizeCellValue(val: string): string {
  // Prefix dangerous leading characters with a single quote
  if (/^[=+\-@\t\r]/.test(val)) {
    return "'" + val;
  }
  return val;
}
Security: Values starting with =, +, -, @ are prefixed with a single quote to prevent formula execution in spreadsheet applications.

File Download Implementation

Browser-based file downloads:
src/lib/csv.ts
export function downloadFile(
  content: string,
  filename: string,
  mime = "text/csv"
) {
  const blob = new Blob([content], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

Search & Filtering

Filter reports before exporting:
src/pages/client/Reports.tsx
const filtered = logs.filter(
  (l) =>
    l.action.toLowerCase().includes(search.toLowerCase()) ||
    (l.entity_type ?? "").toLowerCase().includes(search.toLowerCase())
);

Search Interface

<div className="relative max-w-sm">
  <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2" />
  <Input
    placeholder="Search logs..."
    value={search}
    onChange={(e) => setSearch(e.target.value)}
    className="pl-9"
  />
</div>

Export Buttons

Consistent export UI across the platform:
<Button
  variant="outline"
  size="sm"
  onClick={downloadEventData}
>
  <FileDown className="mr-2 h-4 w-4" />
  Download Event Data
</Button>

<Button
  variant="outline"
  size="sm"
  disabled={filtered.length === 0}
  onClick={exportAuditLogs}
>
  <Download className="mr-2 h-4 w-4" />
  Export CSV
</Button>

Check-In Analytics

Analyze check-in patterns and methods:

Method Breakdown

Attendees who checked in by scanning their QR code on the public check-in page.

Timing Analysis

Exported check-in times allow analysis of:
  • Peak check-in periods
  • Average check-in duration
  • Late arrivals vs. early arrivals
  • Check-in method efficiency

Report Limits

Audit Logs: Display limited to most recent 100 entries. Use export for full history.
src/pages/client/Reports.tsx
const { data } = await supabase
  .from("audit_logs")
  .select("*")
  .eq("organization_id", organizationId)
  .order("created_at", { ascending: false })
  .limit(100);  // Display limit

Data Retention

PassTru maintains all historical data:
  • Audit Logs: Permanent retention
  • Event Data: Retained until event deletion
  • Attendee Records: Retained until manual deletion
  • Check-In History: Permanent retention per attendee

Best Practices

Regular Exports

Export data regularly for backup and offline analysis.

Pre-Deletion Backup

Always export event data before deleting events.

Audit Review

Regularly review audit logs for unusual activity.

Metric Tracking

Use check-in data to optimize future event planning.

Build docs developers (and LLMs) love