398 lines
14 KiB
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,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|