Files
pfm-ocr/lib/features/camera/camera_drawer.dart
T
fhanyuh caf8e98378 chore: normalize line endings (CRLF -> LF)
No content changes: git diff --ignore-all-space over these files is empty.
The churn came from editing on Windows against a repo checked out with LF.
2026-08-27 10:40:49 +07:00

234 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/app_config.dart';
import '../auth/auth_provider.dart';
import '../documents/pending_documents_provider.dart';
import 'camera_drawer_helpers.dart';
import 'camera_drawer_mode_toggle.dart';
/// Main navigation drawer for the camera screen.
///
/// Split during task 8.1 (AGENTS.md §3 — 256-line threshold on touched files):
/// - [CameraDrawerModeToggle] owns the DO/Product mode toggle pill and
/// [scanModeProvider] read/write (including the new doModeColor cue).
/// - [camera_drawer_helpers.dart] contains stateless UI helpers
/// (section header, drawer item, dialogs).
class CameraDrawer extends ConsumerStatefulWidget {
const CameraDrawer({super.key});
@override
ConsumerState<CameraDrawer> createState() => _CameraDrawerState();
}
class _CameraDrawerState extends ConsumerState<CameraDrawer> {
String _storeName = 'Store Name';
@override
void initState() {
super.initState();
_loadStoreName();
}
Future<void> _loadStoreName() async {
final prefs = await SharedPreferences.getInstance();
if (mounted) {
setState(() {
_storeName = prefs.getString('nama_toko') ?? 'Store Name';
});
}
}
@override
Widget build(BuildContext context) {
return Drawer(
backgroundColor: Colors.white,
child: SafeArea(
child: Column(
children: [
// ── Profile header ─────────────────────────────────────────
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Column(
children: [
Align(
alignment: Alignment.topRight,
child: IconButton(
icon: const Icon(Icons.close, color: Colors.grey),
onPressed: () => Navigator.pop(context),
),
),
const SizedBox(height: 8),
Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
border: Border.all(color: Colors.grey.shade200, width: 1),
),
child: ClipOval(
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Image.asset('assets/logo.png', fit: BoxFit.contain),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Prima Fresh Mart',
style: AppConfig.h3.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
_storeName,
style: AppConfig.bodySmall.copyWith(
color: Colors.grey.shade600,
fontSize: 13,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
],
),
),
const SizedBox(height: 16),
const Divider(height: 1, thickness: 1),
const SizedBox(height: 20),
// ── Mode toggle (DO/Product) ────────────────────────────────
const CameraDrawerModeToggle(),
const SizedBox(height: 20),
// ── Menu items ─────────────────────────────────────────────
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
children: [
buildDrawerSectionHeader('DASHBOARD'),
buildDrawerItem(
icon: Icons.home_outlined,
title: 'Home',
onTap: () => Navigator.pop(context),
),
buildDrawerItem(
icon: Icons.history_outlined,
title: 'History',
onTap: () {
Navigator.pop(context);
context.push('/documents');
},
),
const SizedBox(height: 16),
buildDrawerSectionHeader('USER PROFILE'),
buildDrawerItem(
icon: Icons.person_outline,
title: 'Profile',
onTap: () {
Navigator.pop(context);
showDrawerNotImplemented(context, 'Profile');
},
),
buildDrawerItem(
icon: Icons.notifications_none_outlined,
title: 'Notifications',
onTap: () {
Navigator.pop(context);
showDrawerNotImplemented(context, 'Notifications');
},
),
const SizedBox(height: 16),
buildDrawerSectionHeader('SUPPORT & SETTINGS'),
buildDrawerItem(
icon: Icons.settings_outlined,
title: 'Settings',
onTap: () {
Navigator.pop(context);
showDrawerNotImplemented(context, 'Settings');
},
),
buildDrawerItem(
icon: Icons.help_outline,
title: 'Help',
onTap: () {
Navigator.pop(context);
showDrawerHelpDialog(context);
},
),
],
),
),
// ── Logout ─────────────────────────────────────────────────
const Divider(height: 1),
Padding(
padding: const EdgeInsets.all(16.0),
child: buildDrawerItem(
icon: Icons.logout_outlined,
title: 'Logout',
textColor: AppConfig.errorColor,
iconColor: AppConfig.errorColor,
onTap: _handleLogout,
),
),
],
),
),
);
}
/// Warns before logout if the pending-documents queue still has unsynced
/// items — a plain logout wipes the local Hive cache and clears the queue
/// (see AuthNotifier.logout()), silently discarding any in-flight
/// uploads, unreviewed scans, or failed syncs otherwise.
Future<void> _handleLogout() async {
final pendingCount = ref.read(pendingDocumentsProvider).length;
if (pendingCount > 0) {
final confirmed = await showDialog<bool>(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('Ada Dokumen Belum Tersinkron'),
content: Text(
'Ada $pendingCount dokumen yang belum selesai diunggah atau '
'disinkronkan ke server. Jika Anda logout sekarang, dokumen '
'tersebut akan hilang.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext, false),
child: const Text('Batal'),
),
TextButton(
onPressed: () => Navigator.pop(dialogContext, true),
child: Text('Ya, Logout', style: TextStyle(color: AppConfig.errorColor)),
),
],
),
);
if (confirmed != true) return;
}
if (!mounted) return;
await ref.read(authProvider.notifier).logout();
if (!mounted) return;
context.go('/login');
}
}