170 lines
5.1 KiB
Dart
170 lines
5.1 KiB
Dart
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<HomeScreen> createState() => _HomeScreenState();
|
|
}
|
|
|
|
class _HomeScreenState extends State<HomeScreen> {
|
|
List<dynamic> _menuItems = [];
|
|
bool _isLoading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => _fetchMenus());
|
|
}
|
|
|
|
Future<void> _fetchMenus() async {
|
|
final odoo = Provider.of<OdooService>(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<OdooService>(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<String, dynamic> 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),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|