Exporting Data
This guide covers how to export report data from Shyfts in multiple formats including PDF, CSV, and Excel for external analysis, record-keeping, or payroll integration.
Overview
Export Capabilities
Shyfts provides flexible data export options:
- PDF Export - Formatted documents with branding for printing or sharing
- Excel Export - Spreadsheet format for analysis and manipulation
- CSV Export - Plain text format for system integration
Supported Reports
All 9 report types support export functionality:
| Report | Excel | CSV | |
|---|---|---|---|
| Overview | ✅ | ✅ | ✅ |
| Staff Performance | ✅ | ✅ | ✅ |
| Scheduling Analytics | ✅ | ✅ | ✅ |
| Attendance Summary | ✅ | ✅ | ✅ |
| Hours Tracking | ✅ | ✅ | ✅ |
| Leave Analysis | ✅ | ✅ | ✅ |
| Skills Matrix | ✅ | ✅ | ✅ |
| Cost Analysis | ✅ | ✅ | ✅ |
| Compliance Report | ✅ | ✅ | ✅ |
Exporting Reports
Export Button Location
The export button is located in the report dashboard header:
// Source: src/components/reporting/ReportingDashboard.tsx:620-650
<div className="flex items-center gap-2">
<button
onClick={() => handleExport('PDF')}
className="form-button secondary flex items-center gap-2"
>
<FileText className="w-4 h-4" />
PDF
</button>
<button
onClick={() => handleExport('EXCEL')}
className="form-button secondary flex items-center gap-2"
>
<Table className="w-4 h-4" />
Excel
</button>
<button
onClick={() => handleExport('CSV')}
className="form-button secondary flex items-center gap-2"
>
<Download className="w-4 h-4" />
CSV
</button>
</div>
Export Process
- Navigate to the Reports dashboard
- Select a report tab (Performance, Scheduling, etc.)
- Configure date range and filters
- Click Refresh to load data
- Click the desired export button (PDF, Excel, or CSV)
- File downloads automatically
Export Handler
// Source: src/components/reporting/ReportingDashboard.tsx:402-485
const handleExport = async (format: 'PDF' | 'EXCEL' | 'CSV') => {
setExportStatus(null)
let reportData: unknown
let reportTitle = ''
// Get current report data based on active tab
switch (activeTab) {
case 'overview':
reportData = {
performance: performanceData,
scheduling: schedulingData,
attendance: attendanceData,
skills: skillsData,
hours: hoursData,
leave: leaveData,
cost: costData,
compliance: complianceData,
meta: { dateRange, filters }
}
reportTitle = 'Overview_Report'
break
case 'performance':
reportData = performanceData
reportTitle = 'Staff_Performance_Report'
break
// ... other cases
}
const result = await ReportingService.exportReport(reportData, format, reportTitle)
if (result.success) {
setExportStatus({
success: true,
message: `Successfully exported as ${format}. File: ${result.file_name}`
})
}
}
PDF Export
Features
- Formatted Layout - Professional document structure
- Company Branding - Coral accent colours (#FFB5B0)
- Tables - Auto-formatted data tables with headers
- Multi-page Support - Automatic pagination for large datasets
PDF Generation
// Source: src/services/reportingService.ts:1529-1642
private static exportToPDF(reportData: unknown, reportTitle: string): ExportResult {
try {
const doc = new jsPDF()
const pageHeight = doc.internal.pageSize.getHeight()
const pageWidth = doc.internal.pageSize.getWidth()
const marginX = 14
const contentWidth = pageWidth - marginX * 2
let y = 35
const ensureSpace = (minY: number) => {
if (y > pageHeight - minY) {
doc.addPage()
y = 20
}
}
// Add title
doc.setFontSize(16)
doc.setTextColor(255, 181, 176) // Coral accent
doc.text(reportTitle.replace(/_/g, ' '), marginX, 25)
// Add timestamp
doc.setFontSize(8)
doc.setTextColor(128, 128, 128)
doc.text(`Generated: ${new Date().toLocaleString('en-GB')}`, marginX, y)
y += 10
// ... table rendering logic
// Save and download
const fileName = `${reportTitle}_${formatForFilename(new Date())}.pdf`
doc.save(fileName)
return {
success: true,
file_name: fileName
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'PDF export failed',
file_name: ''
}
}
}
PDF Document Structure
| Section | Content |
|---|---|
| Header | Report title with coral styling |
| Metadata | Generation timestamp (DD/MM/YYYY HH:mm) |
| Data Tables | Auto-generated from report data |
| Footer | Page numbers |
PDF Use Cases
| Use Case | Benefit |
|---|---|
| Printable Reports | Professional format for meetings |
| Email Attachments | Shareable document format |
| Audit Records | Official documentation |
| Board Presentations | Branded visual reports |
Excel Export
Features
- Multiple Sheets - Overview exports create separate sheets per section
- Data Formatting - Numbers, dates, and text properly typed
- Pivot-Ready - Data structured for pivot table analysis
- Formula Compatible - Ready for Excel calculations
Excel Generation
// Source: src/services/reportingService.ts:1648-1717
private static exportToExcel(reportData: unknown, reportTitle: string): ExportResult {
try {
const workbook = XLSX.utils.book_new()
if (Array.isArray(reportData)) {
// Array-based reports - convert to worksheet
if (reportData.length === 0) {
return {
success: false,
error: 'No data to export',
file_name: ''
}
}
const worksheet = XLSX.utils.json_to_sheet(reportData)
XLSX.utils.book_append_sheet(workbook, worksheet, 'Report Data')
} else if (typeof reportData === 'object' && reportData !== null) {
// Object-based reports - create sheet per section
const entries = Object.entries(reportData as Record<string, unknown>)
for (const [key, value] of entries) {
const safeName = String(key).slice(0, 31) || 'Sheet'
if (Array.isArray(value)) {
const rows = value.length > 0 ? value : [{ note: 'No data' }]
const worksheet = XLSX.utils.json_to_sheet(rows)
XLSX.utils.book_append_sheet(workbook, worksheet, safeName)
}
}
}
// Generate and download
const fileName = `${reportTitle}_${formatForFilename(new Date())}.xlsx`
XLSX.writeFile(workbook, fileName)
return {
success: true,
file_name: fileName
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Excel export failed',
file_name: ''
}
}
}
Overview Report Sheets
When exporting the Overview report, each section gets its own sheet:
| Sheet Name | Content |
|---|---|
| performance | Staff performance metrics |
| scheduling | Scheduling analytics data |
| attendance | Attendance summary records |
| skills | Skills matrix data |
| hours | Hours tracking records |
| leave | Leave analysis data |
| cost | Cost analysis breakdown |
| compliance | Compliance report data |
| meta | Date range and filter info |
Excel Use Cases
| Use Case | Benefit |
|---|---|
| Payroll Integration | Import into payroll systems |
| Data Analysis | Create pivot tables and charts |
| Custom Reports | Build on exported data |
| Historical Tracking | Archive for comparison |
CSV Export
Features
- Universal Compatibility - Works with any spreadsheet or database
- System Integration - Import into payroll and HR systems
- Lightweight - Small file sizes for quick transfer
- Plain Text - Human-readable format
CSV Generation
// Source: src/services/reportingService.ts:1468-1524
private static exportToCSV(reportData: unknown, reportTitle: string): ExportResult {
try {
let csv = ''
if (Array.isArray(reportData)) {
// For array-based reports (staff performance, attendance, etc.)
if (reportData.length === 0) {
return {
success: false,
error: 'No data to export',
file_name: ''
}
}
// Get headers from first item
const headers = Object.keys(reportData[0])
csv = headers.join(',') + '\n'
// Add data rows
reportData.forEach(item => {
const row = headers.map(header => {
const value = this.getNestedValue(item, header)
return typeof value === 'string' && value.includes(',')
? `"${value}"`
: value
})
csv += row.join(',') + '\n'
})
} else if (typeof reportData === 'object' && reportData !== null) {
// For object-based reports (cost analysis, scheduling analytics)
csv = this.objectToCSV(reportData as Record<string, unknown>)
}
// Create and download file
const fileName = `${reportTitle}_${formatForFilename(new Date())}.csv`
const blob = new Blob([csv], { type: 'text/csv' })
// ... download logic
return {
success: true,
file_name: fileName
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'CSV export failed',
file_name: ''
}
}
}
CSV Format Example
staff_name,role,attendance_rate,punctuality_rate,shifts_completed,total_hours
"Sarah Johnson",Nurse,98.5,95.2,22,176.5
"James Wilson",Doctor,94.0,88.5,20,160.0
"Emma Brown",Receptionist,100.0,97.8,23,184.0
CSV Use Cases
| Use Case | Benefit |
|---|---|
| Payroll Systems | Direct import compatibility |
| Database Import | Load into SQL databases |
| Data Migration | Transfer between systems |
| Simple Analysis | Open in any spreadsheet app |
Export File Naming
Filename Convention
All exports follow a consistent naming pattern:
{ReportType}_{Timestamp}.{extension}
Examples
| Report | Excel | CSV | |
|---|---|---|---|
| Overview | Overview_Report_20250114_143052.pdf | .xlsx | .csv |
| Performance | Staff_Performance_Report_20250114_143052.pdf | .xlsx | .csv |
| Scheduling | Scheduling_Analytics_Report_20250114_143052.pdf | .xlsx | .csv |
| Attendance | Attendance_Summary_Report_20250114_143052.pdf | .xlsx | .csv |
| Hours | Hours_Tracking_Report_20250114_143052.pdf | .xlsx | .csv |
| Leave | Leave_Analysis_Report_20250114_143052.pdf | .xlsx | .csv |
| Skills | Skills_Matrix_Report_20250114_143052.pdf | .xlsx | .csv |
| Cost | Cost_Analysis_Report_20250114_143052.pdf | .xlsx | .csv |
| Compliance | Compliance_Report_20250114_143052.pdf | .xlsx | .csv |
Timestamp Format
// Filename timestamp format: YYYYMMDD_HHmmss
function formatForFilename(date: Date): string {
return date.toISOString()
.replace(/[-:]/g, '')
.replace('T', '_')
.slice(0, 15)
}
Export Status Feedback
Success Message
// Source: src/components/reporting/ReportingDashboard.tsx:468-473
if (result.success) {
setExportStatus({
success: true,
message: `Successfully exported as ${format}. File: ${result.file_name}`
})
}
Error Handling
// Source: src/components/reporting/ReportingDashboard.tsx:474-483
if (!result.success) {
setExportStatus({
success: false,
message: result.error || 'Export failed'
})
}
Status Display
// Export status banner
{exportStatus && (
<div className={`p-4 rounded-xl ${
exportStatus.success
? 'bg-green-500/20 border-green-500/30'
: 'bg-red-500/20 border-red-500/30'
}`}>
{exportStatus.success ? (
<CheckCircle className="w-5 h-5 text-green-400" />
) : (
<AlertCircle className="w-5 h-5 text-red-400" />
)}
<span>{exportStatus.message}</span>
</div>
)}
Data Formatting
Value Formatting for Export
// Source: src/services/reportingService.ts:1768-1784
private static formatValueForExport(value: unknown): string {
if (value === null || value === undefined) {
return ''
}
if (typeof value === 'boolean') {
return value ? 'Yes' : 'No'
}
if (typeof value === 'number') {
return value.toFixed(2)
}
if (typeof value === 'object') {
return JSON.stringify(value)
}
return String(value)
}
Type Conversions
| Data Type | Export Format |
|---|---|
| Numbers | 2 decimal places |
| Booleans | "Yes" / "No" |
| Dates | DD/MM/YYYY format |
| Objects | JSON string |
| Arrays | Separate rows/sheets |
| Null/Undefined | Empty string |
Currency Formatting
Cost and financial data is formatted in GBP:
| Field | Example |
|---|---|
| Total Labour Cost | £12,450.00 |
| Overtime Cost | £1,250.50 |
| Average Cost/Hour | £18.75 |
| Budget Variance | -£500.00 |
Export Considerations
Performance
| Report Size | Recommendation |
|---|---|
| < 100 rows | Any format suitable |
| 100-1000 rows | CSV or Excel preferred |
| > 1000 rows | Use pagination, export in batches |
Data Freshness
Exports include data as of the export time:
- Check date range - Verify correct period selected
- Apply filters - Ensure filters match requirements
- Refresh data - Click Refresh before exporting
- Note timestamp - File includes generation time
Privacy Considerations
Exported files may contain sensitive information:
| Data Type | Consideration |
|---|---|
| Staff Names | Personal data - handle appropriately |
| Hours/Attendance | HR records - secure storage required |
| Cost Data | Financial information - restricted access |
| Compliance Violations | Sensitive - limited distribution |
Best Practices
For Regular Reporting
| Frequency | Recommended Format | Use Case |
|---|---|---|
| Daily | CSV | System integration |
| Weekly | Excel | Analysis and review |
| Monthly | Management reports | |
| Quarterly | PDF + Excel | Board presentations |
For Data Analysis
- Use Excel - Better for pivot tables and charts
- Export Overview - Gets all data in one file
- Check Filters - Ensure correct data scope
- Save Originals - Archive for comparison
For Payroll Integration
- Use CSV - Universal compatibility
- Export Hours - Hours Tracking report
- Include Staff IDs - For matching records
- Verify Totals - Cross-check with system
For Compliance Audits
- Use PDF - Official record format
- Include Date Range - Clear reporting period
- Note Filters Applied - Document scope
- Archive Securely - Retain for required period
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| No data to export | Empty report | Load data before exporting |
| Export failed | Large dataset | Use pagination, export smaller batches |
| File not downloading | Browser blocking | Check download permissions |
| Wrong data exported | Wrong tab selected | Switch to correct report tab |
Error Messages
| Error | Meaning | Action |
|---|---|---|
| "No data to export" | Report has no records | Adjust date range or filters |
| "PDF export failed" | Document generation error | Try CSV instead |
| "Excel export failed" | Workbook creation error | Try CSV instead |
| "CSV export failed" | Data conversion error | Check data format |
Browser Compatibility
| Browser | Excel | CSV | |
|---|---|---|---|
| Chrome | ✅ | ✅ | ✅ |
| Firefox | ✅ | ✅ | ✅ |
| Safari | ✅ | ✅ | ✅ |
| Edge | ✅ | ✅ | ✅ |
Related Documentation
- Available Reports - Report types and metrics
- Generating Reports - How to run reports
- Time Tracking Reports - Detailed time analytics
Source Files:
src/components/reporting/ReportingDashboard.tsx- Export UI and handlerssrc/services/reportingService.ts:1435-1784- Export implementationsrc/lib/utils/csv-parser.ts- CSV utilities