729 lines
27 KiB
Dart
729 lines
27 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';
|
|
import 'package:cashumit/models/report_signature_config.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();
|
|
|
|
// Controller Konfigurasi Laporan Keuangan (4 Pihak Penandatangan Grid 2x2)
|
|
final _reportTitleController = TextEditingController();
|
|
final _cityController = TextEditingController();
|
|
|
|
// Pihak 1: Pelaksana UPJ (Kiri Atas)
|
|
final _sig1RoleController = TextEditingController();
|
|
final _sig1NameController = TextEditingController();
|
|
final _sig1NipController = TextEditingController();
|
|
|
|
// Pihak 2: Pembuat Laporan (Kanan Atas)
|
|
final _sig2RoleController = TextEditingController();
|
|
final _sig2NameController = TextEditingController();
|
|
final _sig2NipController = TextEditingController();
|
|
|
|
// Pihak 3: Koordinator UPJ (Kiri Bawah)
|
|
final _sig3RoleController = TextEditingController();
|
|
final _sig3NameController = TextEditingController();
|
|
final _sig3NipController = TextEditingController();
|
|
|
|
// Pihak 4: KPA BLUD SMKN 4 Malang (Kanan Bawah)
|
|
final _sig4RoleController = TextEditingController();
|
|
final _sig4NameController = TextEditingController();
|
|
final _sig4NipController = TextEditingController();
|
|
|
|
bool _isTestingConnection = false;
|
|
bool _isTestingAuth = false;
|
|
|
|
// Pengaturan cetak gambar di struk thermal
|
|
bool _printLogo = true;
|
|
bool _printQris = true;
|
|
|
|
static const String _prefsKeyUrl = 'firefly_url';
|
|
static const String _prefsKeyToken = 'firefly_token';
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadConfig();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_urlController.dispose();
|
|
_tokenController.dispose();
|
|
_reportTitleController.dispose();
|
|
_cityController.dispose();
|
|
|
|
_sig1RoleController.dispose();
|
|
_sig1NameController.dispose();
|
|
_sig1NipController.dispose();
|
|
|
|
_sig2RoleController.dispose();
|
|
_sig2NameController.dispose();
|
|
_sig2NipController.dispose();
|
|
|
|
_sig3RoleController.dispose();
|
|
_sig3NameController.dispose();
|
|
_sig3NipController.dispose();
|
|
|
|
_sig4RoleController.dispose();
|
|
_sig4NameController.dispose();
|
|
_sig4NipController.dispose();
|
|
|
|
super.dispose();
|
|
}
|
|
|
|
/// 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) ?? '';
|
|
|
|
final sigConfig = await ReportSignatureConfig.load();
|
|
|
|
setState(() {
|
|
_urlController.text = savedUrl;
|
|
_tokenController.text = savedToken;
|
|
_reportTitleController.text = sigConfig.reportTitle;
|
|
_cityController.text = sigConfig.city;
|
|
_printLogo = prefs.getBool('print_logo_enabled') ?? true;
|
|
_printQris = prefs.getBool('print_qris_enabled') ?? true;
|
|
|
|
_sig1RoleController.text = sigConfig.signatory1Role;
|
|
_sig1NameController.text = sigConfig.signatory1Name;
|
|
_sig1NipController.text = sigConfig.signatory1Nip;
|
|
|
|
_sig2RoleController.text = sigConfig.signatory2Role;
|
|
_sig2NameController.text = sigConfig.signatory2Name;
|
|
_sig2NipController.text = sigConfig.signatory2Nip;
|
|
|
|
_sig3RoleController.text = sigConfig.signatory3Role;
|
|
_sig3NameController.text = sigConfig.signatory3Name;
|
|
_sig3NipController.text = sigConfig.signatory3Nip;
|
|
|
|
_sig4RoleController.text = sigConfig.signatory4Role;
|
|
_sig4NameController.text = sigConfig.signatory4Name;
|
|
_sig4NipController.text = sigConfig.signatory4Nip;
|
|
});
|
|
}
|
|
|
|
/// 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);
|
|
await prefs.setBool('print_logo_enabled', _printLogo);
|
|
await prefs.setBool('print_qris_enabled', _printQris);
|
|
|
|
final sigConfig = ReportSignatureConfig(
|
|
reportTitle: _reportTitleController.text.trim().isEmpty
|
|
? 'LAPORAN KEUANGAN UPJ MEKATRONIKA'
|
|
: _reportTitleController.text.trim(),
|
|
city: _cityController.text.trim().isEmpty
|
|
? 'Malang'
|
|
: _cityController.text.trim(),
|
|
signatory1Role: _sig1RoleController.text.trim().isEmpty
|
|
? 'Mengetahui,\nPelaksana UPJ Mekatronika'
|
|
: _sig1RoleController.text.trim(),
|
|
signatory1Name: _sig1NameController.text.trim().isEmpty
|
|
? 'Anggoro Dwi Nur Rohman, S.Pd, M.T'
|
|
: _sig1NameController.text.trim(),
|
|
signatory1Nip: _sig1NipController.text.trim(),
|
|
signatory2Role: _sig2RoleController.text.trim().isEmpty
|
|
? 'Dibuat oleh,'
|
|
: _sig2RoleController.text.trim(),
|
|
signatory2Name: _sig2NameController.text.trim().isEmpty
|
|
? 'Rama Dwi Nofanto Setiawan'
|
|
: _sig2NameController.text.trim(),
|
|
signatory2Nip: _sig2NipController.text.trim(),
|
|
signatory3Role: _sig3RoleController.text.trim().isEmpty
|
|
? 'Koordinator UPJ'
|
|
: _sig3RoleController.text.trim(),
|
|
signatory3Name: _sig3NameController.text.trim().isEmpty
|
|
? 'Puguh Hartono, SPd'
|
|
: _sig3NameController.text.trim(),
|
|
signatory3Nip: _sig3NipController.text.trim(),
|
|
signatory4Role: _sig4RoleController.text.trim().isEmpty
|
|
? 'KPA BLUD\nSMKN 4 Malang'
|
|
: _sig4RoleController.text.trim(),
|
|
signatory4Name: _sig4NameController.text.trim().isEmpty
|
|
? 'Drs. Gunawan Dwiyono, S.ST, M.Pd'
|
|
: _sig4NameController.text.trim(),
|
|
signatory4Nip: _sig4NipController.text.trim(),
|
|
);
|
|
await sigConfig.save();
|
|
|
|
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
|
|
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),
|
|
|
|
// ==================== PENGATURAN CETAK STRUK ====================
|
|
const Divider(thickness: 1.5),
|
|
const SizedBox(height: 10),
|
|
const Text(
|
|
'Pengaturan Cetak Struk',
|
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'Pilih gambar yang ikut dicetak pada struk printer thermal. Logo dan QRIS diambil dari Konfigurasi Info Toko.',
|
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
|
),
|
|
SwitchListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: const Text('Cetak logo toko'),
|
|
value: _printLogo,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_printLogo = value;
|
|
});
|
|
},
|
|
),
|
|
SwitchListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: const Text('Cetak QRIS'),
|
|
value: _printQris,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_printQris = value;
|
|
});
|
|
},
|
|
),
|
|
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,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 30),
|
|
|
|
// ==================== PENGATURAN LAPORAN KEUANGAN ====================
|
|
const Divider(thickness: 1.5),
|
|
const SizedBox(height: 10),
|
|
const Text(
|
|
'Pengaturan Dokumen Laporan Keuangan',
|
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'Kustomisasi judul dokumen, kota, dan data penandatangan resmi untuk laporan PDF A4.',
|
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
TextFormField(
|
|
controller: _reportTitleController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Judul Laporan',
|
|
hintText: 'LAPORAN KEUANGAN UPJ MEKATRONIKA',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _cityController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Kota Penerbitan Laporan',
|
|
hintText: 'Malang',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
|
|
// Penandatangan 1 (Pelaksana UPJ - Kiri Atas)
|
|
const Text(
|
|
'1. Pelaksana UPJ (Kiri Atas):',
|
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig1RoleController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Jabatan / Posisi Penandatangan 1',
|
|
hintText: 'Mengetahui,\nPelaksana UPJ Mekatronika',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig1NameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nama Lengkap & Gelar',
|
|
hintText: 'Anggoro Dwi Nur Rohman, S.Pd, M.T',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig1NipController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'NIP / NNIP Penandatangan 1',
|
|
hintText: 'NNIP. 20533816260193012106054',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Penandatangan 2 (Pembuat Laporan - Kanan Atas)
|
|
const Text(
|
|
'2. Pembuat Laporan (Kanan Atas):',
|
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig2RoleController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Keterangan Pembuat',
|
|
hintText: 'Dibuat oleh,',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig2NameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nama Lengkap & Gelar',
|
|
hintText: 'Rama Dwi Nofanto Setiawan',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig2NipController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'NIP / ID (Opsional)',
|
|
hintText: 'Kosongkan jika tidak ada',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Penandatangan 3 (Koordinator UPJ - Kiri Bawah)
|
|
const Text(
|
|
'3. Koordinator UPJ (Kiri Bawah):',
|
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig3RoleController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Jabatan Penandatangan 3',
|
|
hintText: 'Koordinator UPJ',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig3NameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nama Lengkap & Gelar',
|
|
hintText: 'Puguh Hartono, SPd',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig3NipController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'NIP Penandatangan 3',
|
|
hintText: '197003152003121009',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Penandatangan 4 (KPA BLUD - Kanan Bawah)
|
|
const Text(
|
|
'4. KPA BLUD SMKN 4 Malang (Kanan Bawah):',
|
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig4RoleController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Jabatan Penandatangan 4',
|
|
hintText: 'KPA BLUD\nSMKN 4 Malang',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig4NameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nama Lengkap & Gelar',
|
|
hintText: 'Drs. Gunawan Dwiyono, S.ST, M.Pd',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
TextFormField(
|
|
controller: _sig4NipController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'NIP Penandatangan 4',
|
|
hintText: '19670930 199512 1 002',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Tombol Simpan Semua
|
|
ElevatedButton.icon(
|
|
onPressed: _saveConfig,
|
|
icon: const Icon(Icons.save),
|
|
label: const Text('Simpan Pengaturan Laporan'),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF14532D),
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|