285 lines
8.9 KiB
TypeScript
285 lines
8.9 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select'
|
|
import { Download, FileText, Users, CreditCard, Activity, BarChart3 } from 'lucide-react'
|
|
import { toast } from '@/hooks/use-toast'
|
|
|
|
const reportTypes = [
|
|
{
|
|
id: 'users',
|
|
name: 'Users Report',
|
|
description: 'Export all user data including registration info, roles, and verification status',
|
|
icon: Users,
|
|
},
|
|
{
|
|
id: 'transactions',
|
|
name: 'Transactions Report',
|
|
description: 'Export all transaction data including payments, refunds, and balance changes',
|
|
icon: CreditCard,
|
|
},
|
|
{
|
|
id: 'billing',
|
|
name: 'Billing Report',
|
|
description: 'Export billing records including service purchases and payment status',
|
|
icon: FileText,
|
|
},
|
|
{
|
|
id: 'developer-requests',
|
|
name: 'Developer Requests Report',
|
|
description: 'Export developer hire requests and their status',
|
|
icon: Activity,
|
|
},
|
|
{
|
|
id: 'summary',
|
|
name: 'Summary Report',
|
|
description: 'Comprehensive overview with key metrics and statistics',
|
|
icon: BarChart3,
|
|
},
|
|
]
|
|
|
|
export default function ReportsManagement() {
|
|
const [selectedReport, setSelectedReport] = useState('')
|
|
const [format, setFormat] = useState('json')
|
|
const [dateFrom, setDateFrom] = useState('')
|
|
const [dateTo, setDateTo] = useState('')
|
|
const [generating, setGenerating] = useState(false)
|
|
|
|
const handleGenerateReport = async () => {
|
|
if (!selectedReport) {
|
|
toast({
|
|
title: 'Error',
|
|
description: 'Please select a report type',
|
|
variant: 'destructive',
|
|
})
|
|
return
|
|
}
|
|
|
|
setGenerating(true)
|
|
try {
|
|
const token =
|
|
localStorage.getItem('token') ||
|
|
document.cookie
|
|
.split('; ')
|
|
.find((row) => row.startsWith('token='))
|
|
?.split('=')[1]
|
|
|
|
const params = new URLSearchParams({
|
|
type: selectedReport,
|
|
format,
|
|
...(dateFrom && { dateFrom }),
|
|
...(dateTo && { dateTo }),
|
|
})
|
|
|
|
const response = await fetch(`/api/admin/reports?${params}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to generate report')
|
|
}
|
|
|
|
if (format === 'csv') {
|
|
// Handle CSV download
|
|
const blob = await response.blob()
|
|
const url = window.URL.createObjectURL(blob)
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = `${selectedReport}_report_${new Date().toISOString().split('T')[0]}.csv`
|
|
document.body.appendChild(a)
|
|
a.click()
|
|
window.URL.revokeObjectURL(url)
|
|
document.body.removeChild(a)
|
|
} else {
|
|
// Handle JSON download
|
|
const data = await response.json()
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
|
|
const url = window.URL.createObjectURL(blob)
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = `${selectedReport}_report_${new Date().toISOString().split('T')[0]}.json`
|
|
document.body.appendChild(a)
|
|
a.click()
|
|
window.URL.revokeObjectURL(url)
|
|
document.body.removeChild(a)
|
|
}
|
|
|
|
toast({
|
|
title: 'Success',
|
|
description: 'Report generated and downloaded successfully',
|
|
})
|
|
} catch (error) {
|
|
console.error('Report generation error:', error)
|
|
toast({
|
|
title: 'Error',
|
|
description: 'Failed to generate report',
|
|
variant: 'destructive',
|
|
})
|
|
} finally {
|
|
setGenerating(false)
|
|
}
|
|
}
|
|
|
|
const handleQuickExport = async (reportType: string) => {
|
|
setSelectedReport(reportType)
|
|
setFormat('csv')
|
|
|
|
// Trigger immediate export
|
|
setTimeout(() => {
|
|
handleGenerateReport()
|
|
}, 100)
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Quick Export Cards */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{reportTypes.map((report) => (
|
|
<Card key={report.id} className="hover:shadow-lg transition-shadow cursor-pointer">
|
|
<CardContent className="p-4">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1">
|
|
<div className="flex items-center mb-2">
|
|
<report.icon className="h-5 w-5 text-blue-600 mr-2" />
|
|
<h3 className="font-semibold">{report.name}</h3>
|
|
</div>
|
|
<p className="text-sm text-gray-600 mb-3">{report.description}</p>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => handleQuickExport(report.id)}
|
|
disabled={generating}
|
|
>
|
|
<Download className="h-4 w-4 mr-2" />
|
|
Quick Export CSV
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
{/* Custom Report Generator */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Custom Report Generator</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="reportType">Report Type</Label>
|
|
<Select value={selectedReport} onValueChange={setSelectedReport}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select report type" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{reportTypes.map((report) => (
|
|
<SelectItem key={report.id} value={report.id}>
|
|
{report.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="format">Format</Label>
|
|
<Select value={format} onValueChange={setFormat}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="json">JSON</SelectItem>
|
|
<SelectItem value="csv">CSV</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="dateFrom">From Date</Label>
|
|
<Input
|
|
id="dateFrom"
|
|
type="date"
|
|
value={dateFrom}
|
|
onChange={(e) => setDateFrom(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="dateTo">To Date</Label>
|
|
<Input
|
|
id="dateTo"
|
|
type="date"
|
|
value={dateTo}
|
|
onChange={(e) => setDateTo(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-end">
|
|
<Button onClick={handleGenerateReport} disabled={generating || !selectedReport}>
|
|
<Download className="h-4 w-4 mr-2" />
|
|
{generating ? 'Generating...' : 'Generate Report'}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Report Guidelines */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Report Guidelines</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h4 className="font-semibold text-gray-900">Data Privacy</h4>
|
|
<p className="text-sm text-gray-600">
|
|
Exported reports contain sensitive user data. Ensure compliance with data protection
|
|
regulations and handle all exported data securely.
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold text-gray-900">File Formats</h4>
|
|
<ul className="text-sm text-gray-600 space-y-1">
|
|
<li>
|
|
• <strong>JSON:</strong> Complete data with full structure, suitable for technical
|
|
analysis
|
|
</li>
|
|
<li>
|
|
• <strong>CSV:</strong> Simplified tabular format, suitable for spreadsheet
|
|
applications
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-semibold text-gray-900">Date Filtering</h4>
|
|
<p className="text-sm text-gray-600">
|
|
Use date filters to limit report scope. Large date ranges may result in slower
|
|
generation times and larger file sizes.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|