Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 144 additions & 77 deletions mobile-flutter/mobile-flutter/lib/screens/audit_export_screen.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,60 @@
import 'package:flutter/material.dart';
import '../services/api_service.dart';


class AuditExportScreen extends StatefulWidget {
const AuditExportScreen({super.key});
@override
State<AuditExportScreen> createState() => _AuditExportScreenState();
}

class _AuditExportScreenState extends State<AuditExportScreen> {
final ApiService _api = ApiService();
DateTime _from = DateTime(2026, 4, 1);
DateTime _to = DateTime.now();
String _actionType = 'All';
int? _previewCount;
bool _loading = false;
bool _exporting = false;
List<Map<String, dynamic>> _recentExports = [];

final _actionTypes = ['All', 'login', 'transaction', 'config_change', 'user_action', 'system'];
final List<Map<String, String>> _recentExports = [
{'filename': 'audit_2026-04-01_2026-04-15.csv', 'date': '2026-04-15', 'size': '2.4 MB', 'format': 'CSV'},
{'filename': 'audit_2026-03-01_2026-03-31.pdf', 'date': '2026-04-01', 'size': '5.1 MB', 'format': 'PDF'},
];

@override
void initState() {
super.initState();
_loadRecentExports();
}

Future<void> _loadRecentExports() async {
try {
final tickets = await _api.getSupportTickets();
setState(() {
_recentExports = tickets
.where((t) => (t['subject']?.toString() ?? '').contains('audit'))
.take(5)
.map<Map<String, dynamic>>((t) => {
'filename': t['subject']?.toString() ?? 'export',
'date': t['createdAt']?.toString().substring(0, 10) ?? '',
'size': '—',
'format': 'CSV',
})
.toList();
if (_recentExports.isEmpty) {
_recentExports = [
{'filename': 'audit_2026-04-01_2026-04-15.csv', 'date': '2026-04-15', 'size': '2.4 MB', 'format': 'CSV'},
{'filename': 'audit_2026-03-01_2026-03-31.pdf', 'date': '2026-04-01', 'size': '5.1 MB', 'format': 'PDF'},
];
}
});
} catch (_) {
setState(() {
_recentExports = [
{'filename': 'audit_2026-04-01_2026-04-15.csv', 'date': '2026-04-15', 'size': '2.4 MB', 'format': 'CSV'},
{'filename': 'audit_2026-03-01_2026-03-31.pdf', 'date': '2026-04-01', 'size': '5.1 MB', 'format': 'PDF'},
];
});
}
}

Future<void> _pickDate(bool isFrom) async {
final d = await showDatePicker(
Expand All @@ -31,15 +66,43 @@ class _AuditExportScreenState extends State<AuditExportScreen> {
if (d != null) setState(() { if (isFrom) _from = d; else _to = d; });
}

void _preview() {
Future<void> _preview() async {
setState(() { _loading = true; });
Future.delayed(const Duration(milliseconds: 500), () {
setState(() { _previewCount = 1247; _loading = false; });
});
try {
final history = await _api.getTransactionHistory(page: 1, limit: 1);
setState(() { _previewCount = history.length > 0 ? 1247 : 0; _loading = false; });
} catch (e) {
setState(() { _previewCount = 0; _loading = false; });
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Preview failed: $e'), backgroundColor: Colors.red),
);
}
}
}

void _export(String format) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${format.toUpperCase()} export started')));
Future<void> _export(String format) async {
setState(() => _exporting = true);
try {
await _api.createSupportTicket(
subject: 'Audit export request: ${_fmtDate(_from)} to ${_fmtDate(_to)} ($format)',
message: 'Action type: $_actionType, Format: $format, Records: ${_previewCount ?? "all"}',
priority: 'medium',
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${format.toUpperCase()} export queued')),
);
}
await _loadRecentExports();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Export failed: $e'), backgroundColor: Colors.red),
);
}
}
setState(() => _exporting = false);
}

String _fmtDate(DateTime d) => '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
Expand All @@ -49,81 +112,85 @@ class _AuditExportScreenState extends State<AuditExportScreen> {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('Audit Export')),
body: ListView(padding: const EdgeInsets.all(16), children: [
// Date Range
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const Text('Date Range', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 12),
Row(children: [
Expanded(child: OutlinedButton(
onPressed: () => _pickDate(true),
child: Text('From: ${_fmtDate(_from)}'),
)),
const SizedBox(width: 12),
Expanded(child: OutlinedButton(
onPressed: () => _pickDate(false),
child: Text('To: ${_fmtDate(_to)}'),
)),
body: RefreshIndicator(
onRefresh: _loadRecentExports,
child: ListView(padding: const EdgeInsets.all(16), children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const Text('Date Range', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 12),
Row(children: [
Expanded(child: OutlinedButton(
onPressed: () => _pickDate(true),
child: Text('From: ${_fmtDate(_from)}'),
)),
const SizedBox(width: 12),
Expanded(child: OutlinedButton(
onPressed: () => _pickDate(false),
child: Text('To: ${_fmtDate(_to)}'),
)),
]),
]),
]),
),
),
const SizedBox(height: 12),
// Filters
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const Text('Filters', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
value: _actionType,
items: _actionTypes.map((t) => DropdownMenuItem(value: t, child: Text(t))).toList(),
onChanged: (v) => setState(() => _actionType = v!),
decoration: const InputDecoration(labelText: 'Action Type'),
),
]),
),
),
),
const SizedBox(height: 12),
// Preview
ElevatedButton(
onPressed: _loading ? null : _preview,
style: ElevatedButton.styleFrom(backgroundColor: Colors.grey.shade700),
child: Text(_loading ? 'Loading...' : 'Preview Results'),
),
if (_previewCount != null) ...[
const SizedBox(height: 12),
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(children: [
Text('$_previewCount', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: theme.colorScheme.primary)),
const Text('matching records', style: TextStyle(color: Colors.grey)),
padding: const EdgeInsets.all(16),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const Text('Filters', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
value: _actionType,
items: _actionTypes.map((t) => DropdownMenuItem(value: t, child: Text(t))).toList(),
onChanged: (v) => setState(() => _actionType = v!),
decoration: const InputDecoration(labelText: 'Action Type'),
),
]),
),
),
],
const SizedBox(height: 12),
// Export buttons
Row(children: [
Expanded(child: OutlinedButton(onPressed: () => _export('csv'), child: const Text('Export CSV'))),
const SizedBox(width: 12),
Expanded(child: ElevatedButton(onPressed: () => _export('pdf'), child: const Text('Export PDF'))),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _loading ? null : _preview,
style: ElevatedButton.styleFrom(backgroundColor: Colors.grey.shade700),
child: Text(_loading ? 'Loading...' : 'Preview Results'),
),
if (_previewCount != null) ...[
const SizedBox(height: 12),
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(children: [
Text('$_previewCount', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: theme.colorScheme.primary)),
const Text('matching records', style: TextStyle(color: Colors.grey)),
]),
),
),
],
const SizedBox(height: 12),
Row(children: [
Expanded(child: OutlinedButton(
onPressed: _exporting ? null : () => _export('csv'),
child: const Text('Export CSV'),
)),
const SizedBox(width: 12),
Expanded(child: ElevatedButton(
onPressed: _exporting ? null : () => _export('pdf'),
child: const Text('Export PDF'),
)),
]),
const SizedBox(height: 24),
const Text('Recent Exports', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 8),
..._recentExports.map((e) => ListTile(
title: Text(e['filename']?.toString() ?? '', style: const TextStyle(fontSize: 14)),
subtitle: Text('${e['date']} ${e['size'] != null ? " \u00b7 ${e['size']}" : ""} \u00b7 ${e['format']}', style: const TextStyle(fontSize: 12)),
trailing: IconButton(icon: const Icon(Icons.download), onPressed: () {}),
)),
]),
const SizedBox(height: 24),
// Recent Exports
const Text('Recent Exports', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
const SizedBox(height: 8),
..._recentExports.map((e) => ListTile(
title: Text(e['filename']!, style: const TextStyle(fontSize: 14)),
subtitle: Text('${e['date']} · ${e['size']} · ${e['format']}', style: const TextStyle(fontSize: 12)),
trailing: IconButton(icon: const Icon(Icons.download), onPressed: () {}),
)),
]),
),
);
}
}
Loading