pakutz79 c19c9d1a98 feat: navigation drawer, EMS setup, scheduler, tarifs, paramètres app
- Drawer custom (overlay Stack) avec mode installateur PIN SHA-256
- GoRouter + ShellRoute : navigation préservée entre onglets
- 6 providers : NavigationProvider, InstallerModeProvider, AppSettingsProvider,
  EnergySetupProvider, SchedulerProvider, TariffProvider
- Écrans Energy Manager : RoleConfigFlow (3 étapes), Scheduler, Tarifs, Timeline
- Écrans Paramètres : Apparence, Écrans actifs, AppSettingsScreen
- DrawerMenuButton présent dans les 5 AppBars principaux
- Simulation : _thingClasses générées avec interfaces EMS pour filtrage des rôles
- Compteur solaire : ajout smartmeter aux interfaces compatibles
- Thème ETM (etm_theme.dart), ProLockBadge, widgets PowerBar/RoleCard/TimelineSlotCard
- Dépendances : go_router, shared_preferences, crypto, url_launcher

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:52:32 +01:00

180 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import '../providers/energy_setup_provider.dart';
import '../theme/app_theme.dart';
import '../theme/etm_theme.dart';
/// Carte de statut pour un rôle EMS (EV Charger, Chauffe-eau, Batterie, etc.).
class RoleCard extends StatelessWidget {
final EmsRole role;
final RoleAssignment? assignment;
final bool isReachable;
final VoidCallback onConfigure;
final VoidCallback? onToggle;
final VoidCallback? onEdit;
final VoidCallback? onTest;
const RoleCard({
super.key,
required this.role,
required this.assignment,
required this.onConfigure,
this.isReachable = true,
this.onToggle,
this.onEdit,
this.onTest,
});
@override
Widget build(BuildContext context) {
final isConfigured = assignment != null;
final isEnabled = assignment?.enabled ?? false;
return Card(
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(
color: isEnabled
? AppTheme.primaryGreen.withValues(alpha: 0.3)
: Colors.grey.withValues(alpha: 0.15),
),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ── En-tête : icône + nom + switch ──────────────────────────────
Row(
children: [
Text(role.icon,
style: const TextStyle(fontSize: 22)),
const SizedBox(width: 10),
Expanded(
child: Text(
role.label,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
if (isConfigured)
Switch.adaptive(
value: isEnabled,
activeColor: AppTheme.primaryGreen,
onChanged: onToggle != null
? (_) => onToggle!()
: null,
),
],
),
const SizedBox(height: 8),
// ── Corps : état de la configuration ─────────────────────────────
if (!isConfigured) ...[
const Text(
'Aucun appareil configuré',
style: TextStyle(
color: AppTheme.textLight,
fontSize: 13,
),
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
icon: const Icon(Icons.add_rounded, size: 16),
label: const Text('Configurer'),
style: OutlinedButton.styleFrom(
foregroundColor: ETMTheme.accentColor,
side: BorderSide(
color: ETMTheme.accentColor.withValues(alpha: 0.5)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
padding: const EdgeInsets.symmetric(vertical: 8),
),
onPressed: onConfigure,
),
),
] else ...[
Row(
children: [
Icon(
isReachable
? Icons.check_circle_rounded
: Icons.error_rounded,
size: 14,
color: isReachable
? AppTheme.primaryGreen
: AppTheme.boostRed,
),
const SizedBox(width: 6),
Expanded(
child: Text(
assignment!.thing.name,
style: const TextStyle(fontSize: 13),
),
),
],
),
if (assignment!.params.containsKey('powerW')) ...[
const SizedBox(height: 4),
Text(
'${assignment!.params['powerW']} W · Priorité: ${assignment!.params['priority'] ?? 'Normal'}',
style: const TextStyle(
fontSize: 12,
color: AppTheme.textLight,
),
),
],
const SizedBox(height: 10),
Row(
children: [
if (onEdit != null)
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: ETMTheme.accentColor,
side: BorderSide(
color:
ETMTheme.accentColor.withValues(alpha: 0.4)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
padding: const EdgeInsets.symmetric(vertical: 8),
),
onPressed: onEdit,
child: const Text('Modifier',
style: TextStyle(fontSize: 13)),
),
),
if (onEdit != null && onTest != null)
const SizedBox(width: 8),
if (onTest != null)
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: AppTheme.primaryGreen,
side: BorderSide(
color: AppTheme.primaryGreen
.withValues(alpha: 0.4)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
padding: const EdgeInsets.symmetric(vertical: 8),
),
onPressed: onTest,
child: const Text('Tester',
style: TextStyle(fontSize: 13)),
),
),
],
),
],
],
),
),
);
}
}