import 'package:flutter/material.dart'; import 'package:login_vault_app/auth_service.dart'; import 'package:login_vault_app/database_helper.dart'; import 'package:login_vault_app/settings_screen.dart'; import 'package:login_vault_app/theme_provider.dart'; import 'package:login_vault_app/vault_entry_dialogs.dart'; /// Main dashboard: search ids, list entries, expand content, edit, swipe delete. class DashboardScreen extends StatefulWidget { final AuthService auth; final ThemeProvider theme; final String email; const DashboardScreen( {super.key, required this.auth, required this.theme, required this.email}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { final _db = DatabaseHelper(); final _searchCtrl = TextEditingController(); List _entries = []; bool _loading = true; @override void initState() { super.initState(); _searchCtrl.addListener(() { _touch(); // user activity _onSearchChanged(); }); _refresh(); } @override void dispose() { _searchCtrl.dispose(); super.dispose(); } /// Refresh the session timestamp — called on every user interaction so the /// 3-minute inactivity timeout only fires when the user is truly idle. void _touch() { widget.auth.openSession(); } void _onSearchChanged() => _refresh(); Future _refresh() async { setState(() => _loading = true); final list = await _db.search(_searchCtrl.text); if (mounted) { setState(() { _entries = list; _loading = false; }); } } Future _deleteEntry(VaultEntry e) async { final confirmed = await showConfirmDialog( context, title: 'Hapus entri?', body: 'Yakin ingin menghapus "${e.id}"? Tindakan ini tidak dapat dibatalkan.', ); if (confirmed == true) { await _db.delete(e.id); _refresh(); } } Future _editEntry(VaultEntry e) async { _touch(); final result = await showEntryDialog( context, title: 'Edit konten', id: e.id, initialContent: e.content, lockedId: true, ); if (result != null) { await _db.update(e.id, result.content); _refresh(); } } Future _addEntry() async { _touch(); final result = await showEntryDialog( context, title: 'Tambah entri baru', id: '', initialContent: '', lockedId: false, ); if (result != null && result.id.isNotEmpty) { try { await _db.insert(VaultEntry(id: result.id, content: result.content)); _refresh(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Gagal: ID "${result.id}" sudah ada')), ); } } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Login Vault'), actions: [ IconButton( icon: const Icon(Icons.settings), tooltip: 'Pengaturan', onPressed: () { _touch(); Navigator.push( context, MaterialPageRoute( builder: (_) => SettingsScreen( auth: widget.auth, theme: widget.theme), ), ).then((_) => _refresh()); }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: _addEntry, tooltip: 'Tambah entri', child: const Icon(Icons.add), ), body: Column( children: [ Padding( padding: const EdgeInsets.all(12), child: TextField( controller: _searchCtrl, decoration: InputDecoration( labelText: 'Cari ID...', prefixIcon: const Icon(Icons.search), border: const OutlineInputBorder(), suffixIcon: _searchCtrl.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _touch(); _searchCtrl.clear(); _refresh(); }, ) : null, ), ), ), Expanded( child: _loading ? const Center(child: CircularProgressIndicator()) : _entries.isEmpty ? const Center(child: Text('Tidak ada entri')) : ListView.builder( itemCount: _entries.length, itemBuilder: (ctx, i) { final e = _entries[i]; return Dismissible( key: Key(e.id), direction: DismissDirection.endToStart, background: Container( color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.symmetric(horizontal: 20), child: const Icon(Icons.delete, color: Colors.white), ), confirmDismiss: (_) async { _touch(); final c = await showConfirmDialog( context, title: 'Hapus entri?', body: 'Yakin hapus "${e.id}"?', ); if (c == true) await _deleteEntry(e); return false; }, child: Card( margin: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), child: ExpansionTile( onExpansionChanged: (_) => _touch(), title: Text(e.id, style: const TextStyle( fontWeight: FontWeight.bold)), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit), tooltip: 'Edit', onPressed: () => _editEntry(e), ), IconButton( icon: const Icon(Icons.delete), tooltip: 'Hapus', onPressed: () { _touch(); _deleteEntry(e); }, ), ], ), children: [ Padding( padding: const EdgeInsets.all(16), child: SelectableText(e.content), ), ], ), ), ); }, ), ), ], ), ); } }