import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../services/project_api_service.dart'; import '../../widgets/toast_service.dart'; import '../../widgets/skeleton_loading.dart'; import '../../providers/access_code_provider.dart'; import '../../providers/translation_provider.dart'; import 'widgets/tree_node_card.dart'; import 'widgets/node_details_popup.dart'; class ProjectTreeScreen extends ConsumerStatefulWidget { const ProjectTreeScreen({super.key}); @override ConsumerState createState() => _ProjectTreeScreenState(); } class _ProjectTreeScreenState extends ConsumerState { final _apiService = ProjectApiService(); final _transformationController = TransformationController(); List> _projects = []; Map? _selectedProject; bool _isLoading = true; String? _error; static const double verticalSpacing = 100; static const double horizontalSpacing = 60; @override void initState() { super.initState(); _initializeApi(); } @override void dispose() { _transformationController.dispose(); super.dispose(); } Future _initializeApi() async { final userInfo = await ref.read(accessCodeProvider.notifier).getUserInfo(); _apiService.setToken(userInfo['apiToken']); await _loadProjects(); } Future _loadProjects() async { setState(() { _isLoading = true; _error = null; }); final response = await _apiService.getProjects(); if (mounted) { setState(() { _isLoading = false; if (response.success) { final projectsList = response.data?['projects'] as List? ?? []; _projects = projectsList.map((e) => e as Map).toList(); if (_projects.isNotEmpty && _selectedProject == null) { _selectProject(_projects.first, showConfirm: false); } } else { _error = response.message; } }); } } Future _selectProject(Map project, {bool showConfirm = true}) async { if (showConfirm && _selectedProject != null) { final confirm = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Changer de projet'), content: Text('Voulez-vous afficher l\'arbre du projet \"\" ?'), actions: [ TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Annuler')), FilledButton(onPressed: () => Navigator.of(context).pop(true), child: const Text('Confirmer')), ], ), ); if (confirm != true) return; } setState(() { _isLoading = true; _error = null; }); final response = await _apiService.getProject(project['id']); if (mounted) { setState(() { _isLoading = false; if (response.success) { _selectedProject = response.data?['project']; _transformationController.value = Matrix4.identity(); } else { _error = response.message; } }); } } void _zoomIn() { final currentScale = _transformationController.value.getMaxScaleOnAxis(); if (currentScale < 2.0) { _transformationController.value = Matrix4.identity()..scale(currentScale * 1.2); } } void _zoomOut() { final currentScale = _transformationController.value.getMaxScaleOnAxis(); if (currentScale > 0.5) { _transformationController.value = Matrix4.identity()..scale(currentScale / 1.2); } } void _resetView() { _transformationController.value = Matrix4.identity(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; final t = TranslationService().t; return Scaffold( body: Column( children: [ _buildNavBar(theme, t), Expanded( child: _isLoading ? _buildSkeletonLoading() : _error != null ? _buildError() : _selectedProject == null ? _buildEmptyState() : _buildTreeView(isDark), ), ], ), ); }