import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:terepi_seged/pages/ntrip_settings/presentation/views/ntrip_settings_sheet.dart'; import 'package:terepi_seged/routes/app_pages.dart'; import 'package:terepi_seged/services/auth_service.dart'; import 'package:terepi_seged/services/ntrip_service.dart'; import 'package:terepi_seged/services/permission_service.dart'; import 'package:terepi_seged/services/project_service.dart'; import 'package:terepi_seged/services/ts_sync_service.dart'; import '../services/gnss/gnss_connection.dart'; import '../services/gnss/gnss_device_service.dart'; import '../services/gnss/gnss_service.dart'; import 'gnss_device_picker_dialog.dart'; import 'project/project_picker_view.dart'; class AppDrawer extends StatelessWidget { const AppDrawer({super.key}); @override Widget build(BuildContext context) { return Drawer( // Közvetlenül Column a Drawer-ben — a Drawer maga korlátozott // magasságú (képernyő teljes magassága), így Expanded működik. // SafeArea csak a Header és Footer részeken kell. child: Column( children: [ // ── 1. Fejléc — SafeArea csak felül ───────────────────── SafeArea( bottom: false, child: _DrawerHeader(), ), const Divider(height: 1), // ── 2. Scrollozható lista — Expanded tölti ki a helyet ── Expanded( child: ListView( padding: EdgeInsets.zero, children: [ // Projekt Obx(() { final project = ProjectService.to.activeProject.value; return ListTile( leading: Icon(Icons.folder_outlined, color: project != null ? _hexColor(project.color) : null), title: const Text('Aktív projekt'), subtitle: Text( project?.name ?? 'Nincs projekt', style: TextStyle(fontSize: 12), ), trailing: const Icon(Icons.arrow_forward_ios, size: 14), onTap: () { Get.back(); Get.to(() => const ProjectPickerView(), transition: Transition.rightToLeft); }, ); }), // GNSS eszköz Obx(() { final device = GnssDeviceService.to.selectedDevice.value; return ListTile( leading: Icon( device?.type == GnssConnectionType.ble ? Icons.bluetooth_searching : device?.type == GnssConnectionType.phoneGps ? Icons.phone_android : Icons.bluetooth, ), title: const Text('GNSS eszköz'), subtitle: Text( device?.name ?? 'Nincs kiválasztva', style: const TextStyle(fontSize: 12), ), // Kapcsolat státusz ikon — saját Obx, nem egymásba ágyazva trailing: Obx(() { final connected = GnssService.to.connectionState.value == GnssConnectionState.connected; return Icon( connected ? Icons.circle : Icons.circle_outlined, size: 12, color: connected ? Colors.green : Colors.grey, ); }), onTap: () { Get.back(); GnssDevicePickerDialog.show(); }, ); }), //const Divider(height: 24), const Divider(height: 12), // ----------- További funkciók const _SectionLabel('Funkciók'), ListTile( leading: const Icon(Icons.message_outlined), title: const Text('Üzenetek'), onTap: () { Get.back(); // Get.to(() => const NtripSettingsView()); }, ), Obx(() { final signedIn = AuthService.to.isSignedIn; final allowed = !Get.isRegistered() || PermissionService.to.canContacts; if (!signedIn || !allowed) return const SizedBox.shrink(); return ListTile( leading: const Icon(Icons.phone_outlined), title: const Text('Kapcsolatok'), onTap: () { Get.back(); Get.toNamed(Routes.CONTACTS); }, ); }), Obx(() => ListTile( leading: TsSyncService.to.isSyncing.value ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.sync), title: const Text('Szinkronizálás'), subtitle: Text( TsSyncService.to.pendingCount.value > 0 ? '${TsSyncService.to.pendingCount.value} elem feltöltésre vár' : 'Minden szinkronban', style: const TextStyle(fontSize: 12), ), onTap: TsSyncService.to.syncNow, )), ListTile( leading: const Icon(Icons.data_exploration_outlined), title: const Text('Mérés'), onTap: () { Get.back(); // Get.to(() => const NtripSettingsView()); }, ), ListTile( leading: const Icon(Icons.local_shipping_outlined), title: const Text('Jármű navigáció'), onTap: () { Get.back(); Get.toNamed(Routes.VIBRONAV); }, ), // ── 3. Beállítások ───────────────────────────────── const _SectionLabel('Beállítások'), // NTRIP — gyors elérés terepre: subtitle mutatja // a konfigurációt, a pötty a kapcsolat állapotát. Obx(() { final ntrip = NtripService.to; final configured = ntrip.hasCompleteSettings; return ListTile( leading: const Icon(Icons.cell_tower), title: const Text('NTRIP'), subtitle: Text( configured ? '${ntrip.host.value} · ${ntrip.mountpoint.value}' : 'Nincs beállítva', style: const TextStyle(fontSize: 12), ), trailing: Icon( Icons.circle, size: 12, color: ntrip.isConnected.value ? Colors.green : (configured ? Colors.grey : Colors.orange), ), onTap: () { Get.back(); NtripSettingsSheet.show(); }, ); }), ListTile( leading: const Icon(Icons.map_outlined), title: const Text('Alaptérkép'), onTap: () { Get.back(); // _showBasemapPicker(context); }, ), // ListTile( // leading: const Icon(Icons.man_3), // title: const Text('Bejelentkezés'), // onTap: () async { // Get.back(); // // _showBasemapPicker(context); // final crtl1 = Get.find(); // if (crtl1 != null) { // await crtl1.signIn(); // } // }, // ), ListTile( leading: const Icon(Icons.straighten), title: const Text('Koordináta-rendszer'), subtitle: const Text( 'EOV', style: TextStyle(fontSize: 12), ), onTap: () { Get.back(); // Get.to(() => const DisplaySettingsView()); }, ), ListTile( leading: const Icon(Icons.volume_up_outlined), title: const Text('Hang és rezgés'), onTap: () async { // Get.back(); // Get.to(() => const FeedbackSettingsView()); await FirebaseCrashlytics.instance.recordError( Exception('Teszt hiba - breadcrumb ellenőrzés'), StackTrace.current, reason: 'Manuális teszt', fatal: false); await FirebaseCrashlytics.instance.sendUnsentReports(); Get.back(); }, ), // Minden további beállítás a központi oldalon. ListTile( leading: const Icon(Icons.settings_outlined), title: const Text('Minden beállítás'), trailing: const Icon(Icons.arrow_forward_ios, size: 14), onTap: () { Get.back(); Get.toNamed(Routes.SETTINGS); }, ), const Divider(height: 24), // ── 4. Admin (kommentezve, később aktiválható) ────── // Obx(() => AuthService.to.isAdmin // ? Column(children: [...]) // : const SizedBox.shrink(), // ), ], ), ), // ── 5. Lábléc — SafeArea csak alul ────────────────────── const Divider(height: 1), SafeArea( top: false, child: _DrawerFooter(), ), ], ), ); } } // ── Fejléc ─────────────────────────────────────────────────────────── // Nincs Obx — amíg AuthService ki van kommentelve, nincs reaktív adat. // Ha AuthService bekötésre kerül, akkor kell visszatenni az Obx-et. class _DrawerHeader extends StatelessWidget { const _DrawerHeader(); @override Widget build(BuildContext context) { return Obx(() { final auth = AuthService.to; final signedIn = auth.isSignedIn; return InkWell( // Kijelentkezve: koppintás = Google-bejelentkezés indítása. onTap: signedIn || auth.isBusy.value ? null : _signIn, child: Container( padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), child: Row(children: [ CircleAvatar( radius: 22, backgroundColor: Theme.of(context).colorScheme.primaryContainer, backgroundImage: auth.avatarUrl != null ? NetworkImage(auth.avatarUrl!) : null, child: auth.avatarUrl == null ? Text( signedIn ? auth.displayName[0].toUpperCase() : '?', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Theme.of(context).colorScheme.onPrimaryContainer, ), ) : null, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( signedIn ? auth.displayName : 'Nincs bejelentkezve', style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 14), overflow: TextOverflow.ellipsis, ), Text( signedIn ? auth.email : 'Koppints a Google-bejelentkezéshez', style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.secondary, ), overflow: TextOverflow.ellipsis, ), ], ), ), if (!signedIn) auth.isBusy.value ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.login, size: 18), ]), ), ); }); } Future _signIn() async { try { await AuthService.to.signInWithGoogle(); } catch (e) { Get.snackbar('Bejelentkezés', e.toString(), snackPosition: SnackPosition.BOTTOM); } } } // ── Lábléc ─────────────────────────────────────────────────────────── class _DrawerFooter extends StatelessWidget { const _DrawerFooter(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row(children: [ // App verzió FutureBuilder( future: PackageInfo.fromPlatform(), builder: (_, snap) => Text( snap.hasData ? 'v${snap.data!.version}+${snap.data!.buildNumber}' : '', style: TextStyle( fontSize: 11, color: Colors.grey.shade500, ), ), ), const Spacer(), // Kijelentkezés TextButton.icon( icon: const Icon(Icons.logout, size: 16), label: const Text('Kilépés'), style: TextButton.styleFrom( foregroundColor: Colors.red.shade400, ), onPressed: () { Get.back(); _confirmSignOut(context); }, ), ]), ); } void _confirmSignOut(BuildContext context) { Get.dialog(AlertDialog( title: const Text('Kijelentkezés'), content: const Text('Biztosan ki szeretnél jelentkezni?'), actions: [ TextButton( onPressed: () => Get.back(), child: const Text('Mégse'), ), FilledButton( onPressed: () { Get.back(); AuthService.to.signOut(); }, child: const Text('Kilépés'), ), ], )); } } // ── Szekció felirat ────────────────────────────────────────────────── class _SectionLabel extends StatelessWidget { final String text; const _SectionLabel(this.text); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 4), child: Text( text.toUpperCase(), style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: Colors.grey.shade500, letterSpacing: 0.8, ), ), ); } } Color _hexColor(String hex) { final h = hex.replaceFirst('#', ''); return Color(int.parse( h.length == 6 ? 'FF$h' : h, radix: 16, )); }