import 'package:flutter/material.dart'; import 'package:mobile_monitoring/core/constants/constants.dart'; import 'package:mobile_monitoring/ui/widgets/monitoring_card_service.dart'; import 'package:mobile_monitoring/core/utils/phase_display_utils.dart'; class MonitoringCard extends StatefulWidget { final String title; final String value; final String unit; final String status; final Color statusColor; final String description; final String? userId; final bool? switch1Value; final bool? switch2Value; final String? switch1Label; final String? switch2Label; final Function(bool)? onSwitch1Changed; final Function(bool)? onSwitch2Changed; const MonitoringCard({ super.key, required this.title, required this.value, required this.unit, required this.status, required this.statusColor, required this.description, this.userId, this.switch1Value, this.switch2Value, this.switch1Label, this.switch2Label, this.onSwitch1Changed, this.onSwitch2Changed, }); @override State createState() => _MonitoringCardState(); } class _MonitoringCardState extends State { final _monitoringService = MonitoringCardService(); @override Widget build(BuildContext context) { if (widget.userId == null || widget.userId!.isEmpty) { return _buildCard(PhaseDisplayUtils.vegetatif); } return StreamBuilder( stream: _monitoringService.growthPhaseStream(widget.userId), builder: (context, snapshot) { final phase = snapshot.data ?? PhaseDisplayUtils.vegetatif; return _buildCard(phase); }, ); } Widget _buildCard(PhaseDisplayConfig phase) { final phaseName = phase.name; final phaseColor = phase.color; final statusData = _monitoringService.determineStatus( title: widget.title, value: widget.value, phase: phase, fallbackStatus: widget.status, fallbackColor: widget.statusColor, fallbackDescription: widget.description, ); final status = statusData.status; final statusColor = statusData.color; final description = statusData.description; final isPh = widget.title.toLowerCase().contains('ph'); final isNutrient = widget.title.toLowerCase().contains('nutrient'); final hasControls = widget.switch1Label != null && widget.switch2Label != null; return Container( width: 320, height: hasControls ? 310 : 280, margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), padding: const EdgeInsets.fromLTRB(18, 14, 18, 16), decoration: BoxDecoration( color: AppColors.white, borderRadius: const BorderRadius.only( topLeft: Radius.circular(36), bottomRight: Radius.circular(36), ), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 16, offset: Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4, ), decoration: BoxDecoration( color: phaseColor.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: phaseColor, width: 1), ), child: Text( phaseName, style: TextStyle( color: phaseColor, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), if (isNutrient || isPh) Text( isPh ? 'Target: 4.0-6.0' : 'Target: ${phase.nutrientMin}-${phase.nutrientMax} ppm', style: const TextStyle( fontSize: 11, color: AppColors.textSecondary, fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( isPh ? 'Level PH' : isNutrient ? 'Kadar Nutrisi' : widget.title, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, letterSpacing: 0.5, ), ), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( isPh ? 'PH ${widget.value.replaceAll(RegExp(r'[^0-9\.]'), '')}' : widget.value, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, letterSpacing: 0.5, ), ), if (widget.unit.isNotEmpty && !isPh) Padding( padding: const EdgeInsets.only(left: 4.0, bottom: 2.0), child: Text( widget.unit, style: const TextStyle( fontSize: 16, color: AppColors.textSecondary, fontWeight: FontWeight.w500, ), ), ), ], ), ], ), const SizedBox(height: 6), Text( isPh ? 'Jaga pH larutan antara 4.0 - 6.0 untuk pertumbuhan optimal.' : isNutrient ? 'Pastikan TDS/PPM nutrisi sesuai kebutuhan tanaman.' : description, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ CustomPaint( painter: _CircleStatusPainter(color: statusColor), child: SizedBox( width: 85, height: 85, child: Center( child: Text( status, style: TextStyle( color: statusColor, fontWeight: FontWeight.bold, fontSize: 15, ), textAlign: TextAlign.center, ), ), ), ), const SizedBox(width: 12), if (hasControls) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildSwitchRow( widget.switch1Label!, widget.switch1Value ?? false, widget.onSwitch1Changed, ), const SizedBox(height: 2), _buildSwitchRow( widget.switch2Label!, widget.switch2Value ?? false, widget.onSwitch2Changed, ), ], ), ), ], ), ], ), ); } Widget _buildSwitchRow(String label, bool value, Function(bool)? onChanged) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: value ? AppColors.secondary : AppColors.black, ), ), Transform.scale( scale: 0.85, child: Switch( value: value, onChanged: onChanged, activeThumbColor: AppColors.secondary, inactiveThumbColor: AppColors.darkGray, inactiveTrackColor: AppColors.mediumGray, ), ), ], ); } } class _CircleStatusPainter extends CustomPainter { final Color color; _CircleStatusPainter({required this.color}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = color ..strokeWidth = 5 ..style = PaintingStyle.stroke; canvas.drawCircle( Offset(size.width / 2, size.height / 2), size.width / 2 - 4, paint, ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }