import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/odoo_service.dart'; import '../utils/theme.dart'; import 'pos_list_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { List _menuItems = []; bool _isLoading = true; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _fetchMenus()); } Future _fetchMenus() async { final odoo = Provider.of(context, listen: false); try { // Fetch top-level menus (Apps) final result = await odoo.callKw( model: 'ir.ui.menu', method: 'search_read', args: [ [['parent_id', '=', false]] // Top level only ], kwargs: { 'fields': ['name', 'web_icon_data', 'action', 'id'], 'order': 'sequence,id', }, ); if (mounted) { setState(() { _menuItems = result; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error fetching apps: $e')), ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Dashboard', style: TextStyle(fontWeight: FontWeight.bold)), backgroundColor: Colors.white, foregroundColor: AppTheme.secondaryColor, elevation: 1, actions: [ IconButton( icon: const Icon(Icons.logout), onPressed: () { Provider.of(context, listen: false).logout(); }, ), ], ), body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFFfdfdfd), Color(0xFFf4f6f8)], ), ), child: _isLoading ? const Center(child: CircularProgressIndicator()) : RefreshIndicator( onRefresh: _fetchMenus, child: Padding( padding: const EdgeInsets.all(16.0), child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 0.85, ), itemCount: _menuItems.length, itemBuilder: (context, index) { final item = _menuItems[index]; return _buildAppIcon(item); }, ), ), ), ), ); } Widget _buildAppIcon(Map item) { String name = item['name'] ?? 'App'; String? iconData = item['web_icon_data']; // Base64 png usually // Clean base64 string if necessary (sometimes Odoo sends headers) if (iconData != null) { iconData = iconData.replaceAll(RegExp(r'\s'), ''); } return Card( elevation: 4, shadowColor: Colors.black12, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: InkWell( onTap: () { if (name.toLowerCase().contains('point of sale')) { Navigator.push( context, MaterialPageRoute(builder: (context) => const PosListScreen()), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Opening $name... (Not implemented in demo)')), ); } }, borderRadius: BorderRadius.circular(16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( height: 50, width: 50, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: iconData != null ? Image.memory( const Base64Decoder().convert(iconData), errorBuilder: (context, error, stackTrace) => const Icon(Icons.apps, size: 30, color: AppTheme.secondaryColor), ) : const Icon(Icons.apps, size: 30, color: AppTheme.secondaryColor), ), const SizedBox(height: 12), Text( name, textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF4a5568), ), ), ], ), ), ); } }