cashumit/lib/screens/config_screen.dart

398 lines
14 KiB
Dart

// lib/screens/config_screen.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cashumit/services/firefly_api_service.dart';
import 'package:cashumit/services/account_cache_service.dart';
import 'package:cashumit/services/account_mirror_service.dart';
class ConfigScreen extends StatefulWidget {
const ConfigScreen({super.key});
@override
State<ConfigScreen> createState() => _ConfigScreenState();
}
class _ConfigScreenState extends State<ConfigScreen> {
final _formKey = GlobalKey<FormState>();
final _urlController = TextEditingController();
final _tokenController = TextEditingController();
bool _isTestingConnection = false;
bool _isTestingAuth = false;
static const String _prefsKeyUrl = 'firefly_url';
static const String _prefsKeyToken = 'firefly_token';
@override
void initState() {
super.initState();
_loadConfig();
}
/// Memuat konfigurasi yang tersimpan dari shared preferences.
Future<void> _loadConfig() async {
final prefs = await SharedPreferences.getInstance();
final savedUrl = prefs.getString(_prefsKeyUrl) ?? '';
final savedToken = prefs.getString(_prefsKeyToken) ?? '';
setState(() {
_urlController.text = savedUrl;
_tokenController.text = savedToken;
});
}
/// Menyimpan konfigurasi ke shared preferences.
Future<void> _saveConfig() async {
if (_formKey.currentState!.validate()) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefsKeyUrl, _urlController.text);
await prefs.setString(_prefsKeyToken, _tokenController.text);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Konfigurasi tersimpan!')),
);
Navigator.pop(context, true);
}
}
}
/// Menguji koneksi ke Firefly III.
Future<void> _testConnection() async {
if (_urlController.text.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text('Silakan masukkan URL Firefly III terlebih dahulu')),
);
}
return;
}
setState(() {
_isTestingConnection = true;
});
try {
final isConnected =
await FireflyApiService.testConnection(baseUrl: _urlController.text);
if (mounted) {
if (isConnected) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Koneksi berhasil!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Koneksi gagal. Periksa URL dan koneksi internet Anda.')),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Terjadi kesalahan saat menguji koneksi')),
);
}
} finally {
if (mounted) {
setState(() {
_isTestingConnection = false;
});
}
}
}
/// Menguji autentikasi dengan token.
Future<void> _testAuthentication() async {
if (_urlController.text.isEmpty || _tokenController.text.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Silakan masukkan URL dan token Firefly III terlebih dahulu')),
);
}
return;
}
setState(() {
_isTestingAuth = true;
});
try {
final isAuthenticated = await FireflyApiService.testAuthentication(
baseUrl: _urlController.text,
accessToken: _tokenController.text,
);
if (mounted) {
if (isAuthenticated) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Autentikasi berhasil!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Autentikasi gagal. Periksa token Anda.')),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Terjadi kesalahan saat menguji autentikasi')),
);
}
} finally {
if (mounted) {
setState(() {
_isTestingAuth = false;
});
}
}
}
@override
void dispose() {
_urlController.dispose();
_tokenController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Konfigurasi'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Firefly III Configuration Section
const Text(
'Konfigurasi Firefly III',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'Masukkan detail koneksi ke instance Firefly III Anda:',
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
// TextField untuk URL
TextFormField(
controller: _urlController,
decoration: const InputDecoration(
labelText: 'Firefly III URL',
hintText: 'https://firefly.yourdomain.com',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'URL tidak boleh kosong';
}
if (!value.startsWith('http')) {
return 'URL harus dimulai dengan http:// atau https://';
}
return null;
},
),
const SizedBox(height: 20),
// TextField untuk Token
TextFormField(
controller: _tokenController,
decoration: const InputDecoration(
labelText: 'Personal Access Token',
border: OutlineInputBorder(),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Token tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: 20),
// Tombol Simpan
ElevatedButton.icon(
onPressed: _saveConfig,
icon: const Icon(Icons.save),
label: const Text('Simpan Konfigurasi'),
),
const SizedBox(height: 10),
// Tombol Test Connection
ElevatedButton.icon(
onPressed: _isTestingConnection ? null : _testConnection,
icon: _isTestingConnection
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.link),
label: Text(_isTestingConnection
? 'Menguji Koneksi...'
: 'Test Koneksi'),
),
const SizedBox(height: 10),
// Tombol Test Authentication
ElevatedButton.icon(
onPressed: _isTestingAuth ? null : _testAuthentication,
icon: _isTestingAuth
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.verified_user),
label: Text(_isTestingAuth
? 'Menguji Autentikasi...'
: 'Test Autentikasi'),
),
const SizedBox(height: 30),
// Offline Account Configuration Section
const Text(
'Pengaturan Akun Offline',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'Atur sinkronisasi data akun ketika server Firefly III terhubung untuk digunakan saat offline.',
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
// Tampilkan informasi waktu sync terakhir
FutureBuilder<DateTime?>(
future: AccountMirrorService.getLastSyncTime(),
builder: (context, snapshot) {
String syncInfo = 'Waktu sync terakhir: ';
if (snapshot.hasData && snapshot.data != null) {
final syncTime = snapshot.data!;
syncInfo += AccountMirrorService.formatSyncTime(syncTime);
} else {
syncInfo += 'Belum pernah disinkronisasi';
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
syncInfo,
style:
const TextStyle(fontSize: 14, color: Colors.grey),
),
const SizedBox(height: 10),
// Tombol untuk sync manual
ElevatedButton.icon(
onPressed: () async {
final prefs = await SharedPreferences.getInstance();
final url = prefs.getString('firefly_url');
final token = prefs.getString('firefly_token');
if (url != null &&
token != null &&
url.isNotEmpty &&
token.isNotEmpty) {
try {
final revenue =
await FireflyApiService.fetchAccounts(
baseUrl: url,
accessToken: token,
type: 'revenue',
);
final asset =
await FireflyApiService.fetchAccounts(
baseUrl: url,
accessToken: token,
type: 'asset',
);
await AccountMirrorService.mirrorAccounts([
...revenue,
...asset,
]);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text('Sinkronisasi akun berhasil!')),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text('Gagal menyinkronisasi: $e')),
);
}
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Silakan atur URL dan token Firefly III terlebih dahulu')),
);
}
}
},
icon: const Icon(Icons.sync),
label: const Text('Sinkronisasi Manual'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
),
),
const SizedBox(height: 10),
// Tombol untuk menghapus cache akun
ElevatedButton.icon(
onPressed: () async {
await AccountCacheService.clearCache();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Cache akun dihapus')),
);
setState(() {});
}
},
icon: const Icon(Icons.delete),
label: const Text('Hapus Cache Akun'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
),
),
],
);
},
),
],
),
),
),
);
}
}