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
Navigate to Reports
From your Client Dashboard, click Reports in the sidebar.
View Audit Logs
See recent activity across all events and users in your organization.
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
Description of the action performed (e.g., “Event Created”, “Attendee Checked In”)
Type of entity affected: Event, Attendee, User, Organization
ID of the user who performed the action
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:
Per-Event Export
Event List Export
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)
From Event List page (before deleting): src/pages/client/EventList.tsx
const downloadEventData = async ( evt : EventRow ) => {
const { data : attendees } = await supabase
. from ( "attendees" )
. select ( "name, email, unique_id, checked_in, checked_in_at, checkin_method" )
. eq ( "event_id" , evt . id );
const columns = [
"Name" , "Email" , "Unique ID" ,
"Checked In" , "Check-In Time" , "Method"
];
const rows = ( attendees ?? []). map (( a : any ) => ({
"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 ?? "" ,
"Method" : a . checkin_method ?? "" ,
}));
const csv = toCSV ( rows , columns );
downloadFile ( csv , ` ${ evt . slug } -attendees.csv` );
};
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:
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:
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:
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 >
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
QR Scan
Self-Service
Manual
Attendees who checked in by scanning their QR code on the public check-in page.
Attendees who manually entered their email and unique ID on the public check-in page.
Attendees checked in by event staff through the Event Portal.
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.