Browse Source

addional enhancement to view all

enzo 5 days ago
parent
commit
e887bd837a
5 changed files with 134 additions and 32 deletions
  1. 2 0
      assets/lang/en.json
  2. 2 0
      assets/lang/ms.json
  3. 2 0
      assets/lang/zh.json
  4. 92 5
      lib/screens/history_screen.dart
  5. 36 27
      lib/screens/home_screen.dart

+ 2 - 0
assets/lang/en.json

@@ -74,6 +74,8 @@
   "all_dates_label": "All Dates",
   "clear_date_range_tooltip": "Clear date range",
   "reset_history_tooltip": "Reset History",
+  "view_toggle_flat_tooltip": "View All Scans",
+  "view_toggle_batches_tooltip": "View by Batch",
   "history_empty_no_records": "No records found yet.",
   "history_empty_no_matches": "No records match the selected filters.",
   "ungrouped_scans_label": "Ungrouped Scans",

+ 2 - 0
assets/lang/ms.json

@@ -74,6 +74,8 @@
   "all_dates_label": "Semua Tarikh",
   "clear_date_range_tooltip": "Kosongkan julat tarikh",
   "reset_history_tooltip": "Set Semula Sejarah",
+  "view_toggle_flat_tooltip": "Lihat Semua Imbasan",
+  "view_toggle_batches_tooltip": "Lihat mengikut Kumpulan",
   "history_empty_no_records": "Tiada rekod ditemui lagi.",
   "history_empty_no_matches": "Tiada rekod sepadan dengan penapis yang dipilih.",
   "ungrouped_scans_label": "Imbasan Tidak Berkumpulan",

+ 2 - 0
assets/lang/zh.json

@@ -74,6 +74,8 @@
   "all_dates_label": "所有日期",
   "clear_date_range_tooltip": "清除日期范围",
   "reset_history_tooltip": "重置历史记录",
+  "view_toggle_flat_tooltip": "查看所有扫描",
+  "view_toggle_batches_tooltip": "按批次查看",
   "history_empty_no_records": "尚未找到任何记录。",
   "history_empty_no_matches": "没有符合所选筛选条件的记录。",
   "ungrouped_scans_label": "未分组扫描",

+ 92 - 5
lib/screens/history_screen.dart

@@ -26,6 +26,15 @@ class _BatchGroup {
   }
 }
 
+/// A single record paired with the batch group it belongs to, so a flat
+/// (ungrouped) list item can still open [RecordDetailScreen] with the full
+/// batch as navigable frames.
+class _FlatRecord {
+  final PalmRecord record;
+  final _BatchGroup group;
+  _FlatRecord({required this.record, required this.group});
+}
+
 class HistoryScreen extends StatefulWidget {
   const HistoryScreen({super.key});
 
@@ -41,6 +50,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
 
   bool _sortAscending = false; // false = latest first (default)
   DateTimeRange? _dateRange;
+  bool _flatView = false;
 
   @override
   void initState() {
@@ -146,6 +156,22 @@ class _HistoryScreenState extends State<HistoryScreen> {
     _loadHistory();
   }
 
+  List<_FlatRecord> get _flatRecords {
+    final all = <_FlatRecord>[
+      for (final g in _groups)
+        for (final r in g.records) _FlatRecord(record: r, group: g),
+    ];
+    all.sort((a, b) => _sortAscending
+        ? a.record.timestamp.compareTo(b.record.timestamp)
+        : b.record.timestamp.compareTo(a.record.timestamp));
+    return all;
+  }
+
+  Future<void> _deleteSingleRecord(PalmRecord record) async {
+    await _dbHelper.deleteRecord(record);
+    _loadHistory();
+  }
+
   bool get _hasActiveFilter => _dateRange != null || _sortAscending;
 
   Future<void> _openFilterSheet() async {
@@ -277,6 +303,11 @@ class _HistoryScreenState extends State<HistoryScreen> {
       appBar: AppBar(
         title: Text(AppStrings.t('history_vault_title')),
         actions: [
+          IconButton(
+            icon: Icon(_flatView ? Icons.collections_bookmark : Icons.view_list),
+            tooltip: _flatView ? AppStrings.t('view_toggle_batches_tooltip') : AppStrings.t('view_toggle_flat_tooltip'),
+            onPressed: () => setState(() => _flatView = !_flatView),
+          ),
           IconButton(
             icon: Icon(_hasActiveFilter ? Icons.filter_alt : Icons.filter_list),
             tooltip: AppStrings.t('filter_sort_title'),
@@ -294,11 +325,67 @@ class _HistoryScreenState extends State<HistoryScreen> {
           ? const Center(child: CircularProgressIndicator())
           : _groups.isEmpty
               ? _buildEmptyState()
-              : ListView.builder(
-                  itemCount: _groups.length,
-                  padding: const EdgeInsets.all(12),
-                  itemBuilder: (context, index) => _buildGroupCard(_groups[index]),
-                ),
+              : _flatView
+                  ? _buildFlatList()
+                  : ListView.builder(
+                      itemCount: _groups.length,
+                      padding: const EdgeInsets.all(12),
+                      itemBuilder: (context, index) => _buildGroupCard(_groups[index]),
+                    ),
+    );
+  }
+
+  Widget _buildFlatList() {
+    final flat = _flatRecords;
+    return ListView.builder(
+      itemCount: flat.length,
+      padding: const EdgeInsets.all(12),
+      itemBuilder: (context, index) => _buildFlatTile(flat[index]),
+    );
+  }
+
+  Widget _buildFlatTile(_FlatRecord entry) {
+    final record = entry.record;
+    final statusColor = getStatusColor(record.ripenessClass);
+    return Dismissible(
+      key: ValueKey('flat_${record.id}'),
+      direction: DismissDirection.endToStart,
+      background: _buildDeleteBackground(),
+      confirmDismiss: (_) => _confirmDelete(AppStrings.t('history_delete_record_confirm')),
+      onDismissed: (_) => _deleteSingleRecord(record),
+      child: Card(
+        margin: const EdgeInsets.only(bottom: 12),
+        elevation: 3,
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
+        child: ListTile(
+          leading: CircleAvatar(
+            backgroundColor: statusColor.withValues(alpha: 0.2),
+            child: Icon(Icons.spa, color: statusColor),
+          ),
+          title: Text(
+            AppStrings.className(record.ripenessClass),
+            style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+          ),
+          subtitle: Text(
+            AppStrings.tp('record_conf_timestamp', {
+              'percent': (record.confidence * 100).toStringAsFixed(1),
+              'timestamp': record.timestamp.toString().split('.')[0],
+            }),
+            style: const TextStyle(fontSize: 12),
+          ),
+          trailing: const Icon(Icons.chevron_right),
+          isThreeLine: true,
+          onTap: () => Navigator.push(
+            context,
+            MaterialPageRoute(
+              builder: (context) => RecordDetailScreen(
+                frames: entry.group.records,
+                initialIndex: entry.group.records.indexOf(record),
+              ),
+            ),
+          ),
+        ),
+      ),
     );
   }
 

+ 36 - 27
lib/screens/home_screen.dart

@@ -75,7 +75,7 @@ class _HomeScreenState extends State<HomeScreen> {
                     ],
                   ),
                   const SizedBox(height: 20),
-                  _buildStatCard(colorScheme),
+                  _buildStatCard(context, colorScheme),
                   const SizedBox(height: 24),
                   GridView.count(
                     shrinkWrap: true,
@@ -318,33 +318,42 @@ class _HomeScreenState extends State<HomeScreen> {
     );
   }
 
-  Widget _buildStatCard(ColorScheme colorScheme) {
-    return Container(
-      width: double.infinity,
-      padding: const EdgeInsets.all(16),
-      decoration: BoxDecoration(
-        color: colorScheme.surfaceContainerHighest,
-        borderRadius: BorderRadius.circular(16),
-        border: Border.all(color: colorScheme.primary.withValues(alpha: 0.15)),
+  Widget _buildStatCard(BuildContext context, ColorScheme colorScheme) {
+    return InkWell(
+      borderRadius: BorderRadius.circular(16),
+      onTap: () => Navigator.push(
+        context,
+        MaterialPageRoute(builder: (context) => const HistoryScreen()),
       ),
-      child: Row(
-        children: [
-          Icon(Icons.analytics_outlined, color: colorScheme.primary, size: 28),
-          const SizedBox(width: 12),
-          Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
-            children: [
-              Text(
-                _totalScans == null ? '—' : '$_totalScans',
-                style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: colorScheme.onSurface),
-              ),
-              Text(
-                AppStrings.t('total_scans'),
-                style: TextStyle(fontSize: 13, color: colorScheme.onSurface.withValues(alpha: 0.6)),
-              ),
-            ],
-          ),
-        ],
+      child: Container(
+        width: double.infinity,
+        padding: const EdgeInsets.all(16),
+        decoration: BoxDecoration(
+          color: colorScheme.surfaceContainerHighest,
+          borderRadius: BorderRadius.circular(16),
+          border: Border.all(color: colorScheme.primary.withValues(alpha: 0.15)),
+        ),
+        child: Row(
+          children: [
+            Icon(Icons.analytics_outlined, color: colorScheme.primary, size: 28),
+            const SizedBox(width: 12),
+            Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Text(
+                  _totalScans == null ? '—' : '$_totalScans',
+                  style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: colorScheme.onSurface),
+                ),
+                Text(
+                  AppStrings.t('total_scans'),
+                  style: TextStyle(fontSize: 13, color: colorScheme.onSurface.withValues(alpha: 0.6)),
+                ),
+              ],
+            ),
+            const Spacer(),
+            Icon(Icons.chevron_right, color: colorScheme.onSurface.withValues(alpha: 0.4)),
+          ],
+        ),
       ),
     );
   }